From d59a412450ab0c8f70763175cd93484277d5ab98 Mon Sep 17 00:00:00 2001 From: Jiri Zbytovsky Date: Fri, 10 Oct 2025 09:11:50 +0200 Subject: [PATCH 1/2] refactor(suite-native): cleanup entropy check thunks --- suite-native/device/src/deviceThunks.ts | 36 +++++++------- .../module-device-onboarding/package.json | 1 + .../src/screens/WalletCreationScreen.tsx | 48 ++++++++++++++----- .../module-device-onboarding/tsconfig.json | 3 ++ yarn.lock | 1 + 5 files changed, 58 insertions(+), 31 deletions(-) diff --git a/suite-native/device/src/deviceThunks.ts b/suite-native/device/src/deviceThunks.ts index fef332750b74..d96851da5efa 100644 --- a/suite-native/device/src/deviceThunks.ts +++ b/suite-native/device/src/deviceThunks.ts @@ -9,8 +9,7 @@ import { selectSelectedDevice, } from '@suite-common/wallet-core'; import { requestPrioritizedDeviceAccess } from '@suite-native/device-mutex'; -import { RootStackRoutes, navigationContainerRef } from '@suite-native/navigation'; -import TrezorConnect, { PROTO } from '@trezor/connect'; +import TrezorConnect, { PROTO, SuccessWithDevice, Unsuccessful } from '@trezor/connect'; import { exhaustive } from '@trezor/type-utils'; const NATIVE_DEVICE_MODULE_PREFIX = 'nativeDevice'; @@ -63,9 +62,13 @@ const getResetDeviceConfig = (walletBackupType: BackupType): PROTO.ResetDevice = } }; -export const createAndBackupWalletThunk = createThunk( +export const createAndBackupWalletThunk = createThunk< + Unsuccessful | SuccessWithDevice, + { walletBackupType: BackupType }, + { rejectValue: string } +>( `${NATIVE_DEVICE_MODULE_PREFIX}/createAndBackupWalletThunk`, - async ({ walletBackupType }: { walletBackupType: BackupType }, { getState, dispatch }) => { + async ({ walletBackupType }, { getState, dispatch, fulfillWithValue, rejectWithValue }) => { const device = selectSelectedDevice(getState()); const devicePath = selectDevicePath(getState()); const isDeviceInitialized = selectIsDeviceInitialized(getState()); @@ -76,7 +79,7 @@ export const createAndBackupWalletThunk = createThunk( ); if (!device || !device.features || !devicePath) { - throw new Error('Device not found'); + return rejectWithValue('Device not found'); } // If the device already has a seed, backup is created. @@ -95,17 +98,14 @@ export const createAndBackupWalletThunk = createThunk( deviceCallback: () => TrezorConnect.backupDevice({ ...backupParams, - device: { - path: device.path, - }, + device: { path: device.path }, }), }); + // error from device-mutex + if (!deviceResponse.success) return rejectWithValue(deviceResponse.error); - if (!deviceResponse.success) { - throw new Error(deviceResponse.error); - } - - return deviceResponse.payload; + // full response from connect, which is either success or error + return fulfillWithValue(deviceResponse.payload); } const deviceResponse = await requestPrioritizedDeviceAccess({ @@ -118,16 +118,14 @@ export const createAndBackupWalletThunk = createThunk( entropy_check: isEntropyCheckEnabled, }), }); + // error from device-mutex + if (!deviceResponse.success) return rejectWithValue(deviceResponse.error); - if (!deviceResponse.success) { - throw new Error(deviceResponse.error); - } - + // full response from connect, which is either success or error const result = deviceResponse.payload; if (isEntropyCheckEnabled) { if (!result.success && result.payload.code === 'Failure_EntropyCheck') { dispatch(failEntropyCheckThunk({ device, error: result.payload })); - navigationContainerRef.navigate(RootStackRoutes.DeviceCompromisedModal); } else { dispatch( deviceActions.setEntropyCheckResult({ deviceId: device.id, success: true }), @@ -135,7 +133,7 @@ export const createAndBackupWalletThunk = createThunk( } } - return result; + return fulfillWithValue(result); }, ); diff --git a/suite-native/module-device-onboarding/package.json b/suite-native/module-device-onboarding/package.json index 315966622b8c..615d8de195ed 100644 --- a/suite-native/module-device-onboarding/package.json +++ b/suite-native/module-device-onboarding/package.json @@ -17,6 +17,7 @@ "@react-navigation/native-stack": "7.3.25", "@reduxjs/toolkit": "2.8.2", "@shopify/react-native-skia": "2.2.3", + "@suite-common/message-system": "workspace:*", "@suite-common/suite-constants": "workspace:*", "@suite-common/suite-types": "workspace:*", "@suite-common/thp": "workspace:*", diff --git a/suite-native/module-device-onboarding/src/screens/WalletCreationScreen.tsx b/suite-native/module-device-onboarding/src/screens/WalletCreationScreen.tsx index 4800c39088ac..fe45d86175bf 100644 --- a/suite-native/module-device-onboarding/src/screens/WalletCreationScreen.tsx +++ b/suite-native/module-device-onboarding/src/screens/WalletCreationScreen.tsx @@ -1,13 +1,21 @@ import { useCallback, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; import { isFulfilled } from '@reduxjs/toolkit'; +import { + Feature, + MessageSystemRootState, + selectIsFeatureEnabled, +} from '@suite-common/message-system'; import { createAndBackupWalletThunk } from '@suite-native/device'; import { DeviceOnboardingStackParamList, DeviceOnboardingStackRoutes, - StackProps, + RootStackParamList, + RootStackRoutes, + StackToStackCompositeNavigationProps, } from '@suite-native/navigation'; import { ERRORS } from '@trezor/connect'; @@ -22,12 +30,26 @@ const DEFINITIVE_ERRORS: ERRORS.ErrorCode[] = [ 'Failure_EntropyCheck', ]; -export const WalletCreationScreen = ({ - navigation, - route, -}: StackProps) => { +type NavigationProp = StackToStackCompositeNavigationProps< + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes.WalletCreation, + RootStackParamList +>; + +type RouteProps = RouteProp< + DeviceOnboardingStackParamList, + DeviceOnboardingStackRoutes.WalletCreation +>; + +export const WalletCreationScreen = () => { + const route = useRoute(); const { walletBackupType } = route.params; const dispatch = useDispatch(); + const navigation = useNavigation(); + + const isEntropyCheckEnabled = useSelector((state: MessageSystemRootState) => + selectIsFeatureEnabled(state, Feature.entropyCheckMobile, true), + ); const handleCreateAndBackupWallet = useCallback(async () => { const response = await dispatch(createAndBackupWalletThunk({ walletBackupType })); @@ -40,16 +62,18 @@ export const WalletCreationScreen = ({ flowType: 'create', }); } - if ( - responsePayload.payload.code && - DEFINITIVE_ERRORS.includes(responsePayload.payload.code) - ) { + const { code } = responsePayload.payload; + if (code && DEFINITIVE_ERRORS.includes(code)) { + if (isEntropyCheckEnabled && code === 'Failure_EntropyCheck') { + navigation.navigate(RootStackRoutes.DeviceCompromisedModal); + } + return; } } - + // repeat the attempt if error was not one of the DEFINITIVE_ERRORS handleCreateAndBackupWallet(); - }, [dispatch, walletBackupType, navigation]); + }, [dispatch, walletBackupType, navigation, isEntropyCheckEnabled]); useEffect(() => { handleCreateAndBackupWallet(); diff --git a/suite-native/module-device-onboarding/tsconfig.json b/suite-native/module-device-onboarding/tsconfig.json index a82b62fb2268..749fbfb17d77 100644 --- a/suite-native/module-device-onboarding/tsconfig.json +++ b/suite-native/module-device-onboarding/tsconfig.json @@ -2,6 +2,9 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "libDev" }, "references": [ + { + "path": "../../suite-common/message-system" + }, { "path": "../../suite-common/suite-constants" }, diff --git a/yarn.lock b/yarn.lock index f53dd126cae3..f704eedd3d04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11982,6 +11982,7 @@ __metadata: "@react-navigation/native-stack": "npm:7.3.25" "@reduxjs/toolkit": "npm:2.8.2" "@shopify/react-native-skia": "npm:2.2.3" + "@suite-common/message-system": "workspace:*" "@suite-common/suite-constants": "workspace:*" "@suite-common/suite-types": "workspace:*" "@suite-common/thp": "workspace:*" From f4eaba4351e995ca9eab24a815339c7610314c4d Mon Sep 17 00:00:00 2001 From: Jiri Zbytovsky Date: Fri, 10 Oct 2025 10:07:48 +0200 Subject: [PATCH 2/2] refactor(wallet-core): unify entropy check handling --- .../actions/settings/deviceSettingsActions.ts | 19 ++--------------- .../wallet-core/src/device/deviceThunks.ts | 21 ++++++++++++++++++- suite-native/device/src/deviceThunks.ts | 10 ++------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/suite/src/actions/settings/deviceSettingsActions.ts b/packages/suite/src/actions/settings/deviceSettingsActions.ts index 2b903b75740c..0903582a94c3 100644 --- a/packages/suite/src/actions/settings/deviceSettingsActions.ts +++ b/packages/suite/src/actions/settings/deviceSettingsActions.ts @@ -2,11 +2,7 @@ import { FIRMWARE_MODULE_PREFIX } from '@suite-common/firmware'; import { Feature, selectIsFeatureDisabled } from '@suite-common/message-system'; import { createThunk } from '@suite-common/redux-utils'; import { notificationsActions } from '@suite-common/toast-notifications'; -import { - deviceActions, - failEntropyCheckThunk, - selectSelectedDevice, -} from '@suite-common/wallet-core'; +import { processEntropyCheckResultThunk, selectSelectedDevice } from '@suite-common/wallet-core'; import TrezorConnect, { ERRORS } from '@trezor/connect'; import * as modalActions from 'src/actions/suite/modalActions'; @@ -164,18 +160,7 @@ export const resetDevice = }); if (isEntropyCheckEnabled) { - if (!result.success) { - dispatch( - notificationsActions.addToast({ type: 'error', error: result.payload.error }), - ); - if (result.payload.code === 'Failure_EntropyCheck') { - dispatch(failEntropyCheckThunk({ device, error: result.payload })); - } - } else { - dispatch( - deviceActions.setEntropyCheckResult({ deviceId: device.id, success: true }), - ); - } + dispatch(processEntropyCheckResultThunk({ device, result })); } return result; diff --git a/suite-common/wallet-core/src/device/deviceThunks.ts b/suite-common/wallet-core/src/device/deviceThunks.ts index 0baa73d59215..f163f73b1708 100644 --- a/suite-common/wallet-core/src/device/deviceThunks.ts +++ b/suite-common/wallet-core/src/device/deviceThunks.ts @@ -557,7 +557,7 @@ type FailEntropyCheckParams = { error: { code?: string; error: string }; }; -export const failEntropyCheckThunk = createThunk( +const failEntropyCheckThunk = createThunk( `${DEVICE_MODULE_PREFIX}/failEntropyCheckThunk`, ({ device, error }: FailEntropyCheckParams, { dispatch, extra }) => { const contextData = { @@ -586,3 +586,22 @@ export const failEntropyCheckThunk = createThunk( } }, ); + +type ProcessEntropyCheckResultThunkParams = { + device: AcquiredDevice; + result: Awaited>; +}; + +export const processEntropyCheckResultThunk = createThunk( + `${DEVICE_MODULE_PREFIX}/processEntropyCheckResultThunk`, + ({ device, result }: ProcessEntropyCheckResultThunkParams, { dispatch }) => { + if (result.success) { + dispatch(deviceActions.setEntropyCheckResult({ deviceId: device.id, success: true })); + } else { + dispatch(notificationsActions.addToast({ type: 'error', error: result.payload.error })); + if (result.payload.code === 'Failure_EntropyCheck') { + dispatch(failEntropyCheckThunk({ device, error: result.payload })); + } + } + }, +); diff --git a/suite-native/device/src/deviceThunks.ts b/suite-native/device/src/deviceThunks.ts index d96851da5efa..732b1c3c248a 100644 --- a/suite-native/device/src/deviceThunks.ts +++ b/suite-native/device/src/deviceThunks.ts @@ -3,7 +3,7 @@ import { createThunk } from '@suite-common/redux-utils'; import { BackupType } from '@suite-common/suite-types'; import { deviceActions, - failEntropyCheckThunk, + processEntropyCheckResultThunk, selectDevicePath, selectIsDeviceInitialized, selectSelectedDevice, @@ -124,13 +124,7 @@ export const createAndBackupWalletThunk = createThunk< // full response from connect, which is either success or error const result = deviceResponse.payload; if (isEntropyCheckEnabled) { - if (!result.success && result.payload.code === 'Failure_EntropyCheck') { - dispatch(failEntropyCheckThunk({ device, error: result.payload })); - } else { - dispatch( - deviceActions.setEntropyCheckResult({ deviceId: device.id, success: true }), - ); - } + dispatch(processEntropyCheckResultThunk({ device, result })); } return fulfillWithValue(result);