diff --git a/src/components/common/ConnectWallet/MPCLogin.tsx b/src/components/common/ConnectWallet/MPCLogin.tsx
index 687d3446dd..32d50e47bd 100644
--- a/src/components/common/ConnectWallet/MPCLogin.tsx
+++ b/src/components/common/ConnectWallet/MPCLogin.tsx
@@ -1,6 +1,6 @@
import { MPCWalletState } from '@/hooks/wallets/mpc/useMPCWallet'
import { Box, Button, SvgIcon, Typography } from '@mui/material'
-import { useContext, useMemo } from 'react'
+import { useCallback, useContext, useMemo } from 'react'
import { MpcWalletContext } from './MPCWalletProvider'
import { PasswordRecovery } from './PasswordRecovery'
import GoogleLogo from '@/public/images/welcome/logo-google.svg'
@@ -16,6 +16,8 @@ import { isSocialWalletEnabled } from '@/hooks/wallets/wallets'
import { isSocialLoginWallet } from '@/services/mpc/module'
import { CGW_NAMES } from '@/hooks/wallets/consts'
import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
+import { TxModalContext } from '@/components/tx-flow'
+import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
export const _getSupportedChains = (chains: ChainInfo[]) => {
return chains
@@ -37,7 +39,9 @@ const useIsSocialWalletEnabled = () => {
}
const MPCLogin = ({ onLogin }: { onLogin?: () => void }) => {
- const { triggerLogin, userInfo, walletState, recoverFactorWithPassword } = useContext(MpcWalletContext)
+ const { triggerLogin, userInfo, walletState, setWalletState, recoverFactorWithPassword } =
+ useContext(MpcWalletContext)
+ const { setTxFlow } = useContext(TxModalContext)
const wallet = useWallet()
const loginPending = walletState === MPCWalletState.AUTHENTICATING
@@ -48,21 +52,33 @@ const MPCLogin = ({ onLogin }: { onLogin?: () => void }) => {
const isDisabled = loginPending || !isMPCLoginEnabled
const login = async () => {
- const success = await triggerLogin()
+ const status = await triggerLogin()
- if (success) {
+ if (status === COREKIT_STATUS.LOGGED_IN) {
onLogin?.()
}
- }
-
- const recoverPassword = async (password: string, storeDeviceFactor: boolean) => {
- const success = await recoverFactorWithPassword(password, storeDeviceFactor)
- if (success) {
- onLogin?.()
+ if (status === COREKIT_STATUS.REQUIRED_SHARE) {
+ setTxFlow(
+ ,
+ () => setWalletState(MPCWalletState.NOT_INITIALIZED),
+ false,
+ )
}
}
+ const recoverPassword = useCallback(
+ async (password: string, storeDeviceFactor: boolean) => {
+ const success = await recoverFactorWithPassword(password, storeDeviceFactor)
+
+ if (success) {
+ onLogin?.()
+ setTxFlow(undefined)
+ }
+ },
+ [onLogin, recoverFactorWithPassword, setTxFlow],
+ )
+
const isSocialLogin = isSocialLoginWallet(wallet?.label)
return (
@@ -128,10 +144,6 @@ const MPCLogin = ({ onLogin }: { onLogin?: () => void }) => {
Currently only supported on {supportedChains.join(', ')}
)}
-
- {walletState === MPCWalletState.MANUAL_RECOVERY && (
-
- )}
>
)
}
diff --git a/src/components/common/ConnectWallet/MPCWalletProvider.tsx b/src/components/common/ConnectWallet/MPCWalletProvider.tsx
index 06162aab82..27872ceddc 100644
--- a/src/components/common/ConnectWallet/MPCWalletProvider.tsx
+++ b/src/components/common/ConnectWallet/MPCWalletProvider.tsx
@@ -1,9 +1,11 @@
-import { useMPCWallet, MPCWalletState, type MPCWalletHook } from '@/hooks/wallets/mpc/useMPCWallet'
+import { type MPCWalletHook, MPCWalletState, useMPCWallet } from '@/hooks/wallets/mpc/useMPCWallet'
import { createContext, type ReactElement } from 'react'
+import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
export const MpcWalletContext = createContext({
walletState: MPCWalletState.NOT_INITIALIZED,
- triggerLogin: () => Promise.resolve(false),
+ setWalletState: () => {},
+ triggerLogin: () => Promise.resolve(COREKIT_STATUS.NOT_INITIALIZED),
resetAccount: () => Promise.resolve(),
upsertPasswordBackup: () => Promise.resolve(),
recoverFactorWithPassword: () => Promise.resolve(false),
diff --git a/src/components/common/ConnectWallet/PasswordRecovery.tsx b/src/components/common/ConnectWallet/PasswordRecovery.tsx
index 72ed86a3ac..053a533a27 100644
--- a/src/components/common/ConnectWallet/PasswordRecovery.tsx
+++ b/src/components/common/ConnectWallet/PasswordRecovery.tsx
@@ -1,70 +1,106 @@
import { MPC_WALLET_EVENTS } from '@/services/analytics/events/mpcWallet'
-import { VisibilityOff, Visibility } from '@mui/icons-material'
import {
- DialogContent,
Typography,
- TextField,
- IconButton,
FormControlLabel,
Checkbox,
Button,
Box,
+ Divider,
+ Grid,
+ LinearProgress,
+ FormControl,
} from '@mui/material'
import { useState } from 'react'
-import ModalDialog from '../ModalDialog'
import Track from '../Track'
+import { FormProvider, useForm } from 'react-hook-form'
+import PasswordInput from '@/components/settings/SignerAccountMFA/PasswordInput'
+
+type PasswordFormData = {
+ password: string
+}
export const PasswordRecovery = ({
recoverFactorWithPassword,
+ onSuccess,
}: {
recoverFactorWithPassword: (password: string, storeDeviceFactor: boolean) => Promise
+ onSuccess: (() => void) | undefined
}) => {
- const [showPassword, setShowPassword] = useState(false)
- const [recoveryPassword, setRecoveryPassword] = useState('')
const [storeDeviceFactor, setStoreDeviceFactor] = useState(false)
+
+ const formMethods = useForm({
+ mode: 'all',
+ defaultValues: {
+ password: '',
+ },
+ })
+
+ const { handleSubmit, formState, setError } = formMethods
+
+ const onSubmit = async (data: PasswordFormData) => {
+ try {
+ await recoverFactorWithPassword(data.password, storeDeviceFactor)
+ onSuccess?.()
+ } catch (e) {
+ setError('password', { type: 'custom', message: 'Incorrect password' })
+ }
+ }
+
+ const isDisabled = formState.isSubmitting
+
return (
-
-
-
-
- This browser is not registered with your Account yet. Please enter your recovery password to restore access
- to this Account.
-
-
- {
- setRecoveryPassword(event.target.value)
- }}
- InputProps={{
- endAdornment: (
- setShowPassword((prev) => !prev)}
- edge="end"
- >
- {showPassword ? : }
-
- ),
- }}
- />
- setStoreDeviceFactor((prev) => !prev)} />}
- label="Do not ask again on this device"
- />
-
-
-
-
-
+
+
+
)
}
diff --git a/src/components/common/ConnectWallet/__tests__/MPCLogin.test.tsx b/src/components/common/ConnectWallet/__tests__/MPCLogin.test.tsx
index 8c33d79441..249e8da820 100644
--- a/src/components/common/ConnectWallet/__tests__/MPCLogin.test.tsx
+++ b/src/components/common/ConnectWallet/__tests__/MPCLogin.test.tsx
@@ -9,6 +9,7 @@ import { type EIP1193Provider } from '@web3-onboard/common'
import { ONBOARD_MPC_MODULE_LABEL } from '@/services/mpc/module'
import { MpcWalletProvider } from '../MPCWalletProvider'
import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
+import { COREKIT_STATUS } from '@web3auth/mpc-core-kit'
describe('MPCLogin', () => {
beforeEach(() => {
@@ -62,7 +63,7 @@ describe('MPCLogin', () => {
.spyOn(chains, 'useCurrentChain')
.mockReturnValue({ chainId: '100', disabledWallets: [] } as unknown as ChainInfo)
jest.spyOn(useWallet, 'default').mockReturnValue(null)
- const mockTriggerLogin = jest.fn(() => true)
+ const mockTriggerLogin = jest.fn(() => COREKIT_STATUS.LOGGED_IN)
jest.spyOn(useMPCWallet, 'useMPCWallet').mockReturnValue({
triggerLogin: mockTriggerLogin,
} as unknown as useMPCWallet.MPCWalletHook)
diff --git a/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx b/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx
index 1fbbd76d89..34041dbdfb 100644
--- a/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx
+++ b/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx
@@ -71,7 +71,7 @@ const SrcEthHashInfo = ({
{name && (
-
+
{name}
)}
diff --git a/src/components/tx-flow/index.tsx b/src/components/tx-flow/index.tsx
index 0cf7a993aa..0f4471b5de 100644
--- a/src/components/tx-flow/index.tsx
+++ b/src/components/tx-flow/index.tsx
@@ -17,6 +17,7 @@ export const TxModalContext = createContext({
setFullWidth: noop,
})
+// TODO: Rename TxModalProvider, setTxFlow, TxModalDialog to not contain Tx since it can be used for any type of modal as a global provider
export const TxModalProvider = ({ children }: { children: ReactNode }): ReactElement => {
const [txFlow, setFlow] = useState(undefined)
const [shouldWarn, setShouldWarn] = useState(true)
diff --git a/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts b/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts
index 6a134ea0b7..53a0b60985 100644
--- a/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts
+++ b/src/hooks/wallets/mpc/__tests__/useMPCWallet.test.ts
@@ -113,8 +113,9 @@ describe('useMPCWallet', () => {
)
const { result } = renderHook(() => useMPCWallet())
+ let status: Promise
act(() => {
- result.current.triggerLogin()
+ status = result.current.triggerLogin()
})
// While the login resolves we are in Authenticating state
@@ -128,6 +129,7 @@ describe('useMPCWallet', () => {
// We should be logged in and onboard should get connected
await waitFor(() => {
+ expect(status).resolves.toEqual(COREKIT_STATUS.LOGGED_IN)
expect(result.current.walletState === MPCWalletState.READY)
expect(connectWalletSpy).toBeCalledWith(expect.anything(), {
autoSelect: {
@@ -158,8 +160,9 @@ describe('useMPCWallet', () => {
const { result } = renderHook(() => useMPCWallet())
+ let status: Promise
act(() => {
- result.current.triggerLogin()
+ status = result.current.triggerLogin()
})
// While the login resolves we are in Authenticating state
@@ -173,6 +176,7 @@ describe('useMPCWallet', () => {
// We should be logged in and onboard should get connected
await waitFor(() => {
+ expect(status).resolves.toEqual(COREKIT_STATUS.LOGGED_IN)
expect(result.current.walletState === MPCWalletState.READY)
expect(connectWalletSpy).toBeCalledWith(expect.anything(), {
autoSelect: {
@@ -201,8 +205,9 @@ describe('useMPCWallet', () => {
const { result } = renderHook(() => useMPCWallet())
+ let status: Promise
act(() => {
- result.current.triggerLogin()
+ status = result.current.triggerLogin()
})
// While the login resolves we are in Authenticating state
@@ -216,6 +221,7 @@ describe('useMPCWallet', () => {
// A missing second factor should result in manual recovery state
await waitFor(() => {
+ expect(status).resolves.toEqual(COREKIT_STATUS.REQUIRED_SHARE)
expect(result.current.walletState === MPCWalletState.MANUAL_RECOVERY)
expect(connectWalletSpy).not.toBeCalled()
})
diff --git a/src/hooks/wallets/mpc/useMPCWallet.ts b/src/hooks/wallets/mpc/useMPCWallet.ts
index 753b199a48..06328e4e82 100644
--- a/src/hooks/wallets/mpc/useMPCWallet.ts
+++ b/src/hooks/wallets/mpc/useMPCWallet.ts
@@ -1,4 +1,4 @@
-import { useState } from 'react'
+import { type Dispatch, type SetStateAction, useState } from 'react'
import useMPC from './useMPC'
import BN from 'bn.js'
import { GOOGLE_CLIENT_ID, WEB3AUTH_VERIFIER_ID } from '@/config/constants'
@@ -21,7 +21,8 @@ export type MPCWalletHook = {
upsertPasswordBackup: (password: string) => Promise
recoverFactorWithPassword: (password: string, storeDeviceShare: boolean) => Promise
walletState: MPCWalletState
- triggerLogin: () => Promise
+ setWalletState: Dispatch>
+ triggerLogin: () => Promise
resetAccount: () => Promise
userInfo: UserInfo | undefined
exportPk: (password: string) => Promise
@@ -78,17 +79,17 @@ export const useMPCWallet = (): MPCWalletHook => {
if (securityQuestions.isEnabled()) {
trackEvent(MPC_WALLET_EVENTS.MANUAL_RECOVERY)
setWalletState(MPCWalletState.MANUAL_RECOVERY)
- return false
+ return mpcCoreKit.status
}
}
}
await finalizeLogin()
- return mpcCoreKit.status === COREKIT_STATUS.LOGGED_IN
+ return mpcCoreKit.status
} catch (error) {
setWalletState(MPCWalletState.NOT_INITIALIZED)
console.error(error)
- return false
+ return mpcCoreKit.status
}
}
@@ -154,6 +155,7 @@ export const useMPCWallet = (): MPCWalletHook => {
return {
triggerLogin,
walletState,
+ setWalletState,
recoverFactorWithPassword,
resetAccount: criticalResetAccount,
upsertPasswordBackup: () => Promise.resolve(),