From 72d9f5ca2e8f00ba79054b774675ddead32f34e8 Mon Sep 17 00:00:00 2001 From: juriczech Date: Tue, 2 Dec 2025 14:13:52 +0000 Subject: [PATCH] chore(suite-native): portfolio tracker evolu unavailable --- .../wallet-core/src/device/deviceConstants.ts | 4 +- suite-native/state/src/reducers.ts | 11 +- suite-native/storage/jest.config.js | 11 ++ suite-native/storage/src/index.ts | 1 + .../storage/src/migrations/device/v4.ts | 19 +++ .../src/tests/migrations/deviceV4.test.ts | 156 ++++++++++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 suite-native/storage/jest.config.js create mode 100644 suite-native/storage/src/migrations/device/v4.ts create mode 100644 suite-native/storage/src/tests/migrations/deviceV4.test.ts diff --git a/suite-common/wallet-core/src/device/deviceConstants.ts b/suite-common/wallet-core/src/device/deviceConstants.ts index 772efa4a9a..2c63fde3ce 100644 --- a/suite-common/wallet-core/src/device/deviceConstants.ts +++ b/suite-common/wallet-core/src/device/deviceConstants.ts @@ -70,7 +70,9 @@ export const portfolioTrackerDevice: TrezorDevice = { buttonRequests: [], metadata: {}, passwords: {}, - unavailableCapabilities: {}, + unavailableCapabilities: { + evolu: 'no-support', + }, availableTranslations: {}, remember: true, authenticityChecks: { diff --git a/suite-native/state/src/reducers.ts b/suite-native/state/src/reducers.ts index 72c93418bf..4e53998cc7 100644 --- a/suite-native/state/src/reducers.ts +++ b/suite-native/state/src/reducers.ts @@ -41,6 +41,7 @@ import { localePersistWhitelist, localeReducer } from '@suite-native/intl'; import { appSettingsPersistWhitelist, appSettingsReducer } from '@suite-native/settings'; import { backfillDeviceAuthenticityChecks, + backfillPortfolioTrackerUnavailableCapabilities, bluetoothPersistTransform, deriveAccountTypeFromPaymentType, devicePersistTransform, @@ -184,7 +185,7 @@ export const prepareRootReducers = async () => { reducer: deviceReducer, persistedKeys: ['devices', 'persistentDeviceData'], key: 'devices', - version: 3, + version: 4, transforms: [devicePersistTransform], migrations: { 2: (oldState: any /* FIXME */) => { @@ -200,6 +201,14 @@ export const prepareRootReducers = async () => { if (!oldState?.devices) return oldState; const migratedDevices = backfillDeviceAuthenticityChecks(oldState.devices); + return { ...oldState, devices: migratedDevices }; + }, + 4: (oldState: any /* FIXME */) => { + if (!oldState?.devices) return oldState; + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities( + oldState.devices, + ); + return { ...oldState, devices: migratedDevices }; }, }, diff --git a/suite-native/storage/jest.config.js b/suite-native/storage/jest.config.js new file mode 100644 index 0000000000..3a43f58155 --- /dev/null +++ b/suite-native/storage/jest.config.js @@ -0,0 +1,11 @@ +/** + * Jest configuration for native packages. + * Keeping this file next to the package.json file instead of providing configuration + * with `-c ../../jest.config.native` option in package.json scripts + * allows us to run jest tests directly from IDEs. + */ +const baseConfig = require('../../jest.config.native'); + +module.exports = { + ...baseConfig, +}; diff --git a/suite-native/storage/src/index.ts b/suite-native/storage/src/index.ts index 3c812a0710..30f36b1ea0 100644 --- a/suite-native/storage/src/index.ts +++ b/suite-native/storage/src/index.ts @@ -8,6 +8,7 @@ export * from './migrations/account/v2'; export * from './migrations/account/v3'; export * from './migrations/device/v2'; export * from './migrations/device/v3'; +export * from './migrations/device/v4'; export * from './migrations/wallet/transactions/v2'; export * from './migrations/wallet/accounts/v2'; export * from './migrations/wallet/accounts/v3'; diff --git a/suite-native/storage/src/migrations/device/v4.ts b/suite-native/storage/src/migrations/device/v4.ts new file mode 100644 index 0000000000..cdee7dd280 --- /dev/null +++ b/suite-native/storage/src/migrations/device/v4.ts @@ -0,0 +1,19 @@ +import { TrezorDevice } from '@suite-common/suite-types'; +import { PORTFOLIO_TRACKER_DEVICE_ID } from '@suite-common/wallet-core'; + +export const backfillPortfolioTrackerUnavailableCapabilities = ( + devices: TrezorDevice[], +): TrezorDevice[] => + devices?.map(device => { + if (device.id !== PORTFOLIO_TRACKER_DEVICE_ID) return device; + + return { + ...device, + unavailableCapabilities: { + ...device.unavailableCapabilities, + ...(device.unavailableCapabilities?.evolu === undefined + ? { evolu: 'no-support' as const } + : {}), + }, + }; + }); diff --git a/suite-native/storage/src/tests/migrations/deviceV4.test.ts b/suite-native/storage/src/tests/migrations/deviceV4.test.ts new file mode 100644 index 0000000000..e87dcb7be7 --- /dev/null +++ b/suite-native/storage/src/tests/migrations/deviceV4.test.ts @@ -0,0 +1,156 @@ +import { TrezorDevice } from '@suite-common/suite-types'; +import { PORTFOLIO_TRACKER_DEVICE_ID } from '@suite-common/wallet-core'; + +import { backfillPortfolioTrackerUnavailableCapabilities } from '../../migrations/device/v4'; + +describe('backfillPortfolioTrackerUnavailableCapabilities', () => { + it('should add unavailableCapabilities to portfolio tracker device without existing capabilities', () => { + const oldDevices = [ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + status: 'available', + }, + ] as unknown as TrezorDevice[]; + + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities(oldDevices as any); + + expect(migratedDevices).toEqual([ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + status: 'available', + unavailableCapabilities: { + evolu: 'no-support', + }, + }, + ]); + }); + + it('should add unavailableCapabilities to portfolio tracker device with empty capabilities', () => { + const oldDevices = [ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + unavailableCapabilities: {}, + }, + ] as unknown as TrezorDevice[]; + + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities(oldDevices as any); + + expect(migratedDevices).toEqual([ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + unavailableCapabilities: { + evolu: 'no-support', + }, + }, + ]); + }); + + it('should preserve existing unavailableCapabilities and add evolu for portfolio tracker', () => { + const oldDevices = [ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + unavailableCapabilities: { + someOtherCapability: 'disabled', + }, + }, + ] as unknown as TrezorDevice[]; + + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities(oldDevices as any); + + expect(migratedDevices).toEqual([ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + unavailableCapabilities: { + someOtherCapability: 'disabled', + evolu: 'no-support', + }, + }, + ]); + }); + + it('should not modify portfolio tracker if evolu capability already exists', () => { + const oldDevices = [ + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + unavailableCapabilities: { + evolu: 'already-set', + otherCapability: 'value', + }, + }, + ] as unknown as TrezorDevice[]; + + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities(oldDevices as any); + + expect(migratedDevices).toEqual(oldDevices); + }); + + it('should not modify regular devices without portfolio tracker ID', () => { + const oldDevices = [ + { + id: 'regular-device-id', + type: 'acquired', + status: 'available', + // No unavailableCapabilities + }, + { + id: 'another-device', + type: 'acquired', + unavailableCapabilities: { + someCapability: 'value', + }, + }, + ] as unknown as TrezorDevice[]; + + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities(oldDevices); + + expect(migratedDevices).toEqual(oldDevices); + }); + + it('should handle mixed devices array with portfolio tracker and regular devices', () => { + const oldDevices = [ + { + id: 'regular-device-1', + type: 'acquired', + }, + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + }, + { + id: 'regular-device-2', + unavailableCapabilities: { + existingCap: 'value', + }, + }, + ] as unknown as TrezorDevice[]; + + const migratedDevices = backfillPortfolioTrackerUnavailableCapabilities(oldDevices); + + expect(migratedDevices).toEqual([ + { + id: 'regular-device-1', + type: 'acquired', + }, + { + id: PORTFOLIO_TRACKER_DEVICE_ID, + type: 'acquired', + unavailableCapabilities: { + evolu: 'no-support', + }, + }, + { + id: 'regular-device-2', + unavailableCapabilities: { + existingCap: 'value', + }, + }, + ]); + }); +});