diff --git a/packages/connect/src/api/cardano/api/cardanoSignTransaction.ts b/packages/connect/src/api/cardano/api/cardanoSignTransaction.ts index 741feaddf9c..f4f7ba198d6 100644 --- a/packages/connect/src/api/cardano/api/cardanoSignTransaction.ts +++ b/packages/connect/src/api/cardano/api/cardanoSignTransaction.ts @@ -47,18 +47,18 @@ export type CardanoSignTransactionParams = { signingMode: PROTO.CardanoTxSigningMode; inputsWithPath: InputWithPath[]; outputsWithData: OutputWithData[]; - fee: PROTO.UintType; - ttl?: PROTO.UintType; + fee: PROTO.CardanoSignTxInit['fee']; + ttl?: PROTO.CardanoSignTxInit['ttl']; certificatesWithPoolOwnersAndRelays: CertificateWithPoolOwnersAndRelays[]; withdrawals: PROTO.CardanoTxWithdrawal[]; mint: AssetGroupWithTokens[]; auxiliaryData?: PROTO.CardanoTxAuxiliaryData; - validityIntervalStart?: PROTO.UintType; + validityIntervalStart?: PROTO.CardanoSignTxInit['validity_interval_start']; scriptDataHash?: string; collateralInputsWithPath: CollateralInputWithPath[]; requiredSigners: PROTO.CardanoTxRequiredSigner[]; collateralReturnWithData?: OutputWithData; - totalCollateral?: PROTO.UintType; + totalCollateral?: PROTO.CardanoSignTxInit['total_collateral']; referenceInputs: PROTO.CardanoTxReferenceInput[]; protocolMagic: number; networkId: number; diff --git a/packages/connect/src/device/DeviceCommands.ts b/packages/connect/src/device/DeviceCommands.ts index 2b1d4f7716a..4d32cc00499 100644 --- a/packages/connect/src/device/DeviceCommands.ts +++ b/packages/connect/src/device/DeviceCommands.ts @@ -18,20 +18,16 @@ import { initLog } from '../utils/debug'; import * as hdnodeUtils from '../utils/hdnodeUtils'; import { getScriptType, getSerializedPath, isTaprootPath, toHardened } from '../utils/pathUtils'; -type MessageType = Messages.MessageType; -type MessageKey = keyof MessageType; -type TypedPayload = { - type: T; - message: MessageType[T]; -}; -type TypedCallResponseMap = { - [K in keyof MessageType]: TypedPayload; -}; -type DefaultPayloadMessage = TypedCallResponseMap[keyof MessageType]; +type TypedCall = Messages.TypedCall; + +export type { TypedCall }; const logger = initLog('DeviceCommands'); -const assertType = (res: DefaultPayloadMessage, resType: MessageKey | MessageKey[]) => { +const assertType = ( + res: Messages.MessageResponse, + resType: Messages.MessageKey | Messages.MessageKey[], +) => { const splitResTypes = Array.isArray(resType) ? resType : resType.split('|'); if (!splitResTypes.includes(res.type)) { throw ERRORS.TypedError( @@ -295,10 +291,10 @@ export class DeviceCommands { } // Sends an async message to the opened device. - private async call( - type: MessageKey, - msg: DefaultPayloadMessage['message'] = {}, - ): Promise { + private async call( + type: T, + msg: Messages.MessagePayload, + ): Promise { logger.debug('Sending', type, filterForLog(type, msg)); this.callPromise = this.transport.call({ @@ -327,32 +323,30 @@ export class DeviceCommands { filterForLog(res.payload.type, res.payload.message), ); - // TODO: https://github.com/trezor/trezor-suite/issues/5301 - // @ts-expect-error return res.payload; } - typedCall( + typedCall( type: T, resType: R, - msg?: MessageType[T], - ): Promise; - typedCall( + msg?: Messages.MessagePayload, + ): Promise>; + typedCall( type: T, resType: R, - msg?: MessageType[T], - ): Promise>; + msg?: Messages.MessagePayload, + ): Promise>; async typedCall( - type: MessageKey, - resType: MessageKey | MessageKey[], - msg?: DefaultPayloadMessage['message'], + type: Messages.MessageKey, + resType: Messages.MessageKey | Messages.MessageKey[], + msg: Messages.MessagePayload = {}, ) { if (this.disposed) { throw ERRORS.TypedError('Runtime', 'typedCall: DeviceCommands already disposed'); } // Assert message type // msg is allowed to be undefined for some calls, in that case the schema is an empty object - Assert(Messages.MessageType.properties[type], msg ?? {}); + Assert(Messages.MessageType.properties[type], msg); const response = await this._commonCall(type, msg); try { assertType(response, resType); @@ -380,7 +374,7 @@ export class DeviceCommands { return response; } - async _commonCall(type: MessageKey, msg?: DefaultPayloadMessage['message']) { + async _commonCall(type: T, msg: Messages.MessagePayload) { if (this.disposed) { throw ERRORS.TypedError('Runtime', 'typedCall: DeviceCommands already disposed'); } @@ -389,7 +383,7 @@ export class DeviceCommands { return this._filterCommonTypes(resp); } - _filterCommonTypes(res: DefaultPayloadMessage): Promise { + _filterCommonTypes(res: Messages.MessageResponse): Promise { this.device.clearCancelableAction(); if (res.type === 'Failure') { @@ -589,5 +583,3 @@ export class DeviceCommands { } } } - -export type TypedCall = DeviceCommands['typedCall']; diff --git a/packages/connect/src/device/prompts.ts b/packages/connect/src/device/prompts.ts index 1e74b5dfc61..a7fd95f9c43 100644 --- a/packages/connect/src/device/prompts.ts +++ b/packages/connect/src/device/prompts.ts @@ -35,6 +35,9 @@ export const cancelPrompt = (device: Device, expectResponse = true) => { return expectResponse ? device.transport.call(cancelArgs) : device.transport.send(cancelArgs); }; +const extractMessage = (payload?: Messages.MessageResponse) => + (payload && 'message' in payload.message && payload.message.message) || ''; + const prompt = (event: E, { device, ...rest }: DeviceEventArgs) => // return non nullable first arg of PromptCallback new Promise>(resolve => { @@ -43,8 +46,8 @@ const prompt = (event: E, { device, ...rest }: DeviceEve response.success ? resolve({ success: false, - error: error || (response.payload?.message.message as string), - message: response.payload?.message.message as string, + error: error || extractMessage(response.payload), + message: extractMessage(response.payload), isTransportError: !response.success, }) : resolve({ diff --git a/packages/protobuf/scripts/protobuf-patches/MessageType.ts b/packages/protobuf/scripts/protobuf-patches/MessageType.ts index 6705283c24f..2e83a29d8b2 100644 --- a/packages/protobuf/scripts/protobuf-patches/MessageType.ts +++ b/packages/protobuf/scripts/protobuf-patches/MessageType.ts @@ -3,13 +3,24 @@ export type MessageKey = keyof MessageType; -export type MessageResponse = { - type: T; - message: MessageType[T]; -}; +export type MessagePayload = MessageType[T]; + +export type MessageResponse = T extends any + ? { + type: T; + message: MessagePayload; + } + : never; -export type TypedCall = ( - type: T, - resType: R, - message?: MessageType[T], -) => Promise>; +export type TypedCall = { + ( + type: T, + resType: R, + message?: MessagePayload, + ): Promise>; + ( + type: T, + resType: R, + message?: MessagePayload, + ): Promise>; +}; diff --git a/packages/protobuf/scripts/protobuf-patches/index.ts b/packages/protobuf/scripts/protobuf-patches/index.ts index 9cf1094ccd1..eec3568e43f 100644 --- a/packages/protobuf/scripts/protobuf-patches/index.ts +++ b/packages/protobuf/scripts/protobuf-patches/index.ts @@ -252,7 +252,9 @@ export const TYPE_PATCH = { }; export const readPatch = (file: string) => - fs.readFileSync(path.join(__dirname, file), 'utf8').replace(/^\/\/ @ts-nocheck.*\n?/gm, ''); + fs + .readFileSync(path.join(__dirname, file), 'utf8') + .replace(/^\/\/ (@ts-nocheck|eslint-disable-next-line).*\n?/gm, ''); export const DEFINITION_PATCH = { TxInputType: () => readPatch('./TxInputType.ts'), diff --git a/packages/protobuf/scripts/protobuf-types.ts b/packages/protobuf/scripts/protobuf-types.ts index 8dd51558e9c..3d1327b9739 100644 --- a/packages/protobuf/scripts/protobuf-types.ts +++ b/packages/protobuf/scripts/protobuf-types.ts @@ -181,9 +181,9 @@ const createCustomTypes = () => { const lines: string[] = []; lines.push('// This file is auto generated by @trezor/protobuf package', ''); lines.push('// custom type uint32/64 may be represented as string'); - lines.push(`export type ${UINT_TYPE} = string | number;`, ''); + lines.push(`type ${UINT_TYPE} = string | number;`, ''); lines.push('// custom type sint32/64'); - lines.push(`export type ${SINT_TYPE} = string | number;`, ''); + lines.push(`type ${SINT_TYPE} = string | number;`, ''); lines.push(readPatch(`../../../device-utils/src/deviceModelInternal.ts`), ''); return lines; @@ -200,6 +200,7 @@ const createMessageType = (types: TypeItem[]) => { }); lines.push('};'); + lines.push('\n// @COPY from this marker to the EOF, types are copied into messages-schema'); lines.push(readPatch('MessageType.ts')); return lines; diff --git a/packages/protobuf/src/decode.ts b/packages/protobuf/src/decode.ts index f2494fe4ccc..d267af8c0f0 100644 --- a/packages/protobuf/src/decode.ts +++ b/packages/protobuf/src/decode.ts @@ -1,5 +1,6 @@ import { Field, Message as MessageType, Type } from 'protobufjs/light'; +import type { MessageResponse } from './messages'; import { createMessageFromType, isPrimitiveField } from './utils'; const transform = (field: Field, value: any) => { @@ -84,5 +85,5 @@ export const decodeMessage = ( const { Message, messageName } = createMessageFromType(messages, messageType); const message = decode(Message, data); - return { messageName, message }; + return { type: messageName, message } as MessageResponse; }; diff --git a/packages/protobuf/src/index.ts b/packages/protobuf/src/index.ts index bedf05b13f4..09ce6d75557 100644 --- a/packages/protobuf/src/index.ts +++ b/packages/protobuf/src/index.ts @@ -17,7 +17,6 @@ export const { parseConfigure, decodeMessage, encodeMessage } = (() => { return { parseConfigure: parse, decodeMessage: decode, encodeMessage: encode }; })(); -export * from './types'; export * as Messages from './messages'; export { loadDefinitions } from './load-definitions'; export * as MessagesSchema from './messages-schema'; diff --git a/packages/protobuf/src/messages-schema.ts b/packages/protobuf/src/messages-schema.ts index 4dea6191bd4..8140f9f7110 100644 --- a/packages/protobuf/src/messages-schema.ts +++ b/packages/protobuf/src/messages-schema.ts @@ -3820,18 +3820,28 @@ export const MessageType = Type.Object( { $id: 'MessageType' }, ); -// custom type uint32/64 may be represented as string -export type UintType = string | number; +// @COPY from this marker to the EOF, types are copied into messages-schema export type MessageKey = keyof MessageType; -export type MessageResponse = { - type: T; - message: MessageType[T]; +export type MessagePayload = MessageType[T]; + +export type MessageResponse = T extends any + ? { + type: T; + message: MessagePayload; + } + : never; + +export type TypedCall = { + ( + type: T, + resType: R, + message?: MessagePayload, + ): Promise>; + ( + type: T, + resType: R, + message?: MessagePayload, + ): Promise>; }; - -export type TypedCall = ( - type: T, - resType: R, - message?: MessageType[T], -) => Promise>; diff --git a/packages/protobuf/src/messages.ts b/packages/protobuf/src/messages.ts index e3411aff519..b353c27be4b 100644 --- a/packages/protobuf/src/messages.ts +++ b/packages/protobuf/src/messages.ts @@ -1,10 +1,10 @@ // This file is auto generated by @trezor/protobuf package // custom type uint32/64 may be represented as string -export type UintType = string | number; +type UintType = string | number; // custom type sint32/64 -export type SintType = string | number; +type SintType = string | number; export enum DeviceModelInternal { T1B1 = 'T1B1', @@ -2569,15 +2569,28 @@ export type MessageType = { TezosSignedTx: TezosSignedTx; }; +// @COPY from this marker to the EOF, types are copied into messages-schema + export type MessageKey = keyof MessageType; -export type MessageResponse = { - type: T; - message: MessageType[T]; -}; +export type MessagePayload = MessageType[T]; -export type TypedCall = ( - type: T, - resType: R, - message?: MessageType[T], -) => Promise>; +export type MessageResponse = T extends any + ? { + type: T; + message: MessagePayload; + } + : never; + +export type TypedCall = { + ( + type: T, + resType: R, + message?: MessagePayload, + ): Promise>; + ( + type: T, + resType: R, + message?: MessagePayload, + ): Promise>; +}; diff --git a/packages/protobuf/src/types.ts b/packages/protobuf/src/types.ts deleted file mode 100644 index 70353e2e5b9..00000000000 --- a/packages/protobuf/src/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as Messages from './messages'; - -export type MessageFromTrezor = { - type: keyof Messages.MessageType; - message: Record; -}; diff --git a/packages/protobuf/src/utils.ts b/packages/protobuf/src/utils.ts index 53d0eb3c31f..d8b67ff2293 100644 --- a/packages/protobuf/src/utils.ts +++ b/packages/protobuf/src/utils.ts @@ -2,7 +2,7 @@ import * as protobuf from 'protobufjs/light'; -import type { MessageFromTrezor } from './types'; +import type { MessageKey } from './messages'; const primitiveTypes = [ 'bool', @@ -58,13 +58,13 @@ export const createMessageFromType = (messages: protobuf.Root, messageType: numb return { Message, - messageName: messageType as MessageFromTrezor['type'], + messageName: messageType as MessageKey, }; } const messageTypes = messages.lookupEnum('MessageType'); - const messageName = messageTypes.valuesById[messageType] as MessageFromTrezor['type']; + const messageName = messageTypes.valuesById[messageType] as MessageKey; const Message = messages.lookupType(messageName); diff --git a/packages/schema-utils/src/codegen.ts b/packages/schema-utils/src/codegen.ts index 6a390d6de18..53d31232e0d 100644 --- a/packages/schema-utils/src/codegen.ts +++ b/packages/schema-utils/src/codegen.ts @@ -7,9 +7,12 @@ export function generate(code: string) { // Since there are some issues with typeof code = code.replace(/typeof undefined/g, 'undefined'); code = code.replace(/keyof typeof/g, 'keyof'); - // Ignore types added at end of message.ts, these are too complex for the generator - if (code.indexOf('export type MessageKey = keyof MessageType') >= 0) { - code = code.substring(0, code.indexOf('export type MessageKey = keyof MessageType')); + let helpers = ''; + // Duplicate types added at end of message.ts, as these are too complex for the generator + const helpersIndex = code.indexOf('// @COPY'); + if (helpersIndex >= 0) { + helpers = code.substring(helpersIndex); + code = code.substring(0, helpersIndex); } // Make generator aware of custom types const customTypesMapping = { @@ -59,23 +62,7 @@ export function generate(code: string) { output = `/* eslint-disable camelcase */\n${output}`; // Add types for message schema if (output.indexOf('export type MessageType =') > -1) { - output = `${output}\n -// custom type uint32/64 may be represented as string -export type UintType = string | number; - -export type MessageKey = keyof MessageType; - -export type MessageResponse = { - type: T; - message: MessageType[T]; -}; - -export type TypedCall = ( - type: T, - resType: R, - message?: MessageType[T], -) => Promise>; -`; + output = `${output}\n\n${helpers}`; } return output; diff --git a/packages/transport/src/transports/abstract.ts b/packages/transport/src/transports/abstract.ts index eb785798046..6a256744909 100644 --- a/packages/transport/src/transports/abstract.ts +++ b/packages/transport/src/transports/abstract.ts @@ -1,4 +1,4 @@ -import { MessageFromTrezor, loadDefinitions, parseConfigure } from '@trezor/protobuf'; +import { Messages, loadDefinitions, parseConfigure } from '@trezor/protobuf'; import { PROTOCOL_MALFORMED, TransportProtocol } from '@trezor/protocol'; import { ScheduleActionParams, ScheduledAction, TypedEmitter, scheduleAction } from '@trezor/utils'; @@ -260,7 +260,7 @@ export abstract class AbstractTransport extends TransportEmitter { session: Session; protocol?: TransportProtocol; } & AbortableParam, - ): AsyncResultWithTypedError; + ): AsyncResultWithTypedError; /** * Send and read after that @@ -272,7 +272,7 @@ export abstract class AbstractTransport extends TransportEmitter { data: Record; protocol?: TransportProtocol; } & AbortableParam, - ): AsyncResultWithTypedError; + ): AsyncResultWithTypedError; /** * Stop transport = remove all listeners + try to release session + cancel all requests diff --git a/packages/transport/src/utils/receive.ts b/packages/transport/src/utils/receive.ts index d5517e9bc0c..b3015e746b9 100644 --- a/packages/transport/src/utils/receive.ts +++ b/packages/transport/src/utils/receive.ts @@ -44,7 +44,7 @@ export async function receiveAndParse ReturnType