From 21b5688b119e28a71d379a5843e3ec34dd8112dc Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 00:22:20 +0300 Subject: [PATCH 1/9] fix: update pagination for all vaults screen. Fix claim modal --- config/network/index.ts | 2 +- .../claim-form-provider.tsx | 17 ++++++++------- .../claim/claim-form/form/balance/balance.tsx | 5 +++-- .../form/feature-tx-info/feature-tx-info.tsx | 21 +++++++++++-------- features/claim/claim-form/hooks/index.ts | 4 ++-- .../hooks/use-claim-with-delegation.ts | 9 +++++--- features/claim/claim-form/types.ts | 2 +- features/home/all-vaults/all-vaults.tsx | 8 ++++--- features/home/hooks/index.ts | 1 + .../home/hooks/use-connected-vaults-number.ts | 17 +++++++++++++++ features/overview/contexts/vault-overview.tsx | 6 ++++-- modules/vaults/hooks/use-vaults-data-all.ts | 9 ++++---- 12 files changed, 66 insertions(+), 35 deletions(-) create mode 100644 features/home/hooks/index.ts create mode 100644 features/home/hooks/use-connected-vaults-number.ts diff --git a/config/network/index.ts b/config/network/index.ts index 5f8c5ac2..929ffe2d 100644 --- a/config/network/index.ts +++ b/config/network/index.ts @@ -62,7 +62,7 @@ export const getNetworkConfig = (chain: CHAINS): NetworkConfig | undefined => { }; export const getContractAddress = ( - chain: CHAINS, + chain: CHAINS | undefined, contractName: CONTRACT_NAMES, ): Address | undefined => { const networkConfig = getNetworkConfig(chain); diff --git a/features/claim/claim-form/claim-form-context/claim-form-provider.tsx b/features/claim/claim-form/claim-form-context/claim-form-provider.tsx index 9653dcb4..9ad83a2c 100644 --- a/features/claim/claim-form/claim-form-context/claim-form-provider.tsx +++ b/features/claim/claim-form/claim-form-context/claim-form-provider.tsx @@ -6,12 +6,12 @@ import { createContext, useContext, } from 'react'; -import { Address, isAddress } from 'viem'; +import { isAddress, ReadContractErrorType } from 'viem'; import { useReadContract } from 'wagmi'; import { FormProvider, useForm } from 'react-hook-form'; import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; -import { useClaimWithDelegation } from 'features/claim/claim-form/hooks'; +import { useClaimDashboard } from 'features/claim/claim-form/hooks'; import { useVaultInfo } from 'features/overview/contexts'; import { @@ -28,6 +28,7 @@ type ClaimDataContextValue = { availableToClaim: bigint | undefined; isLoadingClaimInfo: boolean; isErrorClaimInfo: boolean; + errorClaimInfo: ReadContractErrorType | null; }; const ClaimDataContext = createContext(null); @@ -52,6 +53,7 @@ export const ClaimFormProvider: FC<{ children: ReactNode }> = ({ data: availableToClaim, isFetching: isLoadingClaimInfo, isError: isErrorClaimInfo, + error: errorClaimInfo, } = useReadContract({ abi: dashboardAbi, address: activeVault?.owner, @@ -66,8 +68,9 @@ export const ClaimFormProvider: FC<{ children: ReactNode }> = ({ availableToClaim, isLoadingClaimInfo, isErrorClaimInfo, + errorClaimInfo, }), - [availableToClaim, isLoadingClaimInfo, isErrorClaimInfo], + [availableToClaim, isLoadingClaimInfo, isErrorClaimInfo, errorClaimInfo], ); const formObject = useForm({ @@ -75,17 +78,17 @@ export const ClaimFormProvider: FC<{ children: ReactNode }> = ({ recipient: '', }, mode: 'all', - reValidateMode: 'onChange', + reValidateMode: 'onBlur', }); - const { callClaim } = useClaimWithDelegation(); + const { callClaim } = useClaimDashboard(); const { retryEvent, retryFire } = useFormControllerRetry(); const onSubmit = useCallback( async ({ recipient }: ClaimFormSchema) => { - if (isAddress(recipient as string)) { + if (recipient && isAddress(recipient)) { // TODO: resolve recipient if ens domain - await callClaim(recipient as Address); + await callClaim(recipient); return true; } diff --git a/features/claim/claim-form/form/balance/balance.tsx b/features/claim/claim-form/form/balance/balance.tsx index 3517b466..9c6c5960 100644 --- a/features/claim/claim-form/form/balance/balance.tsx +++ b/features/claim/claim-form/form/balance/balance.tsx @@ -8,8 +8,9 @@ export const Balance = () => { const { isLoadingClaimInfo, availableToClaim, isErrorClaimInfo } = useClaimFormData(); + const isAvailableToClaim = typeof availableToClaim === 'bigint'; const isLoading = - (!isErrorClaimInfo && !availableToClaim) || isLoadingClaimInfo; + (!isErrorClaimInfo && !isAvailableToClaim) || isLoadingClaimInfo; return ( @@ -18,7 +19,7 @@ export const Balance = () => { Available to claim {isLoading && } - {!!availableToClaim && ( + {isAvailableToClaim && ( {formatBalance(availableToClaim).trimmed} ETH )} diff --git a/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx b/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx index 8a295686..753ce647 100644 --- a/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx +++ b/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx @@ -2,13 +2,14 @@ import { useFormContext } from 'react-hook-form'; import { Loader, Text } from '@lidofinance/lido-ui'; import { AmountInfo, InfoRow, Wrapper } from './styles'; -import { useSimulationClaimWithDelegation } from 'features/claim/claim-form/hooks'; +import { useSimulationClaimDashboard } from 'features/claim/claim-form/hooks'; +import { Address } from 'viem'; export const FeatureTxInfo = () => { - const { getValues } = useFormContext(); - const { recipient } = getValues(); - const { data, isLoading, isError } = - useSimulationClaimWithDelegation(recipient); + const { watch } = useFormContext(); + const recipient: Address = watch('recipient'); + const { data, isLoading, isError, isFetching } = + useSimulationClaimDashboard(recipient); return ( @@ -16,10 +17,12 @@ export const FeatureTxInfo = () => { Transaction cost - {isLoading && } - {data && {'$99.99'}} - {isError && Is not available} - {!isLoading && !data && !isError && {'$0'}} + {(isLoading || isFetching) && } + {/*TODO: get simulated data*/} + {/*TODO: show error message*/} + {data && {'$0.99'}} + {isError && -} + {!isLoading && !data && !isError && -} ); diff --git a/features/claim/claim-form/hooks/index.ts b/features/claim/claim-form/hooks/index.ts index 3f983e3e..70d4390f 100644 --- a/features/claim/claim-form/hooks/index.ts +++ b/features/claim/claim-form/hooks/index.ts @@ -1,4 +1,4 @@ export { - useClaimWithDelegation, - useSimulationClaimWithDelegation, + useClaimDashboard, + useSimulationClaimDashboard, } from './use-claim-with-delegation'; diff --git a/features/claim/claim-form/hooks/use-claim-with-delegation.ts b/features/claim/claim-form/hooks/use-claim-with-delegation.ts index 63773f41..398e4a1a 100644 --- a/features/claim/claim-form/hooks/use-claim-with-delegation.ts +++ b/features/claim/claim-form/hooks/use-claim-with-delegation.ts @@ -10,8 +10,9 @@ import { Address } from 'viem'; import { dashboardAbi } from 'abi/dashboard-abi'; import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; +import invariant from 'tiny-invariant'; -export const useClaimWithDelegation = (onMutate = () => {}) => { +export const useClaimDashboard = (onMutate = () => {}) => { const { chainId } = useDappStatus(); const wagmiConfig = useConfig(); const { activeVault } = useVaultInfo(); @@ -30,9 +31,11 @@ export const useClaimWithDelegation = (onMutate = () => {}) => { const callClaim = useCallback( async (recipient: Address) => { + invariant(owner, '[useClaimDashboard] owner is not available'); + return await writeContractAsync({ abi: dashboardAbi, - address: owner as Address, + address: owner, functionName: 'claimNodeOperatorFee', args: [recipient], chainId, @@ -48,7 +51,7 @@ export const useClaimWithDelegation = (onMutate = () => {}) => { }; }; -export const useSimulationClaimWithDelegation = (recipient: Address) => { +export const useSimulationClaimDashboard = (recipient: Address) => { const { activeVault } = useVaultInfo(); const owner = activeVault?.owner; const isEnabled = !!owner && !!recipient; diff --git a/features/claim/claim-form/types.ts b/features/claim/claim-form/types.ts index df91e904..590d92e1 100644 --- a/features/claim/claim-form/types.ts +++ b/features/claim/claim-form/types.ts @@ -1,3 +1,3 @@ export type ClaimFormSchema = { - recipient: string | null; + recipient: string; }; diff --git a/features/home/all-vaults/all-vaults.tsx b/features/home/all-vaults/all-vaults.tsx index 12ed3e79..6399bb8d 100644 --- a/features/home/all-vaults/all-vaults.tsx +++ b/features/home/all-vaults/all-vaults.tsx @@ -1,16 +1,18 @@ import { Loader, Pagination } from '@lidofinance/lido-ui'; import { VaultTable } from 'features/home/components/vault-table'; import { AllVaultsWrapper } from './styles'; -import { useVaultsDataAll } from 'modules/vaults'; +import { useVaultsDataAll, VAULTS_PER_PAGE } from 'modules/vaults'; +import { useConnectedVaultsNumber } from 'features/home/hooks'; export const AllVaults = () => { const { vaults, isLoading: isLoadingAllVaults, - pagesCount, handlePagination, } = useVaultsDataAll(); - const showPagination = !!pagesCount; + const { data } = useConnectedVaultsNumber(); + const pagesCount = Math.ceil(Number(data ?? 0) / VAULTS_PER_PAGE); + const showPagination = !!vaults && vaults?.length > 0 && pagesCount > 1; return ( diff --git a/features/home/hooks/index.ts b/features/home/hooks/index.ts new file mode 100644 index 00000000..ff8b55a6 --- /dev/null +++ b/features/home/hooks/index.ts @@ -0,0 +1 @@ +export { useConnectedVaultsNumber } from './use-connected-vaults-number'; diff --git a/features/home/hooks/use-connected-vaults-number.ts b/features/home/hooks/use-connected-vaults-number.ts new file mode 100644 index 00000000..07b66f91 --- /dev/null +++ b/features/home/hooks/use-connected-vaults-number.ts @@ -0,0 +1,17 @@ +import { usePublicClient, useReadContract } from 'wagmi'; +import { getContractAddress } from 'config'; +import { VaultHubAbi } from 'abi/vault-hub'; + +export const useConnectedVaultsNumber = () => { + const publicClient = usePublicClient(); + const address = getContractAddress(publicClient?.chain.id, 'vaultHub'); + + return useReadContract({ + address, + abi: VaultHubAbi, + functionName: 'vaultsCount', + query: { + enabled: !!address, + }, + }); +}; diff --git a/features/overview/contexts/vault-overview.tsx b/features/overview/contexts/vault-overview.tsx index 6385a4ad..ef043c0d 100644 --- a/features/overview/contexts/vault-overview.tsx +++ b/features/overview/contexts/vault-overview.tsx @@ -95,10 +95,12 @@ export const VaultOverviewProvider: FC = ({ children }) => { (minted * (VAULT_TOTAL_BASIS_POINTS_BN - BigInt(reserveRatioBP))); const utilizationRatio = formatPercent.format(utilization); const totalMintable = bigIntMin( - (valuation - nodeOperatorUnclaimedFee) * - BigInt(VAULT_TOTAL_BASIS_POINTS - reserveRatioBP), + ((valuation - nodeOperatorUnclaimedFee) * + BigInt(VAULT_TOTAL_BASIS_POINTS - reserveRatioBP)) / + VAULT_TOTAL_BASIS_POINTS_BN, ethLimit, ); + const totalMintingCapacity = toEthValue(totalMintable); const depositedToValidators = toEthValue(valuation - balance); const accumulatedFee = toEthValue(nodeOperatorUnclaimedFee); diff --git a/modules/vaults/hooks/use-vaults-data-all.ts b/modules/vaults/hooks/use-vaults-data-all.ts index 10054c99..2db67ab9 100644 --- a/modules/vaults/hooks/use-vaults-data-all.ts +++ b/modules/vaults/hooks/use-vaults-data-all.ts @@ -3,7 +3,7 @@ import { useState, useCallback } from 'react'; import { useVaultData } from 'modules/vaults/hooks/use-vault-data'; import { useVaultsConnectedBound } from 'modules/vaults/hooks/use-vaults-connected-bound'; -import { VAULTS_PER_PAGE } from '../consts'; +import { VAULTS_PER_PAGE } from 'modules/vaults/consts'; import type { Address } from 'viem'; @@ -29,10 +29,9 @@ export const useVaultsDataAll = () => { } = useVaultData(connectedVaults as Address[] | undefined); const handlePagination = useCallback((page: number) => { - const toCursor = page * VAULTS_PER_PAGE - 1; - const fromCursor = toCursor - VAULTS_PER_PAGE; - // TODO: refactor. Waiting for devnet - setPaginationData({ from: toCursor, to: fromCursor, page }); + const fromCursor = (page - 1) * VAULTS_PER_PAGE; + const toCursor = page * VAULTS_PER_PAGE; + setPaginationData({ from: fromCursor, to: toCursor, page }); }, []); return { From 3e262784dfb049a2ec072e660b64be427789e2c3 Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 02:33:42 +0300 Subject: [PATCH 2/9] feat: add modal for fund form. Fix fund some form issues --- features/settings/permissions/consts.ts | 10 +- features/supply/fund/form/balance/balance.tsx | 11 +- .../form/feature-tx-info/feature-tx-info.tsx | 34 ++--- .../fund/form/submit-button/submit-button.tsx | 1 + .../fund-form-context/fund-form-provider.tsx | 44 ++++++- .../fund/hooks/use-fund-with-dashboard.ts | 57 ++++---- features/supply/fund/submit-modal/index.ts | 1 + features/supply/fund/submit-modal/styles.ts | 8 ++ .../supply/fund/submit-modal/submit-modal.tsx | 122 ++++++++++++++++++ features/supply/fund/types.ts | 12 ++ 10 files changed, 240 insertions(+), 60 deletions(-) create mode 100644 features/supply/fund/submit-modal/index.ts create mode 100644 features/supply/fund/submit-modal/styles.ts create mode 100644 features/supply/fund/submit-modal/submit-modal.tsx diff --git a/features/settings/permissions/consts.ts b/features/settings/permissions/consts.ts index 600107dc..cfe3c26f 100644 --- a/features/settings/permissions/consts.ts +++ b/features/settings/permissions/consts.ts @@ -82,12 +82,12 @@ export const editPermissionsSchema = z.object({ export const adminPermissionsList: PermissionsRoles[] = [ { role: 'FUND_ROLE', - title: 'Mint ETH', - tooltip: 'Allows Funding ETH', + title: 'Supply ETH', + tooltip: 'Allows Supplying ETH', }, { role: 'WITHDRAW_ROLE', - title: 'Repay ETH', + title: 'Withdraw ETH', tooltip: 'Allows Withdrawing unlocked ETH from stVault', }, { @@ -126,8 +126,8 @@ export const adminPermissionsList: PermissionsRoles[] = [ }, { role: 'BURN_ROLE', - title: 'Burn stETH', - tooltip: 'Allows Burning stETH', + title: 'Repay stETH', + tooltip: 'Allows Repaying stETH', }, { role: 'VOLUNTARY_DISCONNECT_ROLE', diff --git a/features/supply/fund/form/balance/balance.tsx b/features/supply/fund/form/balance/balance.tsx index db4cd94c..31e3e68c 100644 --- a/features/supply/fund/form/balance/balance.tsx +++ b/features/supply/fund/form/balance/balance.tsx @@ -8,7 +8,10 @@ import { formatBalance } from 'utils'; export const Balance = () => { const { address } = useDappStatus(); - const { data, isLoading, isSuccess, isError } = useBalance({ address }); + const { data, isLoading, isFetching, isSuccess, isError } = useBalance({ + address, + }); + const showLoader = isLoading || isFetching; return ( @@ -16,11 +19,11 @@ export const Balance = () => { Available to Supply - {isLoading && } - {isSuccess && !isLoading && ( + {showLoader && } + {isSuccess && !showLoader && ( {formatBalance(data.value).trimmed} ETH )} - {isError && Balance is not available} + {isError && -} ); diff --git a/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx b/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx index 7c85f669..b3549c42 100644 --- a/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx +++ b/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx @@ -6,39 +6,33 @@ import { AmountInfo, InfoRow, Wrapper } from './styles'; import { useVaultInfo } from 'features/overview/contexts'; export const FeatureTxInfo = () => { - // TODO: simulate tx, add tx price, convert ETH to stEth - // TODO: add question info - const { getValues } = useFormContext(); - const { amount } = getValues(); + const { watch } = useFormContext(); + const amount: bigint | undefined = watch('amount'); const { activeVault } = useVaultInfo(); - const { data, isLoading, isError } = useSimulationFundWithDashboard({ - address: activeVault?.address, - amount: amount ?? 0, - }); + const { data, isLoading, isFetching, isError } = + useSimulationFundWithDashboard({ + address: activeVault?.owner, + amount: amount ?? 0n, + }); + const showLoader = isLoading || isFetching; return ( - {/* - - You will receive - - - {'50 stETH'} - - - */} Transaction cost - {isLoading && } + {showLoader && } {isError && ( Can't load gas simulation info )} - {data && {'$99.99'}} - {!data && !isError && !isLoading && -} + {/* TODO: check result, useSimulationFundWithDashboard returns data.result === undefined */} + {!!data?.result && !showLoader && ( + {data?.result} + )} + {!data?.result && !isError && !showLoader && -} ); diff --git a/features/supply/fund/form/submit-button/submit-button.tsx b/features/supply/fund/form/submit-button/submit-button.tsx index 91ffa090..ba1f82ab 100644 --- a/features/supply/fund/form/submit-button/submit-button.tsx +++ b/features/supply/fund/form/submit-button/submit-button.tsx @@ -5,6 +5,7 @@ export const SubmitButton = () => { const { formState: { isSubmitting, isValid, isDirty }, } = useFormContext(); + return ( = ({ children }) => { + const [submitStep, setSubmitStep] = useState<{ + step: SubmitStep; + tx?: Address; + }>(() => ({ step: SubmitStepEnum.edit })); const formObject = useForm({ defaultValues: { amount: undefined, @@ -18,18 +28,39 @@ export const FundFormProvider: FC<{ children: ReactNode }> = ({ children }) => { reValidateMode: 'onChange', }); const { callVaultFund } = useFundWithDashboard(); - const { retryEvent, retryFire } = useFormControllerRetry(); + const setModalState = useCallback( + (submitStep: { step: SubmitStep; tx?: Address }) => { + setSubmitStep(submitStep); + }, + [], + ); const onSubmit = useCallback( async ({ amount }: FundFormSchema) => { - if (amount) { - await callVaultFund(amount); - return true; + try { + if (amount) { + setModalState({ step: SubmitStepEnum.initiate }); + const tx = await callVaultFund(amount, setModalState); + setModalState({ step: SubmitStepEnum.success, tx }); + return true; + } + } catch (err) { + if ( + err instanceof Error && + err.message.includes('User rejected the request') + ) { + setModalState({ step: SubmitStepEnum.reject }); + } else { + setModalState({ step: SubmitStepEnum.error }); + } + + return false; } return false; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [callVaultFund], ); @@ -48,6 +79,7 @@ export const FundFormProvider: FC<{ children: ReactNode }> = ({ children }) => { {children} + ); diff --git a/features/supply/fund/hooks/use-fund-with-dashboard.ts b/features/supply/fund/hooks/use-fund-with-dashboard.ts index e8038ae7..fad5984b 100644 --- a/features/supply/fund/hooks/use-fund-with-dashboard.ts +++ b/features/supply/fund/hooks/use-fund-with-dashboard.ts @@ -1,20 +1,17 @@ import { useCallback } from 'react'; -import { - useSimulateContract, - useWaitForTransactionReceipt, - useWriteContract, - UseSimulateContractParameters, - useAccount, -} from 'wagmi'; +import { usePublicClient, useSimulateContract, useWriteContract } from 'wagmi'; import { Address } from 'viem'; import { dashboardAbi } from 'abi/dashboard-abi'; import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; import { useVaultPermissions } from 'modules/vaults/hooks/use-vault-permissions'; +import { useDappStatus } from 'modules/web3'; +import { SubmitStep, SubmitStepEnum } from '../types'; export const useFundWithDashboard = (onMutate = () => {}) => { const { activeVault } = useVaultInfo(); + const publicClient = usePublicClient(); const { data: fundTx, writeContractAsync } = useWriteContract({ mutation: { @@ -22,31 +19,42 @@ export const useFundWithDashboard = (onMutate = () => {}) => { }, }); - const { data: fundReceipt } = useWaitForTransactionReceipt({ - hash: fundTx, - }); - const callVaultFund = useCallback( - async (amount: bigint) => { - invariant(activeVault?.owner, 'activeVault?.owner is undefined'); - return writeContractAsync({ + async ( + amount: bigint, + setModalState: (submitStep: { step: SubmitStep; tx?: Address }) => void, + ) => { + invariant( + activeVault?.owner, + '[useFundWithDashboard] owner is undefined', + ); + invariant(publicClient, 'activeVault?.owner is undefined'); + + setModalState({ step: SubmitStepEnum.confirming }); + const tx = await writeContractAsync({ abi: dashboardAbi, address: activeVault.owner, functionName: 'fund', value: amount, }); + + setModalState({ step: SubmitStepEnum.submitting, tx }); + await publicClient.waitForTransactionReceipt({ + hash: tx, + }); + + return tx; }, - [writeContractAsync, activeVault?.owner], + [writeContractAsync, activeVault?.owner, publicClient], ); return { callVaultFund, fundTx, - fundReceipt, }; }; -export type SimulationFundWithDashboardProps = { +export type SimulationFundDashboardProps = { address: Address | undefined; amount: bigint; }; @@ -54,19 +62,18 @@ export type SimulationFundWithDashboardProps = { export const useSimulationFundWithDashboard = ({ address, amount, -}: SimulationFundWithDashboardProps) => { - const { address: accountAddress } = useAccount(); +}: SimulationFundDashboardProps) => { const { hasPermission } = useVaultPermissions('funder'); - const simulationContractPayload: UseSimulateContractParameters = { + const { address: account } = useDappStatus(); + + return useSimulateContract({ abi: dashboardAbi, address, - account: accountAddress, + account, functionName: 'fund', value: amount, query: { - enabled: !!address && hasPermission, + enabled: !!address && hasPermission && !!amount, }, - }; - - return useSimulateContract(simulationContractPayload); + }); }; diff --git a/features/supply/fund/submit-modal/index.ts b/features/supply/fund/submit-modal/index.ts new file mode 100644 index 00000000..1c0e3509 --- /dev/null +++ b/features/supply/fund/submit-modal/index.ts @@ -0,0 +1 @@ +export { SubmitModal } from './submit-modal'; diff --git a/features/supply/fund/submit-modal/styles.ts b/features/supply/fund/submit-modal/styles.ts new file mode 100644 index 00000000..dfca0ac4 --- /dev/null +++ b/features/supply/fund/submit-modal/styles.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +export const Content = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spaceMap.xl}px; + margin-top: ${({ theme }) => theme.spaceMap.sm}px; +`; diff --git a/features/supply/fund/submit-modal/submit-modal.tsx b/features/supply/fund/submit-modal/submit-modal.tsx new file mode 100644 index 00000000..63f90a1d --- /dev/null +++ b/features/supply/fund/submit-modal/submit-modal.tsx @@ -0,0 +1,122 @@ +import { FC, useMemo } from 'react'; +import { useRouter } from 'next/router'; +import { useFormContext } from 'react-hook-form'; + +import { + Loader, + Modal, + Text, + Success, + Error, + type ModalProps, + Button, +} from '@lidofinance/lido-ui'; + +import { useFormControllerContext } from 'shared/hook-form/form-controller'; +import { ButtonLink, TxLinkEtherscan } from 'shared/components'; +import { Content } from './styles'; + +import { SubmitStepEnum, SubmitStep } from 'features/supply/fund/types'; +import { AppPaths } from 'consts/urls'; +import { useVaultInfo } from 'features/overview/contexts'; +import { Address } from 'viem'; + +const getIconComponent = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) + return ; + if (step === SubmitStepEnum.reject) return ; + if (step === SubmitStepEnum.error) return ; + return ; +}; + +const getModalTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) return 'Vault has been funded'; + if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; + if (step === SubmitStepEnum.error) return 'Transaction error'; + return 'You are supplying to the vault'; +}; + +const getModalSubTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; + if (step === SubmitStepEnum.reject) + return 'User denied transaction signature'; + if (step === SubmitStepEnum.error) + return 'Got error when called contract simulation or transaction'; + return ''; +}; + +interface SubmitModalProps extends ModalProps { + submitStep: { + step: SubmitStep; + tx?: Address; + }; + setModalState: ({ step }: { step: SubmitStep }) => void; +} + +export const SubmitModal: FC = ({ + submitStep, + setModalState, +}) => { + const router = useRouter(); + const { + formState: { isSubmitting, isSubmitted }, + } = useFormContext(); + const { retryFire } = useFormControllerContext(); + const { step, tx } = submitStep ?? {}; + const { activeVault } = useVaultInfo(); + + const iconComponent = useMemo(() => getIconComponent(step), [step]); + const title = getModalTitle(step); + const subtitle = getModalSubTitle(step); + + const handleNavigateToVault = () => { + void router.push(`/${activeVault?.address}/${AppPaths.overview}`); + }; + + const handleCloseModal = () => { + setModalState({ step: SubmitStepEnum.edit }); + }; + + return ( + + + {step === SubmitStepEnum.initiate && ( + + Wait for wallet confirmation window + + )} + + {step === SubmitStepEnum.confirming && ( + + Confirm this transaction in your wallet + + )} + + {step === SubmitStepEnum.success && ( + + )} + + {(SubmitStepEnum.success === step || + SubmitStepEnum.submitting === step) && + tx && } + + {step === SubmitStepEnum.reject && ( + retry + )} + + {step === SubmitStepEnum.error && ( + close + )} + + + ); +}; diff --git a/features/supply/fund/types.ts b/features/supply/fund/types.ts index 1bbbf334..3b35b0c3 100644 --- a/features/supply/fund/types.ts +++ b/features/supply/fund/types.ts @@ -1,3 +1,15 @@ export type FundFormSchema = { amount: bigint | undefined; }; + +export enum SubmitStepEnum { + edit = 'edit', + initiate = 'initiate', + confirming = 'confirming', + reject = 'reject', + error = 'error', + submitting = 'submitting', + success = 'success', +} + +export type SubmitStep = keyof typeof SubmitStepEnum; From c5a0ac1d3128b3dd32f127c53e60af9c0a1ef94e Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 03:04:27 +0300 Subject: [PATCH 3/9] feat: update withdrawal form --- .../fund-form-context/fund-form-provider.tsx | 7 +- .../fund/hooks/use-fund-with-dashboard.ts | 7 +- .../supply/fund/submit-modal/submit-modal.tsx | 2 +- features/supply/fund/types.ts | 12 -- .../supply/withdraw/form/balance/balance.tsx | 2 +- .../form/feature-tx-info/feature-tx-info.tsx | 24 ++-- .../hooks/use-withdraw-with-dashboard.ts | 38 ++++-- .../supply/withdraw/submit-modal/index.ts | 1 + .../supply/withdraw/submit-modal/styles.ts | 8 ++ .../withdraw/submit-modal/submit-modal.tsx | 122 ++++++++++++++++++ .../withdraw-form-provider.tsx | 38 +++++- shared/transaction-modal/types.ts | 11 ++ 12 files changed, 221 insertions(+), 51 deletions(-) create mode 100644 features/supply/withdraw/submit-modal/index.ts create mode 100644 features/supply/withdraw/submit-modal/styles.ts create mode 100644 features/supply/withdraw/submit-modal/submit-modal.tsx create mode 100644 shared/transaction-modal/types.ts diff --git a/features/supply/fund/fund-form-context/fund-form-provider.tsx b/features/supply/fund/fund-form-context/fund-form-provider.tsx index 5850d8de..8a04786a 100644 --- a/features/supply/fund/fund-form-context/fund-form-provider.tsx +++ b/features/supply/fund/fund-form-context/fund-form-provider.tsx @@ -7,11 +7,8 @@ import { FormControllerContext, FormControllerContextValueType, } from 'shared/hook-form/form-controller'; -import { - FundFormSchema, - SubmitStep, - SubmitStepEnum, -} from 'features/supply/fund/types'; +import { FundFormSchema } from 'features/supply/fund/types'; +import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; import { SubmitModal } from '../submit-modal'; import { Address } from 'viem'; diff --git a/features/supply/fund/hooks/use-fund-with-dashboard.ts b/features/supply/fund/hooks/use-fund-with-dashboard.ts index fad5984b..bec6847d 100644 --- a/features/supply/fund/hooks/use-fund-with-dashboard.ts +++ b/features/supply/fund/hooks/use-fund-with-dashboard.ts @@ -7,7 +7,7 @@ import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; import { useVaultPermissions } from 'modules/vaults/hooks/use-vault-permissions'; import { useDappStatus } from 'modules/web3'; -import { SubmitStep, SubmitStepEnum } from '../types'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; export const useFundWithDashboard = (onMutate = () => {}) => { const { activeVault } = useVaultInfo(); @@ -28,7 +28,10 @@ export const useFundWithDashboard = (onMutate = () => {}) => { activeVault?.owner, '[useFundWithDashboard] owner is undefined', ); - invariant(publicClient, 'activeVault?.owner is undefined'); + invariant( + publicClient, + '[useFundWithDashboard] publicClient is undefined', + ); setModalState({ step: SubmitStepEnum.confirming }); const tx = await writeContractAsync({ diff --git a/features/supply/fund/submit-modal/submit-modal.tsx b/features/supply/fund/submit-modal/submit-modal.tsx index 63f90a1d..507bfdc7 100644 --- a/features/supply/fund/submit-modal/submit-modal.tsx +++ b/features/supply/fund/submit-modal/submit-modal.tsx @@ -16,7 +16,7 @@ import { useFormControllerContext } from 'shared/hook-form/form-controller'; import { ButtonLink, TxLinkEtherscan } from 'shared/components'; import { Content } from './styles'; -import { SubmitStepEnum, SubmitStep } from 'features/supply/fund/types'; +import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; import { AppPaths } from 'consts/urls'; import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; diff --git a/features/supply/fund/types.ts b/features/supply/fund/types.ts index 3b35b0c3..1bbbf334 100644 --- a/features/supply/fund/types.ts +++ b/features/supply/fund/types.ts @@ -1,15 +1,3 @@ export type FundFormSchema = { amount: bigint | undefined; }; - -export enum SubmitStepEnum { - edit = 'edit', - initiate = 'initiate', - confirming = 'confirming', - reject = 'reject', - error = 'error', - submitting = 'submitting', - success = 'success', -} - -export type SubmitStep = keyof typeof SubmitStepEnum; diff --git a/features/supply/withdraw/form/balance/balance.tsx b/features/supply/withdraw/form/balance/balance.tsx index 14fba951..9632dbff 100644 --- a/features/supply/withdraw/form/balance/balance.tsx +++ b/features/supply/withdraw/form/balance/balance.tsx @@ -24,7 +24,7 @@ export const Balance = () => { {formatBalance(withdrawableAmount).trimmed} ETH )} - {isWithdrawableError && ( + {isWithdrawableError && !isWithdrawableLoading && ( Balance is not available )} diff --git a/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx b/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx index 043ac549..657d584c 100644 --- a/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx +++ b/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx @@ -3,36 +3,40 @@ import { useSimulateWithdrawDashboard } from 'features/supply/withdraw/hooks'; import { Loader, Text } from '@lidofinance/lido-ui'; import { AmountInfo, InfoRow, Wrapper } from './styles'; +import { formatBalance } from '../../../../../utils'; export const FeatureTxInfo = () => { const { watch } = useFormContext(); const [recipient, amount] = watch(['recipient', 'amount']); - const { isLoading, isError, data } = useSimulateWithdrawDashboard({ - recipient, - amount, - }); + const { isLoading, isFetching, isError, data } = useSimulateWithdrawDashboard( + { + recipient, + amount, + }, + ); + const showLoader = isLoading || isFetching; return ( - {/* + You will receive - {'50 ETH'} */} + {formatBalance(amount ?? 0n).trimmed} ETH Transaction cost - {isLoading && } + {showLoader && } {isError && ( Can't load gas simulation info )} - {/*TODO: replace by real data*/} - {data && {'$99.99'}} - {!data && !isError && !isLoading && -} + {/* TODO: fix simulation */} + {data?.result && !showLoader && {'$0.99'}} + {!data?.result && !isError && !showLoader && -} ); diff --git a/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts b/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts index 663d8240..22b70383 100644 --- a/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts +++ b/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts @@ -1,9 +1,5 @@ import { useCallback } from 'react'; -import { - useSimulateContract, - useWaitForTransactionReceipt, - useWriteContract, -} from 'wagmi'; +import { usePublicClient, useSimulateContract, useWriteContract } from 'wagmi'; import { Address } from 'viem'; import { dashboardAbi } from 'abi/dashboard-abi'; @@ -11,14 +7,17 @@ import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; import { useVaultPermissions } from 'modules/vaults/hooks/use-vault-permissions'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; type WithdrawWithDashboardArgs = { recipient: Address; amount: bigint; + setModalState: (submitStep: { step: SubmitStep; tx?: Address }) => void; }; export const useWithdrawWithDashboard = (onMutate = () => {}) => { const { activeVault } = useVaultInfo(); + const publicClient = usePublicClient(); const { data: withdrawTx, writeContractAsync } = useWriteContract({ mutation: { @@ -26,27 +25,38 @@ export const useWithdrawWithDashboard = (onMutate = () => {}) => { }, }); - const { data: withdrawReceipt } = useWaitForTransactionReceipt({ - hash: withdrawTx, - }); - const callWithdraw = useCallback( - async ({ amount, recipient }: WithdrawWithDashboardArgs) => { - invariant(activeVault, 'activeVault is undefined'); - return await writeContractAsync({ + async ({ amount, recipient, setModalState }: WithdrawWithDashboardArgs) => { + invariant( + activeVault, + '[useWithdrawWithDashboard] activeVault is undefined', + ); + invariant( + publicClient, + '[useFundWithDashboard] publicClient is undefined', + ); + + setModalState({ step: SubmitStepEnum.confirming }); + const tx = await writeContractAsync({ abi: dashboardAbi, address: activeVault.owner, functionName: 'withdraw', args: [recipient, amount], }); + + setModalState({ step: SubmitStepEnum.submitting, tx }); + await publicClient.waitForTransactionReceipt({ + hash: tx, + }); + + return tx; }, - [activeVault, writeContractAsync], + [activeVault, writeContractAsync, publicClient], ); return { callWithdraw, withdrawTx, - withdrawReceipt, }; }; diff --git a/features/supply/withdraw/submit-modal/index.ts b/features/supply/withdraw/submit-modal/index.ts new file mode 100644 index 00000000..1c0e3509 --- /dev/null +++ b/features/supply/withdraw/submit-modal/index.ts @@ -0,0 +1 @@ +export { SubmitModal } from './submit-modal'; diff --git a/features/supply/withdraw/submit-modal/styles.ts b/features/supply/withdraw/submit-modal/styles.ts new file mode 100644 index 00000000..dfca0ac4 --- /dev/null +++ b/features/supply/withdraw/submit-modal/styles.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +export const Content = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spaceMap.xl}px; + margin-top: ${({ theme }) => theme.spaceMap.sm}px; +`; diff --git a/features/supply/withdraw/submit-modal/submit-modal.tsx b/features/supply/withdraw/submit-modal/submit-modal.tsx new file mode 100644 index 00000000..d35a8164 --- /dev/null +++ b/features/supply/withdraw/submit-modal/submit-modal.tsx @@ -0,0 +1,122 @@ +import { FC, useMemo } from 'react'; +import { useRouter } from 'next/router'; +import { useFormContext } from 'react-hook-form'; + +import { + Loader, + Modal, + Text, + Success, + Error, + type ModalProps, + Button, +} from '@lidofinance/lido-ui'; + +import { useFormControllerContext } from 'shared/hook-form/form-controller'; +import { ButtonLink, TxLinkEtherscan } from 'shared/components'; +import { Content } from './styles'; + +import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; +import { AppPaths } from 'consts/urls'; +import { useVaultInfo } from 'features/overview/contexts'; +import { Address } from 'viem'; + +const getIconComponent = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) + return ; + if (step === SubmitStepEnum.reject) return ; + if (step === SubmitStepEnum.error) return ; + return ; +}; + +const getModalTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) return 'Funds withdrawed'; + if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; + if (step === SubmitStepEnum.error) return 'Transaction error'; + return 'You are withdrawing to an address'; +}; + +const getModalSubTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; + if (step === SubmitStepEnum.reject) + return 'User denied transaction signature'; + if (step === SubmitStepEnum.error) + return 'Got error when called contract simulation or transaction'; + return ''; +}; + +interface SubmitModalProps extends ModalProps { + submitStep: { + step: SubmitStep; + tx?: Address; + }; + setModalState: ({ step }: { step: SubmitStep }) => void; +} + +export const SubmitModal: FC = ({ + submitStep, + setModalState, +}) => { + const router = useRouter(); + const { + formState: { isSubmitting, isSubmitted }, + } = useFormContext(); + const { retryFire } = useFormControllerContext(); + const { step, tx } = submitStep ?? {}; + const { activeVault } = useVaultInfo(); + + const iconComponent = useMemo(() => getIconComponent(step), [step]); + const title = getModalTitle(step); + const subtitle = getModalSubTitle(step); + + const handleNavigateToVault = () => { + void router.push(`/${activeVault?.address}/${AppPaths.overview}`); + }; + + const handleCloseModal = () => { + setModalState({ step: SubmitStepEnum.edit }); + }; + + return ( + + + {step === SubmitStepEnum.initiate && ( + + Wait for wallet confirmation window + + )} + + {step === SubmitStepEnum.confirming && ( + + Confirm this transaction in your wallet + + )} + + {step === SubmitStepEnum.success && ( + + )} + + {(SubmitStepEnum.success === step || + SubmitStepEnum.submitting === step) && + tx && } + + {step === SubmitStepEnum.reject && ( + retry + )} + + {step === SubmitStepEnum.error && ( + close + )} + + + ); +}; diff --git a/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx b/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx index 6122b75a..16607d34 100644 --- a/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx +++ b/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx @@ -5,24 +5,27 @@ import { useCallback, createContext, useContext, + useState, } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { Address, isAddress } from 'viem'; import invariant from 'tiny-invariant'; import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; +import { + useWithdrawable, + useWithdrawWithDashboard, +} from 'features/supply/withdraw/hooks'; import { FormController, FormControllerContext, FormControllerContextValueType, } from 'shared/hook-form/form-controller'; +import { SubmitModal } from 'features/supply/withdraw/submit-modal'; import { WithdrawFormSchema } from 'features/supply/withdraw/types'; -import { - useWithdrawable, - useWithdrawWithDashboard, -} from 'features/supply/withdraw/hooks'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; type WithdrawDataContextValue = { withdrawableAmount: bigint | undefined; @@ -49,6 +52,10 @@ export const useWithdrawFormData = () => { export const WithdrawFormProvider: FC<{ children: ReactNode }> = ({ children, }) => { + const [submitStep, setSubmitStep] = useState<{ + step: SubmitStep; + tx?: Address; + }>(() => ({ step: SubmitStepEnum.edit })); const formObject = useForm({ defaultValues: { amount: undefined, @@ -59,6 +66,12 @@ export const WithdrawFormProvider: FC<{ children: ReactNode }> = ({ }); const { callWithdraw } = useWithdrawWithDashboard(); const { retryEvent, retryFire } = useFormControllerRetry(); + const setModalState = useCallback( + (submitStep: { step: SubmitStep; tx?: Address }) => { + setSubmitStep(submitStep); + }, + [], + ); const { data: withdrawableAmount, @@ -86,15 +99,27 @@ export const WithdrawFormProvider: FC<{ children: ReactNode }> = ({ async ({ amount, recipient }: WithdrawFormSchema) => { try { if (amount && recipient && isAddress(recipient)) { - await callWithdraw({ amount, recipient }); + setModalState({ step: SubmitStepEnum.initiate }); + const tx = await callWithdraw({ amount, recipient, setModalState }); + setModalState({ step: SubmitStepEnum.success, tx }); return true; } - } catch (e) { + } catch (err) { + if ( + err instanceof Error && + err.message.includes('User rejected the request') + ) { + setModalState({ step: SubmitStepEnum.reject }); + } else { + setModalState({ step: SubmitStepEnum.error }); + } + return false; } return false; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [callWithdraw], ); @@ -114,6 +139,7 @@ export const WithdrawFormProvider: FC<{ children: ReactNode }> = ({ {children} + diff --git a/shared/transaction-modal/types.ts b/shared/transaction-modal/types.ts new file mode 100644 index 00000000..3fe8c3b3 --- /dev/null +++ b/shared/transaction-modal/types.ts @@ -0,0 +1,11 @@ +export enum SubmitStepEnum { + edit = 'edit', + initiate = 'initiate', + confirming = 'confirming', + reject = 'reject', + error = 'error', + submitting = 'submitting', + success = 'success', +} + +export type SubmitStep = keyof typeof SubmitStepEnum; From 91d78b384f963b8e1710f69d35e48247013802f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 04:06:15 +0300 Subject: [PATCH 4/9] feat: add tx modals to mint and repay forms --- .../mint/form/address-field/address-field.tsx | 1 - .../form/feature-tx-info/feature-tx-info.tsx | 14 +- features/adjustment/mint/hooks/index.ts | 6 +- ...th-delegation.ts => use-mint-dashboard.ts} | 36 ++++-- .../mint-form-context/mint-form-provider.tsx | 43 +++++- .../adjustment/mint/submit-modal/index.ts | 1 + .../adjustment/mint/submit-modal/styles.ts | 8 ++ .../mint/submit-modal/submit-modal.tsx | 122 ++++++++++++++++++ features/adjustment/mint/types.ts | 2 +- .../adjustment/repay/form/balance/balance.tsx | 4 +- .../form/feature-tx-info/feature-tx-info.tsx | 12 +- features/adjustment/repay/hooks/index.ts | 2 +- .../repay/hooks/use-burn-dashboard.ts | 59 +++++++++ .../repay/hooks/use-burn-with-delegation.ts | 47 ------- .../repay-form-provider.tsx | 41 +++++- .../adjustment/repay/submit-modal/index.ts | 1 + .../adjustment/repay/submit-modal/styles.ts | 8 ++ .../repay/submit-modal/submit-modal.tsx | 122 ++++++++++++++++++ 18 files changed, 440 insertions(+), 89 deletions(-) rename features/adjustment/mint/hooks/{use-mint-with-delegation.ts => use-mint-dashboard.ts} (65%) create mode 100644 features/adjustment/mint/submit-modal/index.ts create mode 100644 features/adjustment/mint/submit-modal/styles.ts create mode 100644 features/adjustment/mint/submit-modal/submit-modal.tsx create mode 100644 features/adjustment/repay/hooks/use-burn-dashboard.ts delete mode 100644 features/adjustment/repay/hooks/use-burn-with-delegation.ts create mode 100644 features/adjustment/repay/submit-modal/index.ts create mode 100644 features/adjustment/repay/submit-modal/styles.ts create mode 100644 features/adjustment/repay/submit-modal/submit-modal.tsx diff --git a/features/adjustment/mint/form/address-field/address-field.tsx b/features/adjustment/mint/form/address-field/address-field.tsx index 3cca8abc..8986f2fc 100644 --- a/features/adjustment/mint/form/address-field/address-field.tsx +++ b/features/adjustment/mint/form/address-field/address-field.tsx @@ -14,7 +14,6 @@ export const AddressField = () => { }); }; - // TODO: add error return ( { const { watch } = useFormContext(); const [token, amount, recipient] = watch(['token', 'amount', 'recipient']); - const { isLoading, data, isError } = useSimulationMintWithDelegation({ + const { isLoading, isFetching, data, isError } = useSimulationMintDashboard({ token, amount, recipient, }); + const showLoader = isLoading || isFetching; - // TODO: add error return ( Transaction cost - {isLoading && } + {showLoader && } {/*TODO: replace static by real data*/} - {data && {'$99.99'}} - {isError && Is not available} - {!isLoading && !data && !isError && {'$0'}} + {data?.result && !showLoader && {'$0.99'}} + {isError && !showLoader && Is not available} + {!showLoader && !data?.result && !isError && -} ); diff --git a/features/adjustment/mint/hooks/index.ts b/features/adjustment/mint/hooks/index.ts index 3e2afbec..c5e844df 100644 --- a/features/adjustment/mint/hooks/index.ts +++ b/features/adjustment/mint/hooks/index.ts @@ -1,4 +1,4 @@ export { - useMintWithDelegation, - useSimulationMintWithDelegation, -} from './use-mint-with-delegation'; + useMintDashboard, + useSimulationMintDashboard, +} from './use-mint-dashboard'; diff --git a/features/adjustment/mint/hooks/use-mint-with-delegation.ts b/features/adjustment/mint/hooks/use-mint-dashboard.ts similarity index 65% rename from features/adjustment/mint/hooks/use-mint-with-delegation.ts rename to features/adjustment/mint/hooks/use-mint-dashboard.ts index 68803030..13995172 100644 --- a/features/adjustment/mint/hooks/use-mint-with-delegation.ts +++ b/features/adjustment/mint/hooks/use-mint-dashboard.ts @@ -2,8 +2,8 @@ import { useCallback } from 'react'; import { useConfig, useSimulateContract, - useWaitForTransactionReceipt, useWriteContract, + usePublicClient, UseSimulateContractParameters, } from 'wagmi'; import { Address } from 'viem'; @@ -11,12 +11,14 @@ import { Address } from 'viem'; import { dashboardAbi } from 'abi/dashboard-abi'; import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import invariant from 'tiny-invariant'; -export const useMintWithDelegation = (onMutate = () => {}) => { +export const useMintDashboard = (onMutate = () => {}) => { const { chainId } = useDappStatus(); const wagmiConfig = useConfig(); const { activeVault } = useVaultInfo(); - + const publicClient = usePublicClient(); const { data: mintTx, writeContractAsync } = useWriteContract({ config: wagmiConfig, mutation: { @@ -24,27 +26,37 @@ export const useMintWithDelegation = (onMutate = () => {}) => { }, }); - const { data: mintReceipt } = useWaitForTransactionReceipt({ - hash: mintTx, - }); - const callMint = useCallback( - async (recipient: Address, amount: bigint, token: string) => { - return await writeContractAsync({ + async ( + recipient: Address, + amount: bigint, + token: string, + setModalState: (submitStep: { step: SubmitStep; tx?: Address }) => void, + ) => { + invariant(publicClient, '[useMintDashboard] publicClient is undefined'); + + setModalState({ step: SubmitStepEnum.confirming }); + const tx = await writeContractAsync({ abi: dashboardAbi, address: activeVault?.owner as Address, functionName: token === 'stETH' ? 'mintStETH' : 'mintWstETH', args: [recipient, amount], chainId, }); + + setModalState({ step: SubmitStepEnum.submitting, tx }); + await publicClient.waitForTransactionReceipt({ + hash: tx, + }); + + return tx; }, - [chainId, writeContractAsync, activeVault?.owner], + [chainId, writeContractAsync, activeVault?.owner, publicClient], ); return { callMint, mintTx, - mintReceipt, }; }; @@ -54,7 +66,7 @@ export interface SimulationMintWithDelegationProps { amount: number; } -export const useSimulationMintWithDelegation = ({ +export const useSimulationMintDashboard = ({ recipient, token, amount, diff --git a/features/adjustment/mint/mint-form-context/mint-form-provider.tsx b/features/adjustment/mint/mint-form-context/mint-form-provider.tsx index af3915bf..35208413 100644 --- a/features/adjustment/mint/mint-form-context/mint-form-provider.tsx +++ b/features/adjustment/mint/mint-form-context/mint-form-provider.tsx @@ -5,13 +5,14 @@ import { useCallback, createContext, useContext, + useState, } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import invariant from 'tiny-invariant'; import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; import { useVaultInfo } from 'features/overview/contexts'; -import { useMintWithDelegation } from 'features/adjustment/mint/hooks'; +import { useMintDashboard } from 'features/adjustment/mint/hooks'; import { FormController, @@ -19,6 +20,9 @@ import { FormControllerContextValueType, } from 'shared/hook-form/form-controller'; import { MintFormSchema } from 'features/adjustment/mint/types'; +import { SubmitModal } from 'features/adjustment/mint/submit-modal'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { Address } from 'viem'; type MintDataContextValue = { mintableStETH: bigint; @@ -39,16 +43,20 @@ export const useMintFormData = () => { }; export const MintFormProvider: FC<{ children: ReactNode }> = ({ children }) => { + const [submitStep, setSubmitStep] = useState<{ + step: SubmitStep; + tx?: Address; + }>(() => ({ step: SubmitStepEnum.edit })); const formObject = useForm({ defaultValues: { amount: undefined, token: 'stETH', - recipient: undefined, + recipient: '' as Address, }, mode: 'all', reValidateMode: 'onChange', }); - const { callMint } = useMintWithDelegation(); + const { callMint } = useMintDashboard(); const { activeVault } = useVaultInfo(); const mintData = useMemo(() => { @@ -60,16 +68,38 @@ export const MintFormProvider: FC<{ children: ReactNode }> = ({ children }) => { }, [activeVault]); const { retryEvent, retryFire } = useFormControllerRetry(); + const setModalState = useCallback( + (submitStep: { step: SubmitStep; tx?: Address }) => { + setSubmitStep(submitStep); + }, + [], + ); const onSubmit = useCallback( async ({ recipient, amount, token }: MintFormSchema) => { - if (amount && recipient) { - await callMint(recipient, amount, token); - return true; + try { + if (amount && recipient) { + setModalState({ step: SubmitStepEnum.initiate }); + const tx = await callMint(recipient, amount, token, setModalState); + setModalState({ step: SubmitStepEnum.success, tx }); + return true; + } + + return false; + } catch (err) { + if ( + err instanceof Error && + err.message.includes('User rejected the request') + ) { + setModalState({ step: SubmitStepEnum.reject }); + } else { + setModalState({ step: SubmitStepEnum.error }); + } } return false; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [callMint], ); @@ -89,6 +119,7 @@ export const MintFormProvider: FC<{ children: ReactNode }> = ({ children }) => { {children} + diff --git a/features/adjustment/mint/submit-modal/index.ts b/features/adjustment/mint/submit-modal/index.ts new file mode 100644 index 00000000..1c0e3509 --- /dev/null +++ b/features/adjustment/mint/submit-modal/index.ts @@ -0,0 +1 @@ +export { SubmitModal } from './submit-modal'; diff --git a/features/adjustment/mint/submit-modal/styles.ts b/features/adjustment/mint/submit-modal/styles.ts new file mode 100644 index 00000000..dfca0ac4 --- /dev/null +++ b/features/adjustment/mint/submit-modal/styles.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +export const Content = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spaceMap.xl}px; + margin-top: ${({ theme }) => theme.spaceMap.sm}px; +`; diff --git a/features/adjustment/mint/submit-modal/submit-modal.tsx b/features/adjustment/mint/submit-modal/submit-modal.tsx new file mode 100644 index 00000000..ece41def --- /dev/null +++ b/features/adjustment/mint/submit-modal/submit-modal.tsx @@ -0,0 +1,122 @@ +import { FC, useMemo } from 'react'; +import { useRouter } from 'next/router'; +import { useFormContext } from 'react-hook-form'; + +import { + Loader, + Modal, + Text, + Success, + Error, + type ModalProps, + Button, +} from '@lidofinance/lido-ui'; + +import { useFormControllerContext } from 'shared/hook-form/form-controller'; +import { ButtonLink, TxLinkEtherscan } from 'shared/components'; +import { Content } from './styles'; + +import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; +import { AppPaths } from 'consts/urls'; +import { useVaultInfo } from 'features/overview/contexts'; +import { Address } from 'viem'; + +const getIconComponent = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) + return ; + if (step === SubmitStepEnum.reject) return ; + if (step === SubmitStepEnum.error) return ; + return ; +}; + +const getModalTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) return 'StETH was minted successfully'; + if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; + if (step === SubmitStepEnum.error) return 'Transaction error'; + return 'You are minting to an address'; +}; + +const getModalSubTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; + if (step === SubmitStepEnum.reject) + return 'User denied transaction signature'; + if (step === SubmitStepEnum.error) + return 'Got error when called contract simulation or transaction'; + return ''; +}; + +interface SubmitModalProps extends ModalProps { + submitStep: { + step: SubmitStep; + tx?: Address; + }; + setModalState: ({ step }: { step: SubmitStep }) => void; +} + +export const SubmitModal: FC = ({ + submitStep, + setModalState, +}) => { + const router = useRouter(); + const { + formState: { isSubmitting, isSubmitted }, + } = useFormContext(); + const { retryFire } = useFormControllerContext(); + const { step, tx } = submitStep ?? {}; + const { activeVault } = useVaultInfo(); + + const iconComponent = useMemo(() => getIconComponent(step), [step]); + const title = getModalTitle(step); + const subtitle = getModalSubTitle(step); + + const handleNavigateToVault = () => { + void router.push(`/${activeVault?.address}/${AppPaths.overview}`); + }; + + const handleCloseModal = () => { + setModalState({ step: SubmitStepEnum.edit }); + }; + + return ( + + + {step === SubmitStepEnum.initiate && ( + + Wait for wallet confirmation window + + )} + + {step === SubmitStepEnum.confirming && ( + + Confirm this transaction in your wallet + + )} + + {step === SubmitStepEnum.success && ( + + )} + + {(SubmitStepEnum.success === step || + SubmitStepEnum.submitting === step) && + tx && } + + {step === SubmitStepEnum.reject && ( + retry + )} + + {step === SubmitStepEnum.error && ( + close + )} + + + ); +}; diff --git a/features/adjustment/mint/types.ts b/features/adjustment/mint/types.ts index ff93bc83..a2051261 100644 --- a/features/adjustment/mint/types.ts +++ b/features/adjustment/mint/types.ts @@ -3,5 +3,5 @@ import { Address } from 'viem'; export type MintFormSchema = { amount: bigint | undefined; token: string; - recipient: Address | undefined; + recipient: Address; }; diff --git a/features/adjustment/repay/form/balance/balance.tsx b/features/adjustment/repay/form/balance/balance.tsx index b76e0c47..11aac132 100644 --- a/features/adjustment/repay/form/balance/balance.tsx +++ b/features/adjustment/repay/form/balance/balance.tsx @@ -29,7 +29,9 @@ export const Balance = () => { {formatBalance(balance).trimmed} {token} )} - {isError && stEth amount is not available} + {isError && !isLoading && ( + stEth amount is not available + )} ); diff --git a/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx b/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx index 41739ce4..f39f6b8d 100644 --- a/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx +++ b/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx @@ -13,7 +13,7 @@ export const FeatureTxInfo = () => { const isEnabled = !!owner && !!amount; const amountAsArg = amount ? BigInt(amount) : BigInt(0); const functionName = token === 'stETH' ? 'burnStETH' : 'burnWstETH'; - const { isLoading, data, isError } = useSimulateContract({ + const { isLoading, data, isError, isFetching } = useSimulateContract({ abi: dashboardAbi, address: owner, functionName, @@ -23,17 +23,19 @@ export const FeatureTxInfo = () => { }, }); + const showLoader = isLoading || isFetching; + return ( Transaction cost - {isLoading && } + {showLoader && } {/*TODO: replace static by real data*/} - {data && {'$99.99'}} - {isError && Is not available} - {!isLoading && !data && !isError && {'$0'}} + {data?.result && {'$0.99'}} + {isError && !showLoader && Is not available} + {!showLoader && !data?.result && !isError && -} ); diff --git a/features/adjustment/repay/hooks/index.ts b/features/adjustment/repay/hooks/index.ts index 34d9f388..2da486cb 100644 --- a/features/adjustment/repay/hooks/index.ts +++ b/features/adjustment/repay/hooks/index.ts @@ -1 +1 @@ -export { useBurnWithDelegation } from './use-burn-with-delegation'; +export { useBurnDashboard } from './use-burn-dashboard'; diff --git a/features/adjustment/repay/hooks/use-burn-dashboard.ts b/features/adjustment/repay/hooks/use-burn-dashboard.ts new file mode 100644 index 00000000..9f15338a --- /dev/null +++ b/features/adjustment/repay/hooks/use-burn-dashboard.ts @@ -0,0 +1,59 @@ +import { useCallback } from 'react'; +import { useConfig, usePublicClient, useWriteContract } from 'wagmi'; +import { Address } from 'viem'; + +import { dashboardAbi } from 'abi/dashboard-abi'; +import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; +import { useVaultInfo } from 'features/overview/contexts'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import invariant from 'tiny-invariant'; + +export const useBurnDashboard = (onMutate = () => {}) => { + const { chainId } = useDappStatus(); + const wagmiConfig = useConfig(); + const { activeVault } = useVaultInfo(); + const publicClient = usePublicClient(); + + const { data: burnTx, writeContractAsync } = useWriteContract({ + config: wagmiConfig, + mutation: { + onMutate, + }, + }); + + const callBurn = useCallback( + async ({ + token, + amount, + setModalState, + }: { + token: string; + amount: bigint; + setModalState: (submitStep: { step: SubmitStep; tx?: Address }) => void; + }) => { + invariant(publicClient, '[useBurnDashboard] publicClient is undefined'); + + setModalState({ step: SubmitStepEnum.confirming }); + const tx = await writeContractAsync({ + abi: dashboardAbi, + address: activeVault?.owner as Address, + functionName: token === 'stETH' ? 'burnStETH' : 'burnWstETH', + args: [amount], + chainId, + }); + + setModalState({ step: SubmitStepEnum.submitting, tx }); + await publicClient.waitForTransactionReceipt({ + hash: tx, + }); + + return tx; + }, + [chainId, activeVault?.owner, writeContractAsync, publicClient], + ); + + return { + callBurn, + burnTx, + }; +}; diff --git a/features/adjustment/repay/hooks/use-burn-with-delegation.ts b/features/adjustment/repay/hooks/use-burn-with-delegation.ts deleted file mode 100644 index 836823e7..00000000 --- a/features/adjustment/repay/hooks/use-burn-with-delegation.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useCallback } from 'react'; -import { - useConfig, - useWaitForTransactionReceipt, - useWriteContract, -} from 'wagmi'; -import { Address } from 'viem'; - -import { dashboardAbi } from 'abi/dashboard-abi'; -import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; -import { useVaultInfo } from 'features/overview/contexts'; - -export const useBurnWithDelegation = (onMutate = () => {}) => { - const { chainId } = useDappStatus(); - const wagmiConfig = useConfig(); - const { activeVault } = useVaultInfo(); - - const { data: burnTx, writeContractAsync } = useWriteContract({ - config: wagmiConfig, - mutation: { - onMutate, - }, - }); - - const { data: burnReceipt } = useWaitForTransactionReceipt({ - hash: burnTx, - }); - - const callBurn = useCallback( - async ({ token, amount }: { token: string; amount: bigint }) => { - return await writeContractAsync({ - abi: dashboardAbi, - address: activeVault?.owner as Address, - functionName: token === 'stETH' ? 'burnStETH' : 'burnWstETH', - args: [amount], - chainId, - }); - }, - [chainId, activeVault?.owner, writeContractAsync], - ); - - return { - callBurn, - burnTx, - burnReceipt, - }; -}; diff --git a/features/adjustment/repay/repay-form-context/repay-form-provider.tsx b/features/adjustment/repay/repay-form-context/repay-form-provider.tsx index 5c078a96..bb2a3bd8 100644 --- a/features/adjustment/repay/repay-form-context/repay-form-provider.tsx +++ b/features/adjustment/repay/repay-form-context/repay-form-provider.tsx @@ -4,12 +4,13 @@ import { useCallback, createContext, useContext, + useState, } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import invariant from 'tiny-invariant'; import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; -import { useBurnWithDelegation } from 'features/adjustment/repay/hooks'; +import { useBurnDashboard } from 'features/adjustment/repay/hooks'; import { useDappStatus, useStethBalance, useWstethBalance } from 'modules/web3'; import { @@ -19,6 +20,9 @@ import { } from 'shared/hook-form/form-controller'; import { RepayFormSchema } from 'features/adjustment/repay/types'; +import { SubmitModal } from 'features/adjustment/repay/submit-modal'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { Address } from 'viem'; type RepayDataContextValue = { stEthBalance: bigint | undefined; @@ -45,6 +49,10 @@ export const useRepayFormData = () => { }; export const RepayFormProvider = ({ children }: { children: ReactNode }) => { + const [submitStep, setSubmitStep] = useState<{ + step: SubmitStep; + tx?: Address; + }>(() => ({ step: SubmitStepEnum.edit })); const formObject = useForm({ defaultValues: { amount: undefined, @@ -53,7 +61,7 @@ export const RepayFormProvider = ({ children }: { children: ReactNode }) => { mode: 'all', reValidateMode: 'onChange', }); - const { callBurn } = useBurnWithDelegation(); + const { callBurn } = useBurnDashboard(); const { address } = useDappStatus(); const { data: stEthBalance, @@ -92,16 +100,38 @@ export const RepayFormProvider = ({ children }: { children: ReactNode }) => { ); const { retryEvent, retryFire } = useFormControllerRetry(); + const setModalState = useCallback( + (submitStep: { step: SubmitStep; tx?: Address }) => { + setSubmitStep(submitStep); + }, + [], + ); const onSubmit = useCallback( async ({ amount, token }: RepayFormSchema) => { - if (amount) { - await callBurn({ amount, token }); - return true; + try { + if (amount) { + setModalState({ step: SubmitStepEnum.initiate }); + const tx = await callBurn({ amount, token, setModalState }); + setModalState({ step: SubmitStepEnum.success, tx }); + return true; + } + + return false; + } catch (err) { + if ( + err instanceof Error && + err.message.includes('User rejected the request') + ) { + setModalState({ step: SubmitStepEnum.reject }); + } else { + setModalState({ step: SubmitStepEnum.error }); + } } return false; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [callBurn], ); @@ -121,6 +151,7 @@ export const RepayFormProvider = ({ children }: { children: ReactNode }) => { {children} + diff --git a/features/adjustment/repay/submit-modal/index.ts b/features/adjustment/repay/submit-modal/index.ts new file mode 100644 index 00000000..1c0e3509 --- /dev/null +++ b/features/adjustment/repay/submit-modal/index.ts @@ -0,0 +1 @@ +export { SubmitModal } from './submit-modal'; diff --git a/features/adjustment/repay/submit-modal/styles.ts b/features/adjustment/repay/submit-modal/styles.ts new file mode 100644 index 00000000..dfca0ac4 --- /dev/null +++ b/features/adjustment/repay/submit-modal/styles.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +export const Content = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spaceMap.xl}px; + margin-top: ${({ theme }) => theme.spaceMap.sm}px; +`; diff --git a/features/adjustment/repay/submit-modal/submit-modal.tsx b/features/adjustment/repay/submit-modal/submit-modal.tsx new file mode 100644 index 00000000..67f5d7ee --- /dev/null +++ b/features/adjustment/repay/submit-modal/submit-modal.tsx @@ -0,0 +1,122 @@ +import { FC, useMemo } from 'react'; +import { useRouter } from 'next/router'; +import { useFormContext } from 'react-hook-form'; + +import { + Loader, + Modal, + Text, + Success, + Error, + type ModalProps, + Button, +} from '@lidofinance/lido-ui'; + +import { useFormControllerContext } from 'shared/hook-form/form-controller'; +import { ButtonLink, TxLinkEtherscan } from 'shared/components'; +import { Content } from './styles'; + +import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; +import { AppPaths } from 'consts/urls'; +import { useVaultInfo } from 'features/overview/contexts'; +import { Address } from 'viem'; + +const getIconComponent = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) + return ; + if (step === SubmitStepEnum.reject) return ; + if (step === SubmitStepEnum.error) return ; + return ; +}; + +const getModalTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) return 'Repay was successfully'; + if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; + if (step === SubmitStepEnum.error) return 'Transaction error'; + return 'You are repaying to an address'; +}; + +const getModalSubTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; + if (step === SubmitStepEnum.reject) + return 'User denied transaction signature'; + if (step === SubmitStepEnum.error) + return 'Got error when called contract simulation or transaction'; + return ''; +}; + +interface SubmitModalProps extends ModalProps { + submitStep: { + step: SubmitStep; + tx?: Address; + }; + setModalState: ({ step }: { step: SubmitStep }) => void; +} + +export const SubmitModal: FC = ({ + submitStep, + setModalState, +}) => { + const router = useRouter(); + const { + formState: { isSubmitting, isSubmitted }, + } = useFormContext(); + const { retryFire } = useFormControllerContext(); + const { step, tx } = submitStep ?? {}; + const { activeVault } = useVaultInfo(); + + const iconComponent = useMemo(() => getIconComponent(step), [step]); + const title = getModalTitle(step); + const subtitle = getModalSubTitle(step); + + const handleNavigateToVault = () => { + void router.push(`/${activeVault?.address}/${AppPaths.overview}`); + }; + + const handleCloseModal = () => { + setModalState({ step: SubmitStepEnum.edit }); + }; + + return ( + + + {step === SubmitStepEnum.initiate && ( + + Wait for wallet confirmation window + + )} + + {step === SubmitStepEnum.confirming && ( + + Confirm this transaction in your wallet + + )} + + {step === SubmitStepEnum.success && ( + + )} + + {(SubmitStepEnum.success === step || + SubmitStepEnum.submitting === step) && + tx && } + + {step === SubmitStepEnum.reject && ( + retry + )} + + {step === SubmitStepEnum.error && ( + close + )} + + + ); +}; From 43a680f2dd5d3d89c5429083a930262313f42c34 Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 04:20:08 +0300 Subject: [PATCH 5/9] feat: update claim modal --- .../mint-form-context/mint-form-provider.tsx | 24 ++-- .../claim-form-provider.tsx | 35 ++++- .../hooks/use-claim-with-delegation.ts | 30 +++-- .../claim/claim-form/submit-modal/index.ts | 1 + .../claim/claim-form/submit-modal/styles.ts | 8 ++ .../claim-form/submit-modal/submit-modal.tsx | 122 ++++++++++++++++++ 6 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 features/claim/claim-form/submit-modal/index.ts create mode 100644 features/claim/claim-form/submit-modal/styles.ts create mode 100644 features/claim/claim-form/submit-modal/submit-modal.tsx diff --git a/features/adjustment/mint/mint-form-context/mint-form-provider.tsx b/features/adjustment/mint/mint-form-context/mint-form-provider.tsx index 35208413..db12f81f 100644 --- a/features/adjustment/mint/mint-form-context/mint-form-provider.tsx +++ b/features/adjustment/mint/mint-form-context/mint-form-provider.tsx @@ -77,23 +77,21 @@ export const MintFormProvider: FC<{ children: ReactNode }> = ({ children }) => { const onSubmit = useCallback( async ({ recipient, amount, token }: MintFormSchema) => { - try { - if (amount && recipient) { + if (amount && recipient) { + try { setModalState({ step: SubmitStepEnum.initiate }); const tx = await callMint(recipient, amount, token, setModalState); setModalState({ step: SubmitStepEnum.success, tx }); return true; - } - - return false; - } catch (err) { - if ( - err instanceof Error && - err.message.includes('User rejected the request') - ) { - setModalState({ step: SubmitStepEnum.reject }); - } else { - setModalState({ step: SubmitStepEnum.error }); + } catch (err) { + if ( + err instanceof Error && + err.message.includes('User rejected the request') + ) { + setModalState({ step: SubmitStepEnum.reject }); + } else { + setModalState({ step: SubmitStepEnum.error }); + } } } diff --git a/features/claim/claim-form/claim-form-context/claim-form-provider.tsx b/features/claim/claim-form/claim-form-context/claim-form-provider.tsx index 9ad83a2c..58756c4a 100644 --- a/features/claim/claim-form/claim-form-context/claim-form-provider.tsx +++ b/features/claim/claim-form/claim-form-context/claim-form-provider.tsx @@ -5,8 +5,9 @@ import { useCallback, createContext, useContext, + useState, } from 'react'; -import { isAddress, ReadContractErrorType } from 'viem'; +import { Address, isAddress, ReadContractErrorType } from 'viem'; import { useReadContract } from 'wagmi'; import { FormProvider, useForm } from 'react-hook-form'; @@ -23,6 +24,8 @@ import { import { dashboardAbi } from 'abi/dashboard-abi'; import { ClaimFormSchema } from 'features/claim/claim-form/types'; import invariant from 'tiny-invariant'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { SubmitModal } from '../submit-modal'; type ClaimDataContextValue = { availableToClaim: bigint | undefined; @@ -47,6 +50,10 @@ export const useClaimFormData = () => { export const ClaimFormProvider: FC<{ children: ReactNode }> = ({ children, }) => { + const [submitStep, setSubmitStep] = useState<{ + step: SubmitStep; + tx?: Address; + }>(() => ({ step: SubmitStepEnum.edit })); const { activeVault } = useVaultInfo(); const { @@ -83,17 +90,36 @@ export const ClaimFormProvider: FC<{ children: ReactNode }> = ({ const { callClaim } = useClaimDashboard(); const { retryEvent, retryFire } = useFormControllerRetry(); + const setModalState = useCallback( + (submitStep: { step: SubmitStep; tx?: Address }) => { + setSubmitStep(submitStep); + }, + [], + ); const onSubmit = useCallback( async ({ recipient }: ClaimFormSchema) => { if (recipient && isAddress(recipient)) { - // TODO: resolve recipient if ens domain - await callClaim(recipient); - return true; + try { + setModalState({ step: SubmitStepEnum.initiate }); + const tx = await callClaim(recipient, setModalState); + setModalState({ step: SubmitStepEnum.success, tx }); + return true; + } catch (err) { + if ( + err instanceof Error && + err.message.includes('User rejected the request') + ) { + setModalState({ step: SubmitStepEnum.reject }); + } else { + setModalState({ step: SubmitStepEnum.error }); + } + } } return false; }, + // eslint-disable-next-line react-hooks/exhaustive-deps [callClaim], ); @@ -113,6 +139,7 @@ export const ClaimFormProvider: FC<{ children: ReactNode }> = ({ {children} + diff --git a/features/claim/claim-form/hooks/use-claim-with-delegation.ts b/features/claim/claim-form/hooks/use-claim-with-delegation.ts index 398e4a1a..a8a8cb16 100644 --- a/features/claim/claim-form/hooks/use-claim-with-delegation.ts +++ b/features/claim/claim-form/hooks/use-claim-with-delegation.ts @@ -2,7 +2,7 @@ import { useCallback } from 'react'; import { useConfig, useSimulateContract, - useWaitForTransactionReceipt, + usePublicClient, useWriteContract, } from 'wagmi'; import { Address } from 'viem'; @@ -11,11 +11,13 @@ import { dashboardAbi } from 'abi/dashboard-abi'; import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; +import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; export const useClaimDashboard = (onMutate = () => {}) => { const { chainId } = useDappStatus(); const wagmiConfig = useConfig(); const { activeVault } = useVaultInfo(); + const publicClient = usePublicClient(); const owner = activeVault?.owner; const { data: claimTx, writeContractAsync } = useWriteContract({ @@ -25,29 +27,39 @@ export const useClaimDashboard = (onMutate = () => {}) => { }, }); - const { data: claimReceipt } = useWaitForTransactionReceipt({ - hash: claimTx, - }); - const callClaim = useCallback( - async (recipient: Address) => { + async ( + recipient: Address, + setModalState: (submitStep: { step: SubmitStep; tx?: Address }) => void, + ) => { invariant(owner, '[useClaimDashboard] owner is not available'); + invariant( + publicClient, + '[useClaimDashboard] publicClient is not available', + ); - return await writeContractAsync({ + setModalState({ step: SubmitStepEnum.confirming }); + const tx = await writeContractAsync({ abi: dashboardAbi, address: owner, functionName: 'claimNodeOperatorFee', args: [recipient], chainId, }); + + setModalState({ step: SubmitStepEnum.submitting, tx }); + await publicClient.waitForTransactionReceipt({ + hash: tx, + }); + + return tx; }, - [chainId, writeContractAsync, owner], + [chainId, writeContractAsync, owner, publicClient], ); return { callClaim, claimTx, - claimReceipt, }; }; diff --git a/features/claim/claim-form/submit-modal/index.ts b/features/claim/claim-form/submit-modal/index.ts new file mode 100644 index 00000000..1c0e3509 --- /dev/null +++ b/features/claim/claim-form/submit-modal/index.ts @@ -0,0 +1 @@ +export { SubmitModal } from './submit-modal'; diff --git a/features/claim/claim-form/submit-modal/styles.ts b/features/claim/claim-form/submit-modal/styles.ts new file mode 100644 index 00000000..dfca0ac4 --- /dev/null +++ b/features/claim/claim-form/submit-modal/styles.ts @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +export const Content = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spaceMap.xl}px; + margin-top: ${({ theme }) => theme.spaceMap.sm}px; +`; diff --git a/features/claim/claim-form/submit-modal/submit-modal.tsx b/features/claim/claim-form/submit-modal/submit-modal.tsx new file mode 100644 index 00000000..37aa02a8 --- /dev/null +++ b/features/claim/claim-form/submit-modal/submit-modal.tsx @@ -0,0 +1,122 @@ +import { FC, useMemo } from 'react'; +import { useRouter } from 'next/router'; +import { useFormContext } from 'react-hook-form'; + +import { + Loader, + Modal, + Text, + Success, + Error, + type ModalProps, + Button, +} from '@lidofinance/lido-ui'; + +import { useFormControllerContext } from 'shared/hook-form/form-controller'; +import { ButtonLink, TxLinkEtherscan } from 'shared/components'; +import { Content } from './styles'; + +import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; +import { AppPaths } from 'consts/urls'; +import { useVaultInfo } from 'features/overview/contexts'; +import { Address } from 'viem'; + +const getIconComponent = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) + return ; + if (step === SubmitStepEnum.reject) return ; + if (step === SubmitStepEnum.error) return ; + return ; +}; + +const getModalTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.success) return 'Fees was getting successfully'; + if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; + if (step === SubmitStepEnum.error) return 'Transaction error'; + return 'You are requesting fees'; +}; + +const getModalSubTitle = (step: SubmitStep) => { + if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; + if (step === SubmitStepEnum.reject) + return 'User denied transaction signature'; + if (step === SubmitStepEnum.error) + return 'Got error when called contract simulation or transaction'; + return ''; +}; + +interface SubmitModalProps extends ModalProps { + submitStep: { + step: SubmitStep; + tx?: Address; + }; + setModalState: ({ step }: { step: SubmitStep }) => void; +} + +export const SubmitModal: FC = ({ + submitStep, + setModalState, +}) => { + const router = useRouter(); + const { + formState: { isSubmitting, isSubmitted }, + } = useFormContext(); + const { retryFire } = useFormControllerContext(); + const { step, tx } = submitStep ?? {}; + const { activeVault } = useVaultInfo(); + + const iconComponent = useMemo(() => getIconComponent(step), [step]); + const title = getModalTitle(step); + const subtitle = getModalSubTitle(step); + + const handleNavigateToVault = () => { + void router.push(`/${activeVault?.address}/${AppPaths.overview}`); + }; + + const handleCloseModal = () => { + setModalState({ step: SubmitStepEnum.edit }); + }; + + return ( + + + {step === SubmitStepEnum.initiate && ( + + Wait for wallet confirmation window + + )} + + {step === SubmitStepEnum.confirming && ( + + Confirm this transaction in your wallet + + )} + + {step === SubmitStepEnum.success && ( + + )} + + {(SubmitStepEnum.success === step || + SubmitStepEnum.submitting === step) && + tx && } + + {step === SubmitStepEnum.reject && ( + retry + )} + + {step === SubmitStepEnum.error && ( + close + )} + + + ); +}; From 591a206b3c0b4c718747c9c84d30b2c1f58ad79e Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 04:26:45 +0300 Subject: [PATCH 6/9] fix: fix types --- config/network/index.ts | 2 +- .../adjustment/repay/form/feature-tx-info/feature-tx-info.tsx | 4 ++-- features/home/hooks/use-connected-vaults-number.ts | 4 +++- .../supply/withdraw/form/feature-tx-info/feature-tx-info.tsx | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/config/network/index.ts b/config/network/index.ts index 929ffe2d..5f8c5ac2 100644 --- a/config/network/index.ts +++ b/config/network/index.ts @@ -62,7 +62,7 @@ export const getNetworkConfig = (chain: CHAINS): NetworkConfig | undefined => { }; export const getContractAddress = ( - chain: CHAINS | undefined, + chain: CHAINS, contractName: CONTRACT_NAMES, ): Address | undefined => { const networkConfig = getNetworkConfig(chain); diff --git a/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx b/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx index f39f6b8d..58f7a5fb 100644 --- a/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx +++ b/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx @@ -32,8 +32,8 @@ export const FeatureTxInfo = () => { Transaction cost {showLoader && } - {/*TODO: replace static by real data*/} - {data?.result && {'$0.99'}} + {/* TODO: fix simulation */} + {!!data?.result && {'$0.99'}} {isError && !showLoader && Is not available} {!showLoader && !data?.result && !isError && -} diff --git a/features/home/hooks/use-connected-vaults-number.ts b/features/home/hooks/use-connected-vaults-number.ts index 07b66f91..8a250cc6 100644 --- a/features/home/hooks/use-connected-vaults-number.ts +++ b/features/home/hooks/use-connected-vaults-number.ts @@ -4,7 +4,9 @@ import { VaultHubAbi } from 'abi/vault-hub'; export const useConnectedVaultsNumber = () => { const publicClient = usePublicClient(); - const address = getContractAddress(publicClient?.chain.id, 'vaultHub'); + const address = publicClient?.chain.id + ? getContractAddress(publicClient.chain.id, 'vaultHub') + : undefined; return useReadContract({ address, diff --git a/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx b/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx index 657d584c..b6c2a6b6 100644 --- a/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx +++ b/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx @@ -35,7 +35,7 @@ export const FeatureTxInfo = () => { )} {/* TODO: fix simulation */} - {data?.result && !showLoader && {'$0.99'}} + {!!data?.result && !showLoader && {'$0.99'}} {!data?.result && !isError && !showLoader && -} From fa63c3e550c5b9e5314303bc92d1c6c8f1316860 Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 11:42:59 +0300 Subject: [PATCH 7/9] fix: resolve comments after review --- .../form/feature-tx-info/feature-tx-info.tsx | 11 +++-- .../mint/submit-modal/submit-modal.tsx | 45 ++++++++++++------- .../form/feature-tx-info/feature-tx-info.tsx | 13 +++--- .../repay/submit-modal/submit-modal.tsx | 45 ++++++++++++------- .../form/feature-tx-info/feature-tx-info.tsx | 11 ++--- features/claim/claim-form/hooks/index.ts | 2 +- ...h-delegation.ts => use-claim-dashboard.ts} | 0 .../claim-form/submit-modal/submit-modal.tsx | 45 ++++++++++++------- .../submit-modal/submit-modal.tsx | 45 ++++++++++++------- features/supply/fund/form/balance/balance.tsx | 9 ++-- .../form/feature-tx-info/feature-tx-info.tsx | 19 ++++---- .../supply/fund/submit-modal/submit-modal.tsx | 45 ++++++++++++------- .../form/feature-tx-info/feature-tx-info.tsx | 22 +++++---- .../withdraw/submit-modal/submit-modal.tsx | 45 ++++++++++++------- 14 files changed, 219 insertions(+), 138 deletions(-) rename features/claim/claim-form/hooks/{use-claim-with-delegation.ts => use-claim-dashboard.ts} (100%) diff --git a/features/adjustment/mint/form/feature-tx-info/feature-tx-info.tsx b/features/adjustment/mint/form/feature-tx-info/feature-tx-info.tsx index f4e3b683..743d1783 100644 --- a/features/adjustment/mint/form/feature-tx-info/feature-tx-info.tsx +++ b/features/adjustment/mint/form/feature-tx-info/feature-tx-info.tsx @@ -7,12 +7,11 @@ import { AmountInfo, InfoRow, Wrapper } from './styles'; export const FeatureTxInfo = () => { const { watch } = useFormContext(); const [token, amount, recipient] = watch(['token', 'amount', 'recipient']); - const { isLoading, isFetching, data, isError } = useSimulationMintDashboard({ + const { isLoading, data, isError } = useSimulationMintDashboard({ token, amount, recipient, }); - const showLoader = isLoading || isFetching; return ( @@ -20,11 +19,11 @@ export const FeatureTxInfo = () => { Transaction cost - {showLoader && } + {isLoading && } {/*TODO: replace static by real data*/} - {data?.result && !showLoader && {'$0.99'}} - {isError && !showLoader && Is not available} - {!showLoader && !data?.result && !isError && -} + {data?.result && !isLoading && {data?.result}} + {isError && !isLoading && Is not available} + {!isLoading && !data?.result && !isError && -} ); diff --git a/features/adjustment/mint/submit-modal/submit-modal.tsx b/features/adjustment/mint/submit-modal/submit-modal.tsx index ece41def..45e84219 100644 --- a/features/adjustment/mint/submit-modal/submit-modal.tsx +++ b/features/adjustment/mint/submit-modal/submit-modal.tsx @@ -22,27 +22,42 @@ import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; const getIconComponent = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) - return ; - if (step === SubmitStepEnum.reject) return ; - if (step === SubmitStepEnum.error) return ; - return ; + switch (step) { + case SubmitStepEnum.success: + return ; + case SubmitStepEnum.reject: + return ; + case SubmitStepEnum.error: + return ; + default: + return ; + } }; const getModalTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) return 'StETH was minted successfully'; - if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; - if (step === SubmitStepEnum.error) return 'Transaction error'; - return 'You are minting to an address'; + switch (step) { + case SubmitStepEnum.success: + return 'StETH was minted successfully'; + case SubmitStepEnum.reject: + return 'Wallet tx signature'; + case SubmitStepEnum.error: + return 'Transaction error'; + default: + return 'You are minting to an address'; + } }; const getModalSubTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; - if (step === SubmitStepEnum.reject) - return 'User denied transaction signature'; - if (step === SubmitStepEnum.error) - return 'Got error when called contract simulation or transaction'; - return ''; + switch (step) { + case SubmitStepEnum.submitting: + return 'Awaiting wallet signature'; + case SubmitStepEnum.reject: + return 'User denied transaction signature'; + case SubmitStepEnum.error: + return 'Got error when called contract simulation or transaction'; + default: + return ''; + } }; interface SubmitModalProps extends ModalProps { diff --git a/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx b/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx index 58f7a5fb..dfb574a8 100644 --- a/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx +++ b/features/adjustment/repay/form/feature-tx-info/feature-tx-info.tsx @@ -13,7 +13,7 @@ export const FeatureTxInfo = () => { const isEnabled = !!owner && !!amount; const amountAsArg = amount ? BigInt(amount) : BigInt(0); const functionName = token === 'stETH' ? 'burnStETH' : 'burnWstETH'; - const { isLoading, data, isError, isFetching } = useSimulateContract({ + const { isLoading, data, isError } = useSimulateContract({ abi: dashboardAbi, address: owner, functionName, @@ -23,19 +23,16 @@ export const FeatureTxInfo = () => { }, }); - const showLoader = isLoading || isFetching; - return ( Transaction cost - {showLoader && } - {/* TODO: fix simulation */} - {!!data?.result && {'$0.99'}} - {isError && !showLoader && Is not available} - {!showLoader && !data?.result && !isError && -} + {isLoading && } + {!!data?.result && {data?.result}} + {isError && !isLoading && Is not available} + {!isLoading && !data?.result && !isError && -} ); diff --git a/features/adjustment/repay/submit-modal/submit-modal.tsx b/features/adjustment/repay/submit-modal/submit-modal.tsx index 67f5d7ee..ac3f10ae 100644 --- a/features/adjustment/repay/submit-modal/submit-modal.tsx +++ b/features/adjustment/repay/submit-modal/submit-modal.tsx @@ -22,27 +22,42 @@ import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; const getIconComponent = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) - return ; - if (step === SubmitStepEnum.reject) return ; - if (step === SubmitStepEnum.error) return ; - return ; + switch (step) { + case SubmitStepEnum.success: + return ; + case SubmitStepEnum.reject: + return ; + case SubmitStepEnum.error: + return ; + default: + return ; + } }; const getModalTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) return 'Repay was successfully'; - if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; - if (step === SubmitStepEnum.error) return 'Transaction error'; - return 'You are repaying to an address'; + switch (step) { + case SubmitStepEnum.success: + return 'Repay was successfully'; + case SubmitStepEnum.reject: + return 'Wallet tx signature'; + case SubmitStepEnum.error: + return 'Transaction error'; + default: + return 'You are repaying to an address'; + } }; const getModalSubTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; - if (step === SubmitStepEnum.reject) - return 'User denied transaction signature'; - if (step === SubmitStepEnum.error) - return 'Got error when called contract simulation or transaction'; - return ''; + switch (step) { + case SubmitStepEnum.submitting: + return 'Awaiting wallet signature'; + case SubmitStepEnum.reject: + return 'User denied transaction signature'; + case SubmitStepEnum.error: + return 'Got error when called contract simulation or transaction'; + default: + return ''; + } }; interface SubmitModalProps extends ModalProps { diff --git a/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx b/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx index 753ce647..d3834e3a 100644 --- a/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx +++ b/features/claim/claim-form/form/feature-tx-info/feature-tx-info.tsx @@ -8,8 +8,7 @@ import { Address } from 'viem'; export const FeatureTxInfo = () => { const { watch } = useFormContext(); const recipient: Address = watch('recipient'); - const { data, isLoading, isError, isFetching } = - useSimulationClaimDashboard(recipient); + const { data, isLoading, isError } = useSimulationClaimDashboard(recipient); return ( @@ -17,11 +16,13 @@ export const FeatureTxInfo = () => { Transaction cost - {(isLoading || isFetching) && } + {isLoading && } {/*TODO: get simulated data*/} {/*TODO: show error message*/} - {data && {'$0.99'}} - {isError && -} + {!!data?.result && !isLoading && ( + {data?.result} + )} + {isError && !isLoading && -} {!isLoading && !data && !isError && -} diff --git a/features/claim/claim-form/hooks/index.ts b/features/claim/claim-form/hooks/index.ts index 70d4390f..84e3dd93 100644 --- a/features/claim/claim-form/hooks/index.ts +++ b/features/claim/claim-form/hooks/index.ts @@ -1,4 +1,4 @@ export { useClaimDashboard, useSimulationClaimDashboard, -} from './use-claim-with-delegation'; +} from './use-claim-dashboard'; diff --git a/features/claim/claim-form/hooks/use-claim-with-delegation.ts b/features/claim/claim-form/hooks/use-claim-dashboard.ts similarity index 100% rename from features/claim/claim-form/hooks/use-claim-with-delegation.ts rename to features/claim/claim-form/hooks/use-claim-dashboard.ts diff --git a/features/claim/claim-form/submit-modal/submit-modal.tsx b/features/claim/claim-form/submit-modal/submit-modal.tsx index 37aa02a8..62498365 100644 --- a/features/claim/claim-form/submit-modal/submit-modal.tsx +++ b/features/claim/claim-form/submit-modal/submit-modal.tsx @@ -22,27 +22,42 @@ import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; const getIconComponent = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) - return ; - if (step === SubmitStepEnum.reject) return ; - if (step === SubmitStepEnum.error) return ; - return ; + switch (step) { + case SubmitStepEnum.success: + return ; + case SubmitStepEnum.reject: + return ; + case SubmitStepEnum.error: + return ; + default: + return ; + } }; const getModalTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) return 'Fees was getting successfully'; - if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; - if (step === SubmitStepEnum.error) return 'Transaction error'; - return 'You are requesting fees'; + switch (step) { + case SubmitStepEnum.success: + return 'Fees was getting successfully'; + case SubmitStepEnum.reject: + return 'Wallet tx signature'; + case SubmitStepEnum.error: + return 'Transaction error'; + default: + return 'You are requesting fees'; + } }; const getModalSubTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; - if (step === SubmitStepEnum.reject) - return 'User denied transaction signature'; - if (step === SubmitStepEnum.error) - return 'Got error when called contract simulation or transaction'; - return ''; + switch (step) { + case SubmitStepEnum.submitting: + return 'Awaiting wallet signature'; + case SubmitStepEnum.reject: + return 'User denied transaction signature'; + case SubmitStepEnum.error: + return 'Got error when called contract simulation or transaction'; + default: + return ''; + } }; interface SubmitModalProps extends ModalProps { diff --git a/features/create-vault/create-vault-form/submit-modal/submit-modal.tsx b/features/create-vault/create-vault-form/submit-modal/submit-modal.tsx index 2bcec850..98bf204f 100644 --- a/features/create-vault/create-vault-form/submit-modal/submit-modal.tsx +++ b/features/create-vault/create-vault-form/submit-modal/submit-modal.tsx @@ -21,27 +21,42 @@ import { SubmitStepEnum, SubmitStep } from 'features/create-vault/types'; import { AppPaths } from '../../../../consts/urls'; const getIconComponent = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) - return ; - if (step === SubmitStepEnum.reject) return ; - if (step === SubmitStepEnum.error) return ; - return ; + switch (step) { + case SubmitStepEnum.success: + return ; + case SubmitStepEnum.reject: + return ; + case SubmitStepEnum.error: + return ; + default: + return ; + } }; const getModalTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) return 'New vault has been created'; - if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; - if (step === SubmitStepEnum.error) return 'Simulation error'; - return 'You are creating a new vault'; + switch (step) { + case SubmitStepEnum.success: + return 'New vault has been created'; + case SubmitStepEnum.reject: + return 'Wallet tx signature'; + case SubmitStepEnum.error: + return 'Simulation error'; + default: + return 'You are creating a new vault'; + } }; const getModalSubTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; - if (step === SubmitStepEnum.reject) - return 'User denied transaction signature'; - if (step === SubmitStepEnum.error) - return 'Got error when called contract simulation'; - return ''; + switch (step) { + case SubmitStepEnum.submitting: + return 'Awaiting wallet signature'; + case SubmitStepEnum.reject: + return 'User denied transaction signature'; + case SubmitStepEnum.error: + return 'Got error when called contract simulation'; + default: + return ''; + } }; export const SubmitModal: FC = () => { diff --git a/features/supply/fund/form/balance/balance.tsx b/features/supply/fund/form/balance/balance.tsx index 31e3e68c..defd0d9e 100644 --- a/features/supply/fund/form/balance/balance.tsx +++ b/features/supply/fund/form/balance/balance.tsx @@ -8,10 +8,9 @@ import { formatBalance } from 'utils'; export const Balance = () => { const { address } = useDappStatus(); - const { data, isLoading, isFetching, isSuccess, isError } = useBalance({ + const { data, isLoading, isSuccess, isError } = useBalance({ address, }); - const showLoader = isLoading || isFetching; return ( @@ -19,11 +18,11 @@ export const Balance = () => { Available to Supply - {showLoader && } - {isSuccess && !showLoader && ( + {isLoading && } + {isSuccess && !isLoading && ( {formatBalance(data.value).trimmed} ETH )} - {isError && -} + {isError && !isLoading && -} ); diff --git a/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx b/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx index b3549c42..82de3e29 100644 --- a/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx +++ b/features/supply/fund/form/feature-tx-info/feature-tx-info.tsx @@ -1,20 +1,18 @@ import { Text, Loader } from '@lidofinance/lido-ui'; import { useFormContext } from 'react-hook-form'; import { useSimulationFundWithDashboard } from 'features/supply/fund/hooks'; +import { useVaultInfo } from 'features/overview/contexts'; import { AmountInfo, InfoRow, Wrapper } from './styles'; -import { useVaultInfo } from 'features/overview/contexts'; export const FeatureTxInfo = () => { const { watch } = useFormContext(); const amount: bigint | undefined = watch('amount'); const { activeVault } = useVaultInfo(); - const { data, isLoading, isFetching, isError } = - useSimulationFundWithDashboard({ - address: activeVault?.owner, - amount: amount ?? 0n, - }); - const showLoader = isLoading || isFetching; + const { data, isLoading, isError } = useSimulationFundWithDashboard({ + address: activeVault?.owner, + amount: amount ?? 0n, + }); return ( @@ -22,17 +20,16 @@ export const FeatureTxInfo = () => { Transaction cost - {showLoader && } + {isLoading && } {isError && ( Can't load gas simulation info )} - {/* TODO: check result, useSimulationFundWithDashboard returns data.result === undefined */} - {!!data?.result && !showLoader && ( + {!!data?.result && !isLoading && ( {data?.result} )} - {!data?.result && !isError && !showLoader && -} + {!data?.result && !isError && !isLoading && -} ); diff --git a/features/supply/fund/submit-modal/submit-modal.tsx b/features/supply/fund/submit-modal/submit-modal.tsx index 507bfdc7..f88aabcd 100644 --- a/features/supply/fund/submit-modal/submit-modal.tsx +++ b/features/supply/fund/submit-modal/submit-modal.tsx @@ -22,27 +22,42 @@ import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; const getIconComponent = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) - return ; - if (step === SubmitStepEnum.reject) return ; - if (step === SubmitStepEnum.error) return ; - return ; + switch (step) { + case SubmitStepEnum.success: + return ; + case SubmitStepEnum.reject: + return ; + case SubmitStepEnum.error: + return ; + default: + return ; + } }; const getModalTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) return 'Vault has been funded'; - if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; - if (step === SubmitStepEnum.error) return 'Transaction error'; - return 'You are supplying to the vault'; + switch (step) { + case SubmitStepEnum.success: + return 'Vault has been funded'; + case SubmitStepEnum.reject: + return 'Wallet tx signature'; + case SubmitStepEnum.error: + return 'Transaction error'; + default: + return 'You are supplying to the vault'; + } }; const getModalSubTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; - if (step === SubmitStepEnum.reject) - return 'User denied transaction signature'; - if (step === SubmitStepEnum.error) - return 'Got error when called contract simulation or transaction'; - return ''; + switch (step) { + case SubmitStepEnum.submitting: + return 'Awaiting wallet signature'; + case SubmitStepEnum.reject: + return 'User denied transaction signature'; + case SubmitStepEnum.error: + return 'Got error when called contract simulation or transaction'; + default: + return ''; + } }; interface SubmitModalProps extends ModalProps { diff --git a/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx b/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx index b6c2a6b6..a5c7250d 100644 --- a/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx +++ b/features/supply/withdraw/form/feature-tx-info/feature-tx-info.tsx @@ -3,18 +3,15 @@ import { useSimulateWithdrawDashboard } from 'features/supply/withdraw/hooks'; import { Loader, Text } from '@lidofinance/lido-ui'; import { AmountInfo, InfoRow, Wrapper } from './styles'; -import { formatBalance } from '../../../../../utils'; +import { formatBalance } from 'utils'; export const FeatureTxInfo = () => { const { watch } = useFormContext(); const [recipient, amount] = watch(['recipient', 'amount']); - const { isLoading, isFetching, isError, data } = useSimulateWithdrawDashboard( - { - recipient, - amount, - }, - ); - const showLoader = isLoading || isFetching; + const { isLoading, isError, data } = useSimulateWithdrawDashboard({ + recipient, + amount, + }); return ( @@ -28,15 +25,16 @@ export const FeatureTxInfo = () => { Transaction cost - {showLoader && } + {isLoading && } {isError && ( Can't load gas simulation info )} - {/* TODO: fix simulation */} - {!!data?.result && !showLoader && {'$0.99'}} - {!data?.result && !isError && !showLoader && -} + {!!data?.result && !isLoading && ( + {data?.result} + )} + {!data?.result && !isError && !isLoading && -} ); diff --git a/features/supply/withdraw/submit-modal/submit-modal.tsx b/features/supply/withdraw/submit-modal/submit-modal.tsx index d35a8164..3173529c 100644 --- a/features/supply/withdraw/submit-modal/submit-modal.tsx +++ b/features/supply/withdraw/submit-modal/submit-modal.tsx @@ -22,27 +22,42 @@ import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; const getIconComponent = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) - return ; - if (step === SubmitStepEnum.reject) return ; - if (step === SubmitStepEnum.error) return ; - return ; + switch (step) { + case SubmitStepEnum.success: + return ; + case SubmitStepEnum.reject: + return ; + case SubmitStepEnum.error: + return ; + default: + return ; + } }; const getModalTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.success) return 'Funds withdrawed'; - if (step === SubmitStepEnum.reject) return 'Wallet tx signature'; - if (step === SubmitStepEnum.error) return 'Transaction error'; - return 'You are withdrawing to an address'; + switch (step) { + case SubmitStepEnum.success: + return 'Funds withdrawed'; + case SubmitStepEnum.reject: + return 'Wallet tx signature'; + case SubmitStepEnum.error: + return 'Transaction error'; + default: + return 'You are withdrawing to an address'; + } }; const getModalSubTitle = (step: SubmitStep) => { - if (step === SubmitStepEnum.submitting) return 'Awaiting wallet signature'; - if (step === SubmitStepEnum.reject) - return 'User denied transaction signature'; - if (step === SubmitStepEnum.error) - return 'Got error when called contract simulation or transaction'; - return ''; + switch (step) { + case SubmitStepEnum.submitting: + return 'Awaiting wallet signature'; + case SubmitStepEnum.reject: + return 'User denied transaction signature'; + case SubmitStepEnum.error: + return 'Got error when called contract simulation or transaction'; + default: + return ''; + } }; interface SubmitModalProps extends ModalProps { From e80d5b814383a9a9a75ac22dfd20040b20bf7e8e Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 14:21:39 +0300 Subject: [PATCH 8/9] feat: remove tech dept --- features/adjustment/mint/hooks/use-mint.ts | 5 +- .../mint-form-context/mint-form-provider.tsx | 7 +- .../mint/submit-modal/submit-modal.tsx | 137 ------------------ features/adjustment/repay/hooks/use-burn.ts | 5 +- .../repay-form-provider.tsx | 4 +- .../adjustment/repay/submit-modal/index.ts | 1 - .../adjustment/repay/submit-modal/styles.ts | 8 - .../claim-form-provider.tsx | 5 +- features/claim/claim-form/hooks/use-claim.ts | 5 +- .../claim-form/submit-modal/submit-modal.tsx | 5 +- .../fund-form-context/fund-form-provider.tsx | 7 +- features/supply/fund/hooks/use-fund.ts | 5 +- features/supply/fund/submit-modal/index.ts | 1 - features/supply/fund/submit-modal/styles.ts | 8 - .../supply/fund/submit-modal/submit-modal.tsx | 137 ------------------ .../hooks/use-withdraw-with-dashboard.ts | 5 +- .../supply/withdraw/submit-modal/index.ts | 1 - .../supply/withdraw/submit-modal/styles.ts | 8 - .../withdraw/submit-modal/submit-modal.tsx | 137 ------------------ .../withdraw-form-provider.tsx | 7 +- shared/components/index.ts | 1 + .../components}/submit-modal/index.ts | 1 + .../components}/submit-modal/styles.ts | 0 .../components}/submit-modal/submit-modal.tsx | 13 +- .../submit-modal}/types.ts | 0 25 files changed, 56 insertions(+), 457 deletions(-) delete mode 100644 features/adjustment/mint/submit-modal/submit-modal.tsx delete mode 100644 features/adjustment/repay/submit-modal/index.ts delete mode 100644 features/adjustment/repay/submit-modal/styles.ts delete mode 100644 features/supply/fund/submit-modal/index.ts delete mode 100644 features/supply/fund/submit-modal/styles.ts delete mode 100644 features/supply/fund/submit-modal/submit-modal.tsx delete mode 100644 features/supply/withdraw/submit-modal/index.ts delete mode 100644 features/supply/withdraw/submit-modal/styles.ts delete mode 100644 features/supply/withdraw/submit-modal/submit-modal.tsx rename {features/adjustment/mint => shared/components}/submit-modal/index.ts (64%) rename {features/adjustment/mint => shared/components}/submit-modal/styles.ts (100%) rename {features/adjustment/repay => shared/components}/submit-modal/submit-modal.tsx (93%) rename shared/{transaction-modal => components/submit-modal}/types.ts (100%) diff --git a/features/adjustment/mint/hooks/use-mint.ts b/features/adjustment/mint/hooks/use-mint.ts index 7cc4a363..25a7cea0 100644 --- a/features/adjustment/mint/hooks/use-mint.ts +++ b/features/adjustment/mint/hooks/use-mint.ts @@ -11,7 +11,10 @@ import { Address } from 'viem'; import { dashboardAbi } from 'abi/dashboard-abi'; import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; import invariant from 'tiny-invariant'; export const useMint = (onMutate = () => {}) => { diff --git a/features/adjustment/mint/mint-form-context/mint-form-provider.tsx b/features/adjustment/mint/mint-form-context/mint-form-provider.tsx index 83d7e5a4..80c1c87a 100644 --- a/features/adjustment/mint/mint-form-context/mint-form-provider.tsx +++ b/features/adjustment/mint/mint-form-context/mint-form-provider.tsx @@ -20,8 +20,11 @@ import { FormControllerContextValueType, } from 'shared/hook-form/form-controller'; import { MintFormSchema } from 'features/adjustment/mint/types'; -import { SubmitModal } from 'features/adjustment/mint/submit-modal'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { SubmitModal } from 'shared/components'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; import { Address } from 'viem'; type MintDataContextValue = { diff --git a/features/adjustment/mint/submit-modal/submit-modal.tsx b/features/adjustment/mint/submit-modal/submit-modal.tsx deleted file mode 100644 index 45e84219..00000000 --- a/features/adjustment/mint/submit-modal/submit-modal.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { FC, useMemo } from 'react'; -import { useRouter } from 'next/router'; -import { useFormContext } from 'react-hook-form'; - -import { - Loader, - Modal, - Text, - Success, - Error, - type ModalProps, - Button, -} from '@lidofinance/lido-ui'; - -import { useFormControllerContext } from 'shared/hook-form/form-controller'; -import { ButtonLink, TxLinkEtherscan } from 'shared/components'; -import { Content } from './styles'; - -import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; -import { AppPaths } from 'consts/urls'; -import { useVaultInfo } from 'features/overview/contexts'; -import { Address } from 'viem'; - -const getIconComponent = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.success: - return ; - case SubmitStepEnum.reject: - return ; - case SubmitStepEnum.error: - return ; - default: - return ; - } -}; - -const getModalTitle = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.success: - return 'StETH was minted successfully'; - case SubmitStepEnum.reject: - return 'Wallet tx signature'; - case SubmitStepEnum.error: - return 'Transaction error'; - default: - return 'You are minting to an address'; - } -}; - -const getModalSubTitle = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.submitting: - return 'Awaiting wallet signature'; - case SubmitStepEnum.reject: - return 'User denied transaction signature'; - case SubmitStepEnum.error: - return 'Got error when called contract simulation or transaction'; - default: - return ''; - } -}; - -interface SubmitModalProps extends ModalProps { - submitStep: { - step: SubmitStep; - tx?: Address; - }; - setModalState: ({ step }: { step: SubmitStep }) => void; -} - -export const SubmitModal: FC = ({ - submitStep, - setModalState, -}) => { - const router = useRouter(); - const { - formState: { isSubmitting, isSubmitted }, - } = useFormContext(); - const { retryFire } = useFormControllerContext(); - const { step, tx } = submitStep ?? {}; - const { activeVault } = useVaultInfo(); - - const iconComponent = useMemo(() => getIconComponent(step), [step]); - const title = getModalTitle(step); - const subtitle = getModalSubTitle(step); - - const handleNavigateToVault = () => { - void router.push(`/${activeVault?.address}/${AppPaths.overview}`); - }; - - const handleCloseModal = () => { - setModalState({ step: SubmitStepEnum.edit }); - }; - - return ( - - - {step === SubmitStepEnum.initiate && ( - - Wait for wallet confirmation window - - )} - - {step === SubmitStepEnum.confirming && ( - - Confirm this transaction in your wallet - - )} - - {step === SubmitStepEnum.success && ( - - )} - - {(SubmitStepEnum.success === step || - SubmitStepEnum.submitting === step) && - tx && } - - {step === SubmitStepEnum.reject && ( - retry - )} - - {step === SubmitStepEnum.error && ( - close - )} - - - ); -}; diff --git a/features/adjustment/repay/hooks/use-burn.ts b/features/adjustment/repay/hooks/use-burn.ts index 6ae36bc9..84d136f5 100644 --- a/features/adjustment/repay/hooks/use-burn.ts +++ b/features/adjustment/repay/hooks/use-burn.ts @@ -10,7 +10,10 @@ import { Address } from 'viem'; import { dashboardAbi } from 'abi/dashboard-abi'; import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; import invariant from 'tiny-invariant'; export const useBurn = (onMutate = () => {}) => { diff --git a/features/adjustment/repay/repay-form-context/repay-form-provider.tsx b/features/adjustment/repay/repay-form-context/repay-form-provider.tsx index 7955428b..ed8888c1 100644 --- a/features/adjustment/repay/repay-form-context/repay-form-provider.tsx +++ b/features/adjustment/repay/repay-form-context/repay-form-provider.tsx @@ -20,8 +20,8 @@ import { } from 'shared/hook-form/form-controller'; import { RepayFormSchema } from 'features/adjustment/repay/types'; -import { SubmitModal } from 'features/adjustment/repay/submit-modal'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { SubmitModal } from 'shared/components'; +import { SubmitStep, SubmitStepEnum } from 'shared/components/submit-modal'; import { Address } from 'viem'; type RepayDataContextValue = { diff --git a/features/adjustment/repay/submit-modal/index.ts b/features/adjustment/repay/submit-modal/index.ts deleted file mode 100644 index 1c0e3509..00000000 --- a/features/adjustment/repay/submit-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SubmitModal } from './submit-modal'; diff --git a/features/adjustment/repay/submit-modal/styles.ts b/features/adjustment/repay/submit-modal/styles.ts deleted file mode 100644 index dfca0ac4..00000000 --- a/features/adjustment/repay/submit-modal/styles.ts +++ /dev/null @@ -1,8 +0,0 @@ -import styled from 'styled-components'; - -export const Content = styled.div` - display: flex; - flex-direction: column; - gap: ${({ theme }) => theme.spaceMap.xl}px; - margin-top: ${({ theme }) => theme.spaceMap.sm}px; -`; diff --git a/features/claim/claim-form/claim-form-context/claim-form-provider.tsx b/features/claim/claim-form/claim-form-context/claim-form-provider.tsx index 601243db..19b36b60 100644 --- a/features/claim/claim-form/claim-form-context/claim-form-provider.tsx +++ b/features/claim/claim-form/claim-form-context/claim-form-provider.tsx @@ -24,7 +24,10 @@ import { import { dashboardAbi } from 'abi/dashboard-abi'; import { ClaimFormSchema } from 'features/claim/claim-form/types'; import invariant from 'tiny-invariant'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; import { SubmitModal } from '../submit-modal'; type ClaimDataContextValue = { diff --git a/features/claim/claim-form/hooks/use-claim.ts b/features/claim/claim-form/hooks/use-claim.ts index ed0d8164..c1108458 100644 --- a/features/claim/claim-form/hooks/use-claim.ts +++ b/features/claim/claim-form/hooks/use-claim.ts @@ -11,7 +11,10 @@ import { dashboardAbi } from 'abi/dashboard-abi'; import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; export const useClaim = (onMutate = () => {}) => { const { chainId } = useDappStatus(); diff --git a/features/claim/claim-form/submit-modal/submit-modal.tsx b/features/claim/claim-form/submit-modal/submit-modal.tsx index 62498365..def6ae51 100644 --- a/features/claim/claim-form/submit-modal/submit-modal.tsx +++ b/features/claim/claim-form/submit-modal/submit-modal.tsx @@ -16,7 +16,10 @@ import { useFormControllerContext } from 'shared/hook-form/form-controller'; import { ButtonLink, TxLinkEtherscan } from 'shared/components'; import { Content } from './styles'; -import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; +import { + SubmitStepEnum, + SubmitStep, +} from 'shared/components/submit-modal/types'; import { AppPaths } from 'consts/urls'; import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; diff --git a/features/supply/fund/fund-form-context/fund-form-provider.tsx b/features/supply/fund/fund-form-context/fund-form-provider.tsx index 17e702cd..b7884556 100644 --- a/features/supply/fund/fund-form-context/fund-form-provider.tsx +++ b/features/supply/fund/fund-form-context/fund-form-provider.tsx @@ -8,8 +8,11 @@ import { FormControllerContextValueType, } from 'shared/hook-form/form-controller'; import { FundFormSchema } from 'features/supply/fund/types'; -import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; -import { SubmitModal } from '../submit-modal'; +import { + SubmitStepEnum, + SubmitStep, +} from 'shared/components/submit-modal/types'; +import { SubmitModal } from 'shared/components'; import { Address } from 'viem'; export const FundFormProvider: FC<{ children: ReactNode }> = ({ children }) => { diff --git a/features/supply/fund/hooks/use-fund.ts b/features/supply/fund/hooks/use-fund.ts index 272a0a71..cb6bbad5 100644 --- a/features/supply/fund/hooks/use-fund.ts +++ b/features/supply/fund/hooks/use-fund.ts @@ -11,7 +11,10 @@ import { dashboardAbi } from 'abi/dashboard-abi'; import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; import { useVaultPermissions } from 'modules/vaults/hooks/use-vault-permissions'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; export const useFund = (onMutate = () => {}) => { const { activeVault } = useVaultInfo(); diff --git a/features/supply/fund/submit-modal/index.ts b/features/supply/fund/submit-modal/index.ts deleted file mode 100644 index 1c0e3509..00000000 --- a/features/supply/fund/submit-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SubmitModal } from './submit-modal'; diff --git a/features/supply/fund/submit-modal/styles.ts b/features/supply/fund/submit-modal/styles.ts deleted file mode 100644 index dfca0ac4..00000000 --- a/features/supply/fund/submit-modal/styles.ts +++ /dev/null @@ -1,8 +0,0 @@ -import styled from 'styled-components'; - -export const Content = styled.div` - display: flex; - flex-direction: column; - gap: ${({ theme }) => theme.spaceMap.xl}px; - margin-top: ${({ theme }) => theme.spaceMap.sm}px; -`; diff --git a/features/supply/fund/submit-modal/submit-modal.tsx b/features/supply/fund/submit-modal/submit-modal.tsx deleted file mode 100644 index f88aabcd..00000000 --- a/features/supply/fund/submit-modal/submit-modal.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { FC, useMemo } from 'react'; -import { useRouter } from 'next/router'; -import { useFormContext } from 'react-hook-form'; - -import { - Loader, - Modal, - Text, - Success, - Error, - type ModalProps, - Button, -} from '@lidofinance/lido-ui'; - -import { useFormControllerContext } from 'shared/hook-form/form-controller'; -import { ButtonLink, TxLinkEtherscan } from 'shared/components'; -import { Content } from './styles'; - -import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; -import { AppPaths } from 'consts/urls'; -import { useVaultInfo } from 'features/overview/contexts'; -import { Address } from 'viem'; - -const getIconComponent = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.success: - return ; - case SubmitStepEnum.reject: - return ; - case SubmitStepEnum.error: - return ; - default: - return ; - } -}; - -const getModalTitle = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.success: - return 'Vault has been funded'; - case SubmitStepEnum.reject: - return 'Wallet tx signature'; - case SubmitStepEnum.error: - return 'Transaction error'; - default: - return 'You are supplying to the vault'; - } -}; - -const getModalSubTitle = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.submitting: - return 'Awaiting wallet signature'; - case SubmitStepEnum.reject: - return 'User denied transaction signature'; - case SubmitStepEnum.error: - return 'Got error when called contract simulation or transaction'; - default: - return ''; - } -}; - -interface SubmitModalProps extends ModalProps { - submitStep: { - step: SubmitStep; - tx?: Address; - }; - setModalState: ({ step }: { step: SubmitStep }) => void; -} - -export const SubmitModal: FC = ({ - submitStep, - setModalState, -}) => { - const router = useRouter(); - const { - formState: { isSubmitting, isSubmitted }, - } = useFormContext(); - const { retryFire } = useFormControllerContext(); - const { step, tx } = submitStep ?? {}; - const { activeVault } = useVaultInfo(); - - const iconComponent = useMemo(() => getIconComponent(step), [step]); - const title = getModalTitle(step); - const subtitle = getModalSubTitle(step); - - const handleNavigateToVault = () => { - void router.push(`/${activeVault?.address}/${AppPaths.overview}`); - }; - - const handleCloseModal = () => { - setModalState({ step: SubmitStepEnum.edit }); - }; - - return ( - - - {step === SubmitStepEnum.initiate && ( - - Wait for wallet confirmation window - - )} - - {step === SubmitStepEnum.confirming && ( - - Confirm this transaction in your wallet - - )} - - {step === SubmitStepEnum.success && ( - - )} - - {(SubmitStepEnum.success === step || - SubmitStepEnum.submitting === step) && - tx && } - - {step === SubmitStepEnum.reject && ( - retry - )} - - {step === SubmitStepEnum.error && ( - close - )} - - - ); -}; diff --git a/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts b/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts index 22b70383..51a2f8bd 100644 --- a/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts +++ b/features/supply/withdraw/hooks/use-withdraw-with-dashboard.ts @@ -7,7 +7,10 @@ import { useDappStatus } from 'modules/web3/hooks/use-dapp-status'; import { useVaultInfo } from 'features/overview/contexts'; import invariant from 'tiny-invariant'; import { useVaultPermissions } from 'modules/vaults/hooks/use-vault-permissions'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; type WithdrawWithDashboardArgs = { recipient: Address; diff --git a/features/supply/withdraw/submit-modal/index.ts b/features/supply/withdraw/submit-modal/index.ts deleted file mode 100644 index 1c0e3509..00000000 --- a/features/supply/withdraw/submit-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SubmitModal } from './submit-modal'; diff --git a/features/supply/withdraw/submit-modal/styles.ts b/features/supply/withdraw/submit-modal/styles.ts deleted file mode 100644 index dfca0ac4..00000000 --- a/features/supply/withdraw/submit-modal/styles.ts +++ /dev/null @@ -1,8 +0,0 @@ -import styled from 'styled-components'; - -export const Content = styled.div` - display: flex; - flex-direction: column; - gap: ${({ theme }) => theme.spaceMap.xl}px; - margin-top: ${({ theme }) => theme.spaceMap.sm}px; -`; diff --git a/features/supply/withdraw/submit-modal/submit-modal.tsx b/features/supply/withdraw/submit-modal/submit-modal.tsx deleted file mode 100644 index 3173529c..00000000 --- a/features/supply/withdraw/submit-modal/submit-modal.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { FC, useMemo } from 'react'; -import { useRouter } from 'next/router'; -import { useFormContext } from 'react-hook-form'; - -import { - Loader, - Modal, - Text, - Success, - Error, - type ModalProps, - Button, -} from '@lidofinance/lido-ui'; - -import { useFormControllerContext } from 'shared/hook-form/form-controller'; -import { ButtonLink, TxLinkEtherscan } from 'shared/components'; -import { Content } from './styles'; - -import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; -import { AppPaths } from 'consts/urls'; -import { useVaultInfo } from 'features/overview/contexts'; -import { Address } from 'viem'; - -const getIconComponent = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.success: - return ; - case SubmitStepEnum.reject: - return ; - case SubmitStepEnum.error: - return ; - default: - return ; - } -}; - -const getModalTitle = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.success: - return 'Funds withdrawed'; - case SubmitStepEnum.reject: - return 'Wallet tx signature'; - case SubmitStepEnum.error: - return 'Transaction error'; - default: - return 'You are withdrawing to an address'; - } -}; - -const getModalSubTitle = (step: SubmitStep) => { - switch (step) { - case SubmitStepEnum.submitting: - return 'Awaiting wallet signature'; - case SubmitStepEnum.reject: - return 'User denied transaction signature'; - case SubmitStepEnum.error: - return 'Got error when called contract simulation or transaction'; - default: - return ''; - } -}; - -interface SubmitModalProps extends ModalProps { - submitStep: { - step: SubmitStep; - tx?: Address; - }; - setModalState: ({ step }: { step: SubmitStep }) => void; -} - -export const SubmitModal: FC = ({ - submitStep, - setModalState, -}) => { - const router = useRouter(); - const { - formState: { isSubmitting, isSubmitted }, - } = useFormContext(); - const { retryFire } = useFormControllerContext(); - const { step, tx } = submitStep ?? {}; - const { activeVault } = useVaultInfo(); - - const iconComponent = useMemo(() => getIconComponent(step), [step]); - const title = getModalTitle(step); - const subtitle = getModalSubTitle(step); - - const handleNavigateToVault = () => { - void router.push(`/${activeVault?.address}/${AppPaths.overview}`); - }; - - const handleCloseModal = () => { - setModalState({ step: SubmitStepEnum.edit }); - }; - - return ( - - - {step === SubmitStepEnum.initiate && ( - - Wait for wallet confirmation window - - )} - - {step === SubmitStepEnum.confirming && ( - - Confirm this transaction in your wallet - - )} - - {step === SubmitStepEnum.success && ( - - )} - - {(SubmitStepEnum.success === step || - SubmitStepEnum.submitting === step) && - tx && } - - {step === SubmitStepEnum.reject && ( - retry - )} - - {step === SubmitStepEnum.error && ( - close - )} - - - ); -}; diff --git a/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx b/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx index 16607d34..b9001a95 100644 --- a/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx +++ b/features/supply/withdraw/withdraw-form-context/withdraw-form-provider.tsx @@ -22,10 +22,13 @@ import { FormControllerContext, FormControllerContextValueType, } from 'shared/hook-form/form-controller'; -import { SubmitModal } from 'features/supply/withdraw/submit-modal'; +import { SubmitModal } from 'shared/components'; import { WithdrawFormSchema } from 'features/supply/withdraw/types'; -import { SubmitStep, SubmitStepEnum } from 'shared/transaction-modal/types'; +import { + SubmitStep, + SubmitStepEnum, +} from 'shared/components/submit-modal/types'; type WithdrawDataContextValue = { withdrawableAmount: bigint | undefined; diff --git a/shared/components/index.ts b/shared/components/index.ts index 0cb66663..80b70f0f 100644 --- a/shared/components/index.ts +++ b/shared/components/index.ts @@ -16,3 +16,4 @@ export { VaultImpactDashboard } from './vault-impact-dashboard'; export { VaultImpactValuation } from './vault-impact-valuation'; export { InputAmount } from './input-amount'; export { Chip } from './chip'; +export { SubmitModal } from './submit-modal'; diff --git a/features/adjustment/mint/submit-modal/index.ts b/shared/components/submit-modal/index.ts similarity index 64% rename from features/adjustment/mint/submit-modal/index.ts rename to shared/components/submit-modal/index.ts index 1c0e3509..09ec63ec 100644 --- a/features/adjustment/mint/submit-modal/index.ts +++ b/shared/components/submit-modal/index.ts @@ -1 +1,2 @@ export { SubmitModal } from './submit-modal'; +export * from './types'; diff --git a/features/adjustment/mint/submit-modal/styles.ts b/shared/components/submit-modal/styles.ts similarity index 100% rename from features/adjustment/mint/submit-modal/styles.ts rename to shared/components/submit-modal/styles.ts diff --git a/features/adjustment/repay/submit-modal/submit-modal.tsx b/shared/components/submit-modal/submit-modal.tsx similarity index 93% rename from features/adjustment/repay/submit-modal/submit-modal.tsx rename to shared/components/submit-modal/submit-modal.tsx index ac3f10ae..7af12ec7 100644 --- a/features/adjustment/repay/submit-modal/submit-modal.tsx +++ b/shared/components/submit-modal/submit-modal.tsx @@ -16,7 +16,10 @@ import { useFormControllerContext } from 'shared/hook-form/form-controller'; import { ButtonLink, TxLinkEtherscan } from 'shared/components'; import { Content } from './styles'; -import { SubmitStepEnum, SubmitStep } from 'shared/transaction-modal/types'; +import { + SubmitStepEnum, + SubmitStep, +} from 'shared/components/submit-modal/types'; import { AppPaths } from 'consts/urls'; import { useVaultInfo } from 'features/overview/contexts'; import { Address } from 'viem'; @@ -37,20 +40,22 @@ const getIconComponent = (step: SubmitStep) => { const getModalTitle = (step: SubmitStep) => { switch (step) { case SubmitStepEnum.success: - return 'Repay was successfully'; + return 'Transaction was finished successfully'; case SubmitStepEnum.reject: return 'Wallet tx signature'; case SubmitStepEnum.error: return 'Transaction error'; default: - return 'You are repaying to an address'; + return 'Transaction'; } }; const getModalSubTitle = (step: SubmitStep) => { switch (step) { - case SubmitStepEnum.submitting: + case SubmitStepEnum.confirming: return 'Awaiting wallet signature'; + case SubmitStepEnum.submitting: + return 'Awaiting network response'; case SubmitStepEnum.reject: return 'User denied transaction signature'; case SubmitStepEnum.error: diff --git a/shared/transaction-modal/types.ts b/shared/components/submit-modal/types.ts similarity index 100% rename from shared/transaction-modal/types.ts rename to shared/components/submit-modal/types.ts From a3b8c6ce1b6b7ff539b4bc02ca5157b7dc6a460f Mon Sep 17 00:00:00 2001 From: Dmitriy Evtifeev Date: Fri, 25 Apr 2025 14:43:30 +0300 Subject: [PATCH 9/9] fix: update text in submit tx modal --- shared/components/submit-modal/submit-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/components/submit-modal/submit-modal.tsx b/shared/components/submit-modal/submit-modal.tsx index 7af12ec7..3c201f74 100644 --- a/shared/components/submit-modal/submit-modal.tsx +++ b/shared/components/submit-modal/submit-modal.tsx @@ -55,7 +55,7 @@ const getModalSubTitle = (step: SubmitStep) => { case SubmitStepEnum.confirming: return 'Awaiting wallet signature'; case SubmitStepEnum.submitting: - return 'Awaiting network response'; + return 'Awaiting block confirmation'; case SubmitStepEnum.reject: return 'User denied transaction signature'; case SubmitStepEnum.error: