From 26127a4df8fa6038b099d17bda49168707a54355 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Tue, 14 Jan 2025 12:11:15 +0100 Subject: [PATCH] feat(connect): emit FIRMWARE_VERSION_CHANGED event --- packages/connect/src/core/index.ts | 3 ++ packages/connect/src/device/Device.ts | 9 +++++ .../src/device/__tests__/DeviceList.test.ts | 37 +++++++++++++++++++ packages/connect/src/events/device.ts | 14 ++++++- .../connect/src/types/api/__tests__/events.ts | 7 ++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/connect/src/core/index.ts b/packages/connect/src/core/index.ts index 9bbf289b94b..a701a92c7cc 100644 --- a/packages/connect/src/core/index.ts +++ b/packages/connect/src/core/index.ts @@ -651,6 +651,9 @@ const onCallDevice = async ( createUiMessage(UI.REQUEST_PASSPHRASE_ON_DEVICE, { device: device.toMessageObject() }), ); }); + device.on(DEVICE.FIRMWARE_VERSION_CHANGED, payload => { + sendCoreMessage(createDeviceMessage(DEVICE.FIRMWARE_VERSION_CHANGED, payload)); + }); if (useCoreInPopup && env === 'webextension' && origin) { device.initStorage(new WebextensionStateStorage(origin)); } diff --git a/packages/connect/src/device/Device.ts b/packages/connect/src/device/Device.ts index 47ac593eb75..20aefde7778 100644 --- a/packages/connect/src/device/Device.ts +++ b/packages/connect/src/device/Device.ts @@ -22,6 +22,7 @@ import { UiResponsePin, UiResponsePassphrase, UiResponseWord, + DeviceVersionChanged, } from '../events'; import { getAllNetworks } from '../data/coinInfo'; import { DataManager } from '../data/DataManager'; @@ -106,6 +107,7 @@ export interface DeviceEvents { ) => void; [DEVICE.PASSPHRASE_ON_DEVICE]: () => void; [DEVICE.BUTTON]: (device: Device, payload: DeviceButtonRequestPayload) => void; + [DEVICE.FIRMWARE_VERSION_CHANGED]: (payload: DeviceVersionChanged['payload']) => void; } type DeviceLifecycle = @@ -983,6 +985,13 @@ export class Device extends TypedEmitter { // check if FW version or capabilities did change if (!version || !versionUtils.isEqual(version, newVersion)) { + if (version) { + this.emit(DEVICE.FIRMWARE_VERSION_CHANGED, { + oldVersion: version, + newVersion, + device: this.toMessageObject(), + }); + } this._unavailableCapabilities = getUnavailableCapabilities(feat, getAllNetworks()); this._firmwareStatus = getFirmwareStatus(feat); this._firmwareRelease = getRelease(feat); diff --git a/packages/connect/src/device/__tests__/DeviceList.test.ts b/packages/connect/src/device/__tests__/DeviceList.test.ts index a245b830b77..2b5d83a08d6 100644 --- a/packages/connect/src/device/__tests__/DeviceList.test.ts +++ b/packages/connect/src/device/__tests__/DeviceList.test.ts @@ -275,4 +275,41 @@ describe('DeviceList', () => { ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[8][1]]), // path 4 ]); }); + + it('FIRMWARE_VERSION_CHANGED event', async () => { + let readCount = 0; + const transport = createTestTransport({ + read: () => { + let features = '3f232300110000000c1002180020006000aa010154'; // 2.0.0 + if (readCount > 0) { + features = `3f232300110000000c10021800200${1}6000aa010154`; // 2.0.1 + } + readCount++; + + return Promise.resolve({ + success: true, + payload: Buffer.from(features, 'hex'), + }); + }, + }); + + list.init({ transports: [transport], pendingTransportEvent: true }); + await list.pendingConnection(); + + const device = list.getOnlyDevice(); + if (!device) throw new Error('Device is missing'); + + const spyEvent = jest.fn(); + device.on('device-firmware_version_changed', spyEvent); + + // Initialize > GetFeatures + await device.acquire(); + await device.initialize(false); + await device.release(); + + expect(spyEvent.mock.calls[0][0]).toMatchObject({ + oldVersion: [2, 0, 0], + newVersion: [2, 0, 1], + }); + }); }); diff --git a/packages/connect/src/events/device.ts b/packages/connect/src/events/device.ts index aa94479a514..09386b92365 100644 --- a/packages/connect/src/events/device.ts +++ b/packages/connect/src/events/device.ts @@ -1,5 +1,6 @@ import type { PROTO } from '../constants'; import type { Device } from '../types/device'; +import type { VersionArray } from '../types/firmware'; import type { MessageFactoryFn } from '../types/utils'; export const DEVICE_EVENT = 'DEVICE_EVENT'; @@ -9,6 +10,7 @@ export const DEVICE = { CONNECT_UNACQUIRED: 'device-connect_unacquired', DISCONNECT: 'device-disconnect', CHANGED: 'device-changed', + FIRMWARE_VERSION_CHANGED: 'device-firmware_version_changed', // trezor-link events in protobuf format BUTTON: 'button', @@ -27,6 +29,15 @@ export interface DeviceButtonRequest { payload: DeviceButtonRequestPayload & { device: Device }; } +export interface DeviceVersionChanged { + type: typeof DEVICE.FIRMWARE_VERSION_CHANGED; + payload: { + device: Device; + oldVersion: VersionArray; + newVersion: VersionArray; + }; +} + export type DeviceEvent = | { type: @@ -36,7 +47,8 @@ export type DeviceEvent = | typeof DEVICE.DISCONNECT; payload: Device; } - | DeviceButtonRequest; + | DeviceButtonRequest + | DeviceVersionChanged; export type DeviceEventMessage = DeviceEvent & { event: typeof DEVICE_EVENT }; diff --git a/packages/connect/src/types/api/__tests__/events.ts b/packages/connect/src/types/api/__tests__/events.ts index a090f623ab6..89b0c523ed6 100644 --- a/packages/connect/src/types/api/__tests__/events.ts +++ b/packages/connect/src/types/api/__tests__/events.ts @@ -22,6 +22,13 @@ export const events = (api: TrezorConnect) => { return; } + if (event.type === 'device-firmware_version_changed') { + const { payload } = event; + payload.oldVersion.join('.'); + payload.newVersion.join('.'); + + return; + } const { payload } = event; payload.path.toLowerCase(); if (payload.type === 'acquired') {