@sidhujag/sysweb3-keyring 1.0.544 → 1.0.547

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/coverage/clover.xml +2875 -0
  2. package/coverage/coverage-final.json +29468 -0
  3. package/coverage/lcov-report/base.css +354 -0
  4. package/coverage/lcov-report/block-navigation.js +85 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +320 -0
  7. package/coverage/lcov-report/prettify.css +101 -0
  8. package/coverage/lcov-report/prettify.js +1008 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +191 -0
  11. package/coverage/lcov-report/src/index.html +276 -0
  12. package/coverage/lcov-report/src/index.ts.html +114 -0
  13. package/coverage/lcov-report/src/initial-state.ts.html +558 -0
  14. package/coverage/lcov-report/src/keyring-manager.ts.html +6279 -0
  15. package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +178 -0
  16. package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +144 -0
  17. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +1560 -0
  18. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +276 -0
  19. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +495 -0
  20. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +1138 -0
  21. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +363 -0
  22. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +289 -0
  23. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +486 -0
  24. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +240 -0
  25. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +342 -0
  26. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +2388 -0
  27. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +453 -0
  28. package/coverage/lcov-report/src/ledger/consts.ts.html +177 -0
  29. package/coverage/lcov-report/src/ledger/index.html +216 -0
  30. package/coverage/lcov-report/src/ledger/index.ts.html +1371 -0
  31. package/coverage/lcov-report/src/ledger/utils.ts.html +102 -0
  32. package/coverage/lcov-report/src/signers.ts.html +591 -0
  33. package/coverage/lcov-report/src/storage.ts.html +198 -0
  34. package/coverage/lcov-report/src/transactions/ethereum.ts.html +5826 -0
  35. package/coverage/lcov-report/src/transactions/index.html +216 -0
  36. package/coverage/lcov-report/src/transactions/index.ts.html +93 -0
  37. package/coverage/lcov-report/src/transactions/syscoin.ts.html +1521 -0
  38. package/coverage/lcov-report/src/trezor/index.html +176 -0
  39. package/coverage/lcov-report/src/trezor/index.ts.html +2655 -0
  40. package/coverage/lcov-report/src/types.ts.html +1443 -0
  41. package/coverage/lcov-report/src/utils/derivation-paths.ts.html +486 -0
  42. package/coverage/lcov-report/src/utils/index.html +196 -0
  43. package/coverage/lcov-report/src/utils/psbt.ts.html +159 -0
  44. package/coverage/lcov-report/test/helpers/constants.ts.html +627 -0
  45. package/coverage/lcov-report/test/helpers/index.html +176 -0
  46. package/coverage/lcov.info +4832 -0
  47. package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/appClient.js +1 -124
  48. package/dist/cjs/ledger/bitcoin_client/lib/appClient.js.map +1 -0
  49. package/{cjs → dist/cjs}/transactions/ethereum.js +24 -11
  50. package/dist/cjs/transactions/ethereum.js.map +1 -0
  51. package/dist/package.json +50 -0
  52. package/{types → dist/types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -6
  53. package/examples/basic-usage.js +140 -0
  54. package/jest.config.js +32 -0
  55. package/package.json +31 -13
  56. package/readme.md +201 -0
  57. package/src/declare.d.ts +7 -0
  58. package/src/errorUtils.ts +83 -0
  59. package/src/hardware-wallet-manager.ts +655 -0
  60. package/src/index.ts +12 -0
  61. package/src/initial-state.ts +108 -0
  62. package/src/keyring-manager.ts +2698 -0
  63. package/src/ledger/bitcoin_client/index.ts +19 -0
  64. package/src/ledger/bitcoin_client/lib/appClient.ts +405 -0
  65. package/src/ledger/bitcoin_client/lib/bip32.ts +61 -0
  66. package/src/ledger/bitcoin_client/lib/buffertools.ts +134 -0
  67. package/src/ledger/bitcoin_client/lib/clientCommands.ts +356 -0
  68. package/src/ledger/bitcoin_client/lib/constants.ts +12 -0
  69. package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +65 -0
  70. package/src/ledger/bitcoin_client/lib/merkle.ts +136 -0
  71. package/src/ledger/bitcoin_client/lib/merkleMap.ts +49 -0
  72. package/src/ledger/bitcoin_client/lib/policy.ts +91 -0
  73. package/src/ledger/bitcoin_client/lib/psbtv2.ts +768 -0
  74. package/src/ledger/bitcoin_client/lib/varint.ts +120 -0
  75. package/src/ledger/consts.ts +3 -0
  76. package/src/ledger/index.ts +685 -0
  77. package/src/ledger/types.ts +74 -0
  78. package/src/network-utils.ts +99 -0
  79. package/src/providers.ts +345 -0
  80. package/src/signers.ts +158 -0
  81. package/src/storage.ts +63 -0
  82. package/src/transactions/__tests__/integration.test.ts +303 -0
  83. package/src/transactions/__tests__/syscoin.test.ts +409 -0
  84. package/src/transactions/ethereum.ts +2503 -0
  85. package/src/transactions/index.ts +2 -0
  86. package/src/transactions/syscoin.ts +542 -0
  87. package/src/trezor/index.ts +1050 -0
  88. package/src/types.ts +366 -0
  89. package/src/utils/derivation-paths.ts +133 -0
  90. package/src/utils/psbt.ts +24 -0
  91. package/src/utils.ts +191 -0
  92. package/test/README.md +158 -0
  93. package/test/__mocks__/ledger-mock.js +20 -0
  94. package/test/__mocks__/trezor-mock.js +75 -0
  95. package/test/cleanup-summary.md +167 -0
  96. package/test/helpers/README.md +78 -0
  97. package/test/helpers/constants.ts +79 -0
  98. package/test/helpers/setup.ts +714 -0
  99. package/test/integration/import-validation.spec.ts +588 -0
  100. package/test/unit/hardware/ledger.spec.ts +869 -0
  101. package/test/unit/hardware/trezor.spec.ts +828 -0
  102. package/test/unit/keyring-manager/account-management.spec.ts +970 -0
  103. package/test/unit/keyring-manager/import-watchonly.spec.ts +181 -0
  104. package/test/unit/keyring-manager/import-wif.spec.ts +126 -0
  105. package/test/unit/keyring-manager/initialization.spec.ts +782 -0
  106. package/test/unit/keyring-manager/key-derivation.spec.ts +996 -0
  107. package/test/unit/keyring-manager/security.spec.ts +505 -0
  108. package/test/unit/keyring-manager/state-management.spec.ts +375 -0
  109. package/test/unit/network/network-management.spec.ts +372 -0
  110. package/test/unit/transactions/ethereum-transactions.spec.ts +382 -0
  111. package/test/unit/transactions/syscoin-transactions.spec.ts +615 -0
  112. package/tsconfig.json +14 -0
  113. package/cjs/ledger/bitcoin_client/lib/appClient.js.map +0 -1
  114. package/cjs/transactions/ethereum.js.map +0 -1
  115. /package/{README.md → dist/README.md} +0 -0
  116. /package/{cjs → dist/cjs}/errorUtils.js +0 -0
  117. /package/{cjs → dist/cjs}/errorUtils.js.map +0 -0
  118. /package/{cjs → dist/cjs}/hardware-wallet-manager.js +0 -0
  119. /package/{cjs → dist/cjs}/hardware-wallet-manager.js.map +0 -0
  120. /package/{cjs → dist/cjs}/index.js +0 -0
  121. /package/{cjs → dist/cjs}/index.js.map +0 -0
  122. /package/{cjs → dist/cjs}/initial-state.js +0 -0
  123. /package/{cjs → dist/cjs}/initial-state.js.map +0 -0
  124. /package/{cjs → dist/cjs}/keyring-manager.js +0 -0
  125. /package/{cjs → dist/cjs}/keyring-manager.js.map +0 -0
  126. /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js +0 -0
  127. /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js.map +0 -0
  128. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
  129. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
  130. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
  131. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
  132. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
  133. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
  134. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
  135. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
  136. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
  137. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
  138. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
  139. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
  140. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
  141. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
  142. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
  143. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
  144. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
  145. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
  146. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
  147. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
  148. /package/{cjs → dist/cjs}/ledger/consts.js +0 -0
  149. /package/{cjs → dist/cjs}/ledger/consts.js.map +0 -0
  150. /package/{cjs → dist/cjs}/ledger/index.js +0 -0
  151. /package/{cjs → dist/cjs}/ledger/index.js.map +0 -0
  152. /package/{cjs → dist/cjs}/ledger/types.js +0 -0
  153. /package/{cjs → dist/cjs}/ledger/types.js.map +0 -0
  154. /package/{cjs → dist/cjs}/network-utils.js +0 -0
  155. /package/{cjs → dist/cjs}/network-utils.js.map +0 -0
  156. /package/{cjs → dist/cjs}/providers.js +0 -0
  157. /package/{cjs → dist/cjs}/providers.js.map +0 -0
  158. /package/{cjs → dist/cjs}/signers.js +0 -0
  159. /package/{cjs → dist/cjs}/signers.js.map +0 -0
  160. /package/{cjs → dist/cjs}/storage.js +0 -0
  161. /package/{cjs → dist/cjs}/storage.js.map +0 -0
  162. /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js +0 -0
  163. /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js.map +0 -0
  164. /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js +0 -0
  165. /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
  166. /package/{cjs → dist/cjs}/transactions/index.js +0 -0
  167. /package/{cjs → dist/cjs}/transactions/index.js.map +0 -0
  168. /package/{cjs → dist/cjs}/transactions/syscoin.js +0 -0
  169. /package/{cjs → dist/cjs}/transactions/syscoin.js.map +0 -0
  170. /package/{cjs → dist/cjs}/trezor/index.js +0 -0
  171. /package/{cjs → dist/cjs}/trezor/index.js.map +0 -0
  172. /package/{cjs → dist/cjs}/types.js +0 -0
  173. /package/{cjs → dist/cjs}/types.js.map +0 -0
  174. /package/{cjs → dist/cjs}/utils/derivation-paths.js +0 -0
  175. /package/{cjs → dist/cjs}/utils/derivation-paths.js.map +0 -0
  176. /package/{cjs → dist/cjs}/utils/psbt.js +0 -0
  177. /package/{cjs → dist/cjs}/utils/psbt.js.map +0 -0
  178. /package/{cjs → dist/cjs}/utils.js +0 -0
  179. /package/{cjs → dist/cjs}/utils.js.map +0 -0
  180. /package/{types → dist/types}/errorUtils.d.ts +0 -0
  181. /package/{types → dist/types}/hardware-wallet-manager.d.ts +0 -0
  182. /package/{types → dist/types}/index.d.ts +0 -0
  183. /package/{types → dist/types}/initial-state.d.ts +0 -0
  184. /package/{types → dist/types}/keyring-manager.d.ts +0 -0
  185. /package/{types → dist/types}/ledger/bitcoin_client/index.d.ts +0 -0
  186. /package/{types → dist/types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
  187. /package/{types → dist/types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
  188. /package/{types → dist/types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
  189. /package/{types → dist/types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
  190. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
  191. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
  192. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
  193. /package/{types → dist/types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
  194. /package/{types → dist/types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
  195. /package/{types → dist/types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
  196. /package/{types → dist/types}/ledger/consts.d.ts +0 -0
  197. /package/{types → dist/types}/ledger/index.d.ts +0 -0
  198. /package/{types → dist/types}/ledger/types.d.ts +0 -0
  199. /package/{types → dist/types}/network-utils.d.ts +0 -0
  200. /package/{types → dist/types}/providers.d.ts +0 -0
  201. /package/{types → dist/types}/signers.d.ts +0 -0
  202. /package/{types → dist/types}/storage.d.ts +0 -0
  203. /package/{types → dist/types}/transactions/__tests__/integration.test.d.ts +0 -0
  204. /package/{types → dist/types}/transactions/__tests__/syscoin.test.d.ts +0 -0
  205. /package/{types → dist/types}/transactions/ethereum.d.ts +0 -0
  206. /package/{types → dist/types}/transactions/index.d.ts +0 -0
  207. /package/{types → dist/types}/transactions/syscoin.d.ts +0 -0
  208. /package/{types → dist/types}/trezor/index.d.ts +0 -0
  209. /package/{types → dist/types}/types.d.ts +0 -0
  210. /package/{types → dist/types}/utils/derivation-paths.d.ts +0 -0
  211. /package/{types → dist/types}/utils/psbt.d.ts +0 -0
  212. /package/{types → dist/types}/utils.d.ts +0 -0
@@ -0,0 +1,1050 @@
1
+ /* eslint-disable camelcase */
2
+ import {
3
+ TypedDataUtils,
4
+ TypedMessage,
5
+ SignTypedDataVersion,
6
+ TypedDataV1,
7
+ } from '@metamask/eth-sig-util';
8
+ import TrezorConnect, {
9
+ AccountInfo,
10
+ DEVICE_EVENT,
11
+ EthereumTransaction,
12
+ EthereumTransactionEIP1559,
13
+ // @ts-ignore
14
+ } from '@trezor/connect-webextension';
15
+ import { address } from '@trezor/utxo-lib';
16
+ import bitcoinops from 'bitcoin-ops';
17
+ import { Transaction, payments, script } from 'bitcoinjs-lib';
18
+ import { Buffer } from 'buffer';
19
+ import { stripHexPrefix } from 'ethereumjs-util';
20
+
21
+ import {
22
+ HardwareWalletManager,
23
+ HardwareWalletType,
24
+ } from '../hardware-wallet-manager';
25
+ import {
26
+ getAccountDerivationPath,
27
+ getAddressDerivationPath,
28
+ isEvmCoin,
29
+ } from '../utils/derivation-paths';
30
+
31
+ const { p2wsh } = payments;
32
+ const { decompile } = script;
33
+ const { fromBase58Check, fromBech32 } = address;
34
+
35
+ const initialHDPath = `m/44'/60'/0'/0/0`;
36
+ const DELAY_BETWEEN_POPUPS = 2000; // Increased from 1000ms to 2000ms for more reliable operation
37
+
38
+ export interface TrezorControllerState {
39
+ hdPath: string;
40
+ paths: Record<string, number>;
41
+ }
42
+
43
+ interface MessageTypeProperty {
44
+ name: string;
45
+ type: string;
46
+ }
47
+ interface MessageTypes {
48
+ [additionalProperties: string]: MessageTypeProperty[];
49
+ EIP712Domain: MessageTypeProperty[];
50
+ }
51
+
52
+ declare global {
53
+ // eslint-disable-next-line @typescript-eslint/naming-convention
54
+ interface Window {
55
+ TrezorConnect: any;
56
+ }
57
+ }
58
+ export class TrezorKeyring {
59
+ public hdPath: string = initialHDPath;
60
+ public publicKey: Buffer;
61
+ public chainCode: Buffer;
62
+ public paths: Record<string, number> = {};
63
+ public model?: string;
64
+ private hardwareWalletManager: HardwareWalletManager;
65
+ private initialized = false;
66
+
67
+ constructor() {
68
+ this.publicKey = Buffer.from('', 'hex');
69
+ this.chainCode = Buffer.from('', 'hex');
70
+ this.hdPath = '';
71
+ this.paths = {};
72
+ this.hardwareWalletManager = new HardwareWalletManager();
73
+
74
+ // Set up event listeners
75
+ this.hardwareWalletManager.on('connected', ({ type }) => {
76
+ if (type === HardwareWalletType.TREZOR) {
77
+ console.log('Trezor connected');
78
+ this.initialized = true;
79
+ }
80
+ });
81
+
82
+ this.hardwareWalletManager.on('disconnected', ({ type }) => {
83
+ if (type === HardwareWalletType.TREZOR) {
84
+ console.log('Trezor disconnected');
85
+ this.initialized = false;
86
+ }
87
+ });
88
+
89
+ TrezorConnect.on(DEVICE_EVENT, (event: any) => {
90
+ if (event.payload.features) {
91
+ this.model = event.payload.features.model;
92
+ }
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Initialize Trezor script.
98
+ */
99
+ public async init(): Promise<boolean> {
100
+ if (this.initialized) {
101
+ return true;
102
+ }
103
+
104
+ // Add a small delay to ensure Chrome extension context is ready
105
+ // This helps prevent "waiting for handshake" errors
106
+ await new Promise((resolve) => setTimeout(resolve, 100));
107
+
108
+ const result = await this.hardwareWalletManager.initializeTrezor();
109
+ if (result) {
110
+ this.initialized = true;
111
+ }
112
+ return result;
113
+ }
114
+
115
+ /**
116
+ * Execute operation with automatic retry
117
+ */
118
+ private async executeWithRetry<T>(
119
+ operation: () => Promise<T>,
120
+ operationName: string
121
+ ): Promise<T> {
122
+ // Ensure initialization first
123
+ if (!this.initialized) {
124
+ const initResult = await this.init();
125
+ if (!initResult) {
126
+ throw new Error('Failed to initialize Trezor');
127
+ }
128
+ }
129
+
130
+ // For Trezor operations, use reduced retry config to prevent popup spam
131
+ const trezorRetryConfig = {
132
+ maxRetries: 1, // Only retry once
133
+ baseDelay: 1000,
134
+ maxDelay: 5000,
135
+ backoffMultiplier: 2,
136
+ };
137
+
138
+ // Use hardware wallet manager's retry mechanism with custom config
139
+ return this.hardwareWalletManager
140
+ .retryOperation(operation, operationName, trezorRetryConfig)
141
+ .catch((error) => {
142
+ // Clean up Trezor state on failure
143
+ if (
144
+ error.message?.includes('Popup closed') ||
145
+ error.message?.includes('cancelled') ||
146
+ error.message?.includes('denied')
147
+ ) {
148
+ this.initialized = false;
149
+ // Dispose Trezor connection to clean up
150
+ try {
151
+ TrezorConnect.dispose();
152
+ } catch (disposeError) {
153
+ console.log('Failed to dispose Trezor on error:', disposeError);
154
+ }
155
+ }
156
+ throw error;
157
+ });
158
+ }
159
+
160
+ /**
161
+ * This return account info based in params provided.
162
+ *
163
+ * @param coin - network symbol. Example: eth, sys, btc
164
+ * @param slip44 - network slip44 number
165
+ * @param hdPath - path derivation. Example: m/84'/57'/0'
166
+ * @param index - index of account for path derivation
167
+ * @returns derivated account info or error
168
+ */
169
+
170
+ public async getAccountInfo({
171
+ coin,
172
+ slip44,
173
+ hdPath,
174
+ index,
175
+ }: {
176
+ coin: string;
177
+ hdPath?: string;
178
+ index?: number;
179
+ slip44: number;
180
+ }): Promise<AccountInfo> {
181
+ return this.executeWithRetry(async () => {
182
+ // Use dynamic path generation instead of hardcoded switch
183
+ this.setHdPath(coin, index || 0, slip44);
184
+
185
+ if (hdPath) this.hdPath = hdPath;
186
+
187
+ // For EVM networks, getAccountInfo is not supported
188
+ // We need to use getAddress instead
189
+ if (slip44 === 60) {
190
+ const addressResponse = await TrezorConnect.ethereumGetAddress({
191
+ path: this.hdPath,
192
+ showOnTrezor: false,
193
+ });
194
+
195
+ if (!addressResponse.success) {
196
+ throw new Error(
197
+ addressResponse.payload.error || 'Failed to get EVM address'
198
+ );
199
+ }
200
+
201
+ // Return a compatible AccountInfo structure
202
+ return {
203
+ descriptor: addressResponse.payload.address,
204
+ balance: '0', // Balance is fetched separately for EVM
205
+ empty: true,
206
+ history: {
207
+ total: 0,
208
+ unconfirmed: 0,
209
+ },
210
+ } as AccountInfo;
211
+ }
212
+
213
+ // For UTXO networks, use the standard getAccountInfo
214
+ const response = await TrezorConnect.getAccountInfo({
215
+ coin,
216
+ path: this.hdPath,
217
+ });
218
+
219
+ if (response.success) {
220
+ return response.payload;
221
+ }
222
+ throw new Error(response.payload.error);
223
+ }, 'getAccountInfo');
224
+ }
225
+
226
+ /**
227
+ * Gets the model, if known.
228
+ * This may be `undefined` if the model hasn't been loaded yet.
229
+ *
230
+ * @returns
231
+ */
232
+ public getModel(): string | undefined {
233
+ return this.model;
234
+ }
235
+
236
+ /**
237
+ * This removes the Trezor Connect iframe from the DOM
238
+ *
239
+ * @returns void
240
+ */
241
+
242
+ public dispose() {
243
+ try {
244
+ TrezorConnect.dispose();
245
+ this.initialized = false;
246
+ // Clear any cached data
247
+ this.publicKey = Buffer.from('', 'hex');
248
+ this.chainCode = Buffer.from('', 'hex');
249
+ this.hdPath = '';
250
+ this.paths = {};
251
+ } catch (error) {
252
+ console.log('Error disposing Trezor:', error);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * This verify if message is valid or not.
258
+ *
259
+ * @param coin - network symbol. Example: eth, sys, btc
260
+ * @param address - account address that signed message
261
+ * @param message - message to be verified. Example: 'Test message'
262
+ * @param signature - signature received in sign method. Example: I6BrpivjCwZmScZ6BMAHWGQPo+JjX2kzKXU5LcGVfEgvFb2VfJuKo3g6eSQcykQZiILoWNUDn5rDHkwJg3EcvuY=
263
+ * @returns derivated account info or error
264
+ */
265
+
266
+ public async verifyMessage({
267
+ coin,
268
+ address,
269
+ message,
270
+ signature,
271
+ }: {
272
+ address: string;
273
+ coin: string;
274
+ message: string;
275
+ signature: string;
276
+ }) {
277
+ return this.executeWithRetry(async () => {
278
+ let method = '';
279
+ switch (coin) {
280
+ case 'eth':
281
+ method = 'ethereumVerifyMessage';
282
+ break;
283
+ default:
284
+ method = 'verifyMessage';
285
+ }
286
+ // @ts-ignore
287
+ const { success, payload } = await TrezorConnect[method]({
288
+ coin,
289
+ address,
290
+ message,
291
+ signature,
292
+ });
293
+
294
+ if (success) {
295
+ return { success, payload };
296
+ }
297
+ throw new Error(payload.error);
298
+ }, 'verifyMessage');
299
+ }
300
+
301
+ /**
302
+ * This return account public key.
303
+ *
304
+ * @param coin - network symbol. Example: eth, sys, btc
305
+ * @param slip44 - network slip44 number
306
+ * @param hdPath - path derivation. Example: m/44'/57'/0'/0/0
307
+ * @returns publicKey and chainCode
308
+ */
309
+
310
+ public async getPublicKey({
311
+ coin,
312
+ slip44,
313
+ hdPath,
314
+ index,
315
+ }: {
316
+ coin: string;
317
+ hdPath?: string;
318
+ index?: number;
319
+ slip44: number;
320
+ }) {
321
+ this.setHdPath(coin, index || 0, slip44);
322
+
323
+ if (hdPath) this.hdPath = hdPath;
324
+
325
+ await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_POPUPS));
326
+
327
+ try {
328
+ // For EVM networks, use ethereumGetPublicKey
329
+ if (slip44 === 60) {
330
+ const { success, payload } = await TrezorConnect.ethereumGetPublicKey({
331
+ path: this.hdPath,
332
+ showOnTrezor: false,
333
+ });
334
+
335
+ if (success) {
336
+ const { publicKey } = payload;
337
+ // For Ethereum, we don't get chainCode from ethereumGetPublicKey
338
+ this.publicKey = Buffer.from(publicKey, 'hex');
339
+
340
+ return {
341
+ publicKey: `0x${publicKey}`,
342
+ chainCode: '', // Ethereum doesn't use chainCode in the same way
343
+ };
344
+ }
345
+
346
+ return { success: false, payload };
347
+ }
348
+
349
+ // For UTXO networks, use standard getPublicKey
350
+ const { success, payload } = await TrezorConnect.getPublicKey({
351
+ coin: coin,
352
+ path: this.hdPath,
353
+ });
354
+
355
+ if (success) {
356
+ const { publicKey, chainCode } = payload;
357
+
358
+ this.publicKey = Buffer.from(publicKey, 'hex');
359
+ this.chainCode = Buffer.from(chainCode, 'hex');
360
+
361
+ return {
362
+ publicKey: `0x${this.publicKey.toString('hex')}`,
363
+ chainCode: `0x${this.chainCode.toString('hex')}`,
364
+ };
365
+ }
366
+
367
+ return { success: false, payload };
368
+ } catch (error) {
369
+ return error;
370
+ }
371
+ }
372
+
373
+ public range(n: number) {
374
+ return [...Array(n).keys()];
375
+ }
376
+
377
+ /**
378
+ * This sign UTXO tx.
379
+ *
380
+ * @param coin - network symbol. Example: eth, sys, btc
381
+ * @param inputs - utxo transaction inputs
382
+ * @param outputs - utxo transaction outputs
383
+ * @returns signature object
384
+ */
385
+
386
+ public async signUtxoTransaction(utxoTransaction: any, psbt: any) {
387
+ return this.executeWithRetry(async () => {
388
+ const { payload, success } = await TrezorConnect.signTransaction(
389
+ utxoTransaction
390
+ );
391
+
392
+ if (success) {
393
+ const tx = Transaction.fromHex(payload.serializedTx);
394
+ for (const i of this.range(psbt.data.inputs.length)) {
395
+ if (tx.ins[i].witness == null) {
396
+ throw new Error(
397
+ 'Please move your funds to a Segwit address: https://wiki.trezor.io/Account'
398
+ );
399
+ }
400
+ const partialSig = [
401
+ {
402
+ pubkey: tx.ins[i].witness[1],
403
+ signature: tx.ins[i].witness[0],
404
+ },
405
+ ];
406
+ psbt.updateInput(i, { partialSig });
407
+ }
408
+
409
+ try {
410
+ const validator = (
411
+ pubkey: Buffer,
412
+ msghash: Buffer,
413
+ signature: Buffer
414
+ ) => {
415
+ if (signature && signature.length === 64) {
416
+ try {
417
+ const xOnly =
418
+ pubkey.length === 32 ? pubkey : pubkey.slice(1, 33);
419
+ // @ts-ignore ecc injected via bitcoinjs initEccLib; runtime available in syscoinjs-lib
420
+ const eccLib =
421
+ (require('bitcoinjs-lib') as any).ecc ||
422
+ require('@bitcoinerlab/secp256k1');
423
+ return eccLib && typeof eccLib.verifySchnorr === 'function'
424
+ ? eccLib.verifySchnorr(signature, msghash, xOnly)
425
+ : false;
426
+ } catch (e) {
427
+ return false;
428
+ }
429
+ }
430
+ try {
431
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
432
+ const ECPairFactory =
433
+ require('ecpair').ECPairFactory ||
434
+ require('ecpair').default ||
435
+ require('ecpair');
436
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
437
+ const ecc = require('@bitcoinerlab/secp256k1');
438
+ const ECPair = ECPairFactory(ecc);
439
+ return ECPair.fromPublicKey(pubkey).verify(msghash, signature);
440
+ } catch (e) {
441
+ return false;
442
+ }
443
+ };
444
+ if (psbt.validateSignaturesOfAllInputs(validator)) {
445
+ psbt.finalizeAllInputs();
446
+ }
447
+ } catch (err) {
448
+ console.log(err);
449
+ }
450
+ return psbt;
451
+ } else {
452
+ throw new Error('Trezor sign failed: ' + payload.error);
453
+ }
454
+ }, 'signUtxoTransaction');
455
+ }
456
+
457
+ private setHdPath(coin: string, accountIndex: number, slip44: number) {
458
+ if (isEvmCoin(coin, slip44)) {
459
+ // For EVM, the "accountIndex" parameter is actually used as the address index
460
+ // EVM typically uses account 0, and different addresses are at different address indices
461
+ this.hdPath = getAddressDerivationPath(
462
+ coin,
463
+ slip44,
464
+ 0, // account is always 0 for EVM
465
+ false, // not a change address
466
+ accountIndex // this is actually the address index for EVM
467
+ );
468
+ } else {
469
+ // For UTXO, use account-level derivation path
470
+ this.hdPath = getAccountDerivationPath(coin, slip44, accountIndex);
471
+ }
472
+ }
473
+
474
+ public convertToAddressNFormat(path: string) {
475
+ const pathArray = path.replace(/'/g, '').split('/');
476
+
477
+ pathArray.shift();
478
+
479
+ const addressN: any[] = [];
480
+
481
+ for (const index in pathArray) {
482
+ if (Number(index) <= 2 && Number(index) >= 0) {
483
+ addressN[Number(index)] = Number(pathArray[index]) | 0x80000000;
484
+ } else {
485
+ addressN[Number(index)] = Number(pathArray[index]);
486
+ }
487
+ }
488
+
489
+ return addressN;
490
+ }
491
+ public isScriptHash(address: string, networkInfo: any) {
492
+ if (!networkInfo) {
493
+ // If network info is not available, make a best guess based on address format
494
+ // This is a fallback for hardware wallet scenarios
495
+ if (this.isBech32(address)) {
496
+ const decoded = fromBech32(address);
497
+ return decoded.data.length === 32; // 32 bytes = P2WSH
498
+ }
499
+ // For non-bech32, we can't reliably determine without network info
500
+ // Default to false (P2PKH) as it's more common
501
+ return false;
502
+ }
503
+
504
+ if (!this.isBech32(address)) {
505
+ const decoded = fromBase58Check(address);
506
+ if (decoded.version === networkInfo.pubKeyHash) {
507
+ return false;
508
+ }
509
+ if (decoded.version === networkInfo.scriptHash) {
510
+ return true;
511
+ }
512
+ } else {
513
+ const decoded = fromBech32(address);
514
+ if (decoded.data.length === 20) {
515
+ return false;
516
+ }
517
+ if (decoded.data.length === 32) {
518
+ return true;
519
+ }
520
+ }
521
+ throw new Error('isScriptHash: Unknown address type');
522
+ }
523
+
524
+ public isPaymentFactory(payment: any) {
525
+ return (script: any) => {
526
+ try {
527
+ payment({ output: script });
528
+ return true;
529
+ } catch (err) {
530
+ return false;
531
+ }
532
+ };
533
+ }
534
+ public isBech32(address: string) {
535
+ try {
536
+ fromBech32(address);
537
+ return true;
538
+ } catch (e) {
539
+ return false;
540
+ }
541
+ }
542
+ public isP2WSHScript(script: any) {
543
+ this.isPaymentFactory(p2wsh)(script);
544
+
545
+ return false;
546
+ }
547
+
548
+ public convertToTrezorFormat({ psbt, coin, network }: any) {
549
+ const trezortx: any = {};
550
+
551
+ trezortx.coin = coin;
552
+ trezortx.version = psbt.version;
553
+ trezortx.inputs = [];
554
+ trezortx.outputs = [];
555
+
556
+ for (let i = 0; i < psbt.txInputs.length; i++) {
557
+ const input = psbt.txInputs[i];
558
+ const inputItem: any = {};
559
+ inputItem.prev_index = input.index;
560
+ inputItem.prev_hash = input.hash.reverse().toString('hex');
561
+ if (input.sequence) inputItem.sequence = input.sequence;
562
+
563
+ const dataInput = psbt.data.inputs[i];
564
+
565
+ // Resolve derivation path for this input
566
+ let resolvedPath: string | null = null;
567
+ const tapDer =
568
+ dataInput.tapBip32Derivation && dataInput.tapBip32Derivation.length > 0
569
+ ? dataInput.tapBip32Derivation[0]
570
+ : null;
571
+ const b32Der =
572
+ dataInput.bip32Derivation && dataInput.bip32Derivation.length > 0
573
+ ? dataInput.bip32Derivation[0]
574
+ : null;
575
+
576
+ if (tapDer && tapDer.path) {
577
+ resolvedPath = tapDer.path;
578
+ } else if (b32Der && b32Der.path) {
579
+ resolvedPath = b32Der.path;
580
+ } else if (dataInput.unknownKeyVals && dataInput.unknownKeyVals.length) {
581
+ for (const kv of dataInput.unknownKeyVals) {
582
+ if (Buffer.isBuffer(kv.key) && kv.key.toString() === 'path') {
583
+ resolvedPath = kv.value.toString();
584
+ break;
585
+ }
586
+ }
587
+ }
588
+
589
+ if (!resolvedPath) {
590
+ throw new Error(
591
+ 'convertToTrezorFormat: Missing path (tapBip32Derivation/bip32Derivation/unknownKeyVals)'
592
+ );
593
+ }
594
+
595
+ // Map path to Trezor address_n
596
+ inputItem.address_n = this.convertToAddressNFormat(resolvedPath);
597
+
598
+ // Coerce witnessUtxo.script to Buffer for downstream helpers
599
+ const inScriptBuf =
600
+ dataInput && dataInput.witnessUtxo && dataInput.witnessUtxo.script
601
+ ? Buffer.isBuffer(dataInput.witnessUtxo.script)
602
+ ? dataInput.witnessUtxo.script
603
+ : Buffer.from(dataInput.witnessUtxo.script)
604
+ : Buffer.alloc(0);
605
+
606
+ // Select Trezor script_type (detect Taproot explicitly)
607
+ const isTaproot =
608
+ inScriptBuf &&
609
+ inScriptBuf.length === 34 &&
610
+ inScriptBuf[0] === bitcoinops.OP_1 &&
611
+ inScriptBuf[1] === 0x20;
612
+
613
+ if (isTaproot) {
614
+ inputItem.script_type = 'SPENDTAPROOT';
615
+ } else {
616
+ const scriptTypes = psbt.getInputType(i);
617
+ switch (scriptTypes) {
618
+ case 'multisig':
619
+ inputItem.script_type = 'SPENDMULTISIG';
620
+ break;
621
+ case 'witnesspubkeyhash':
622
+ inputItem.script_type = 'SPENDWITNESS';
623
+ break;
624
+ default:
625
+ inputItem.script_type = this.isP2WSHScript(inScriptBuf)
626
+ ? 'SPENDP2SHWITNESS'
627
+ : 'SPENDADDRESS';
628
+ break;
629
+ }
630
+ }
631
+
632
+ trezortx.inputs.push(inputItem);
633
+ }
634
+
635
+ for (let i = 0; i < psbt.txOutputs.length; i++) {
636
+ const output = psbt.txOutputs[i];
637
+ const outputItem: any = {};
638
+ const scriptBuf = Buffer.isBuffer(output.script)
639
+ ? output.script
640
+ : Buffer.from(output.script || []);
641
+ const chunks = decompile(scriptBuf);
642
+
643
+ // Debug logging to understand the output structure
644
+ console.log(`[Trezor] Processing output ${i}:`, {
645
+ value: output.value,
646
+ valueType: typeof output.value,
647
+ hasAddress: !!output.address,
648
+ address: output.address,
649
+ scriptLength: output.script?.length,
650
+ });
651
+
652
+ // Ensure value exists and convert properly
653
+ if (output.value === undefined || output.value === null) {
654
+ throw new Error(`Output ${i} has no value`);
655
+ }
656
+
657
+ // Handle different value types
658
+ let amountStr: string;
659
+ if (typeof output.value === 'bigint') {
660
+ amountStr = output.value.toString();
661
+ } else if (typeof output.value === 'number') {
662
+ amountStr = output.value.toString();
663
+ } else if (typeof output.value === 'string') {
664
+ amountStr = output.value;
665
+ } else {
666
+ throw new Error(
667
+ `Output ${i} has unexpected value type: ${typeof output.value}`
668
+ );
669
+ }
670
+
671
+ outputItem.amount = amountStr;
672
+ if (chunks && chunks[0] === bitcoinops.OP_RETURN) {
673
+ outputItem.script_type = 'PAYTOOPRETURN';
674
+ // @ts-ignore
675
+ outputItem.op_return_data = Buffer.from(chunks[1]).toString('hex');
676
+ } else {
677
+ if (output && this.isBech32(output.address)) {
678
+ if (
679
+ scriptBuf.length === 34 &&
680
+ scriptBuf[0] === 0 &&
681
+ scriptBuf[1] === 0x20
682
+ ) {
683
+ outputItem.script_type = 'PAYTOP2SHWITNESS';
684
+ } else {
685
+ outputItem.script_type = 'PAYTOWITNESS';
686
+ }
687
+ } else {
688
+ outputItem.script_type = this.isScriptHash(output.address, network)
689
+ ? 'PAYTOSCRIPTHASH'
690
+ : 'PAYTOADDRESS';
691
+ }
692
+ if (output.address) outputItem.address = output.address;
693
+ }
694
+
695
+ // Validate that the output has required fields for Trezor
696
+ if (
697
+ outputItem.script_type === 'PAYTOADDRESS' ||
698
+ outputItem.script_type === 'PAYTOSCRIPTHASH' ||
699
+ outputItem.script_type === 'PAYTOWITNESS' ||
700
+ outputItem.script_type === 'PAYTOP2SHWITNESS'
701
+ ) {
702
+ if (!outputItem.address) {
703
+ console.error(
704
+ `[Trezor] Output ${i} missing address for script type ${outputItem.script_type}`
705
+ );
706
+ throw new Error(
707
+ `Output ${i} requires an address for script type ${outputItem.script_type}`
708
+ );
709
+ }
710
+ }
711
+
712
+ trezortx.outputs.push(outputItem);
713
+ }
714
+ return trezortx;
715
+ }
716
+
717
+ /**
718
+ * This sign EVM tx.
719
+ *
720
+ * @param index - index of account for path derivation
721
+ * @param tx - ethereum tx object
722
+ * @returns signature object
723
+ */
724
+ public async signEthTransaction({
725
+ tx,
726
+ index,
727
+ coin,
728
+ slip44,
729
+ }: {
730
+ index: string;
731
+ tx: EthereumTransaction | EthereumTransactionEIP1559;
732
+ coin: string;
733
+ slip44: number;
734
+ }) {
735
+ return this.executeWithRetry(async () => {
736
+ // Wait between popups
737
+ await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_POPUPS));
738
+
739
+ // Use dynamic path generation based on actual network parameters
740
+ this.setHdPath(coin, Number(index) || 0, slip44);
741
+
742
+ const response = await TrezorConnect.ethereumSignTransaction({
743
+ path: this.hdPath,
744
+ transaction: tx,
745
+ });
746
+
747
+ if (response.success) {
748
+ return response;
749
+ }
750
+ throw new Error(response.payload.error);
751
+ }, 'signEthTransaction');
752
+ }
753
+
754
+ /**
755
+ * This sign message.
756
+ *
757
+ * @param coin - network symbol. Example: eth, sys, btc
758
+ * @param slip44 - network slip44 number
759
+ * @param message - message to be signed. Example: 'Test message'
760
+ * @param index - index of account for path derivation
761
+ * @returns signature object
762
+ */
763
+
764
+ public async signMessage({
765
+ index,
766
+ message,
767
+ coin,
768
+ slip44,
769
+ address,
770
+ }: {
771
+ address: string;
772
+ coin: string;
773
+ index?: number;
774
+ message?: string;
775
+ slip44: number; // Required, not optional
776
+ }) {
777
+ return this.executeWithRetry(async () => {
778
+ // Wait between popups
779
+ await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_POPUPS));
780
+
781
+ if (isEvmCoin(coin, slip44) && `${index ? index : 0}` && message) {
782
+ return this._signEthPersonalMessage(Number(index), message, address);
783
+ }
784
+ return this._signUtxoPersonalMessage({ coin, index, slip44, message });
785
+ }, 'signMessage');
786
+ }
787
+
788
+ private async _signUtxoPersonalMessage({
789
+ coin,
790
+ index,
791
+ slip44,
792
+ message,
793
+ }: {
794
+ coin: string;
795
+ index?: number;
796
+ slip44: number;
797
+ message?: string;
798
+ }) {
799
+ try {
800
+ // Use dynamic path generation instead of hardcoded switch
801
+ this.setHdPath(coin, index || 0, slip44);
802
+ const { success, payload } = await TrezorConnect.signMessage({
803
+ path: this.hdPath,
804
+ coin: coin,
805
+ message: message,
806
+ });
807
+
808
+ if (success) {
809
+ return { success, payload };
810
+ }
811
+ return { success: false, payload };
812
+ } catch (error) {
813
+ return { error };
814
+ }
815
+ }
816
+
817
+ // For personal_sign, we need to prefix the message:
818
+ private async _signEthPersonalMessage(
819
+ index: number,
820
+ message: string,
821
+ address: string
822
+ ) {
823
+ return new Promise((resolve, reject) => {
824
+ setTimeout(async () => {
825
+ try {
826
+ this.setHdPath('eth', index, 60);
827
+
828
+ TrezorConnect.ethereumSignMessage({
829
+ path: this.hdPath,
830
+ message: stripHexPrefix(message),
831
+ hex: true,
832
+ })
833
+ .then((response: any) => {
834
+ if (response.success) {
835
+ if (
836
+ address &&
837
+ response.payload.address.toLowerCase() !==
838
+ address.toLowerCase()
839
+ ) {
840
+ reject(new Error('signature doesnt match the right address'));
841
+ }
842
+ const signature = `0x${response.payload.signature}`;
843
+ resolve({ signature, success: true });
844
+ } else {
845
+ reject(
846
+ // @ts-ignore
847
+ new Error(response.payload.error || 'Unknown error')
848
+ );
849
+ }
850
+ })
851
+ .catch((e: any) => {
852
+ reject(new Error(e.toString() || 'Unknown error'));
853
+ });
854
+ } catch (error) {
855
+ reject(error);
856
+ }
857
+ // This is necessary to avoid popup collision
858
+ // between the unlock & sign trezor popups
859
+ }, DELAY_BETWEEN_POPUPS);
860
+ });
861
+ }
862
+ private _sanitizeData(data: any): any {
863
+ switch (Object.prototype.toString.call(data)) {
864
+ case '[object Object]': {
865
+ const entries = Object.keys(data).map((k) => [
866
+ k,
867
+ this._sanitizeData(data[k]),
868
+ ]);
869
+ return Object.fromEntries(entries);
870
+ }
871
+
872
+ case '[object Array]':
873
+ return data.map((v: any[]) => this._sanitizeData(v));
874
+
875
+ case '[object BigInt]':
876
+ return data.toString();
877
+
878
+ default:
879
+ return data;
880
+ }
881
+ }
882
+
883
+ private _transformTypedData = <T extends MessageTypes>(
884
+ data: TypedMessage<T>,
885
+ version: SignTypedDataVersion
886
+ ) => {
887
+ const { types, primaryType, domain, message } = this._sanitizeData(data);
888
+
889
+ const domainSeparatorHash = TypedDataUtils.hashStruct(
890
+ 'EIP712Domain',
891
+ this._sanitizeData(domain),
892
+ types,
893
+ version as SignTypedDataVersion.V3 | SignTypedDataVersion.V4
894
+ ).toString('hex');
895
+
896
+ let messageHash: string | null = null;
897
+
898
+ if (primaryType !== 'EIP712Domain') {
899
+ messageHash = TypedDataUtils.hashStruct(
900
+ primaryType as string,
901
+ this._sanitizeData(message),
902
+ types,
903
+ version as SignTypedDataVersion.V3 | SignTypedDataVersion.V4
904
+ ).toString('hex');
905
+ }
906
+
907
+ return {
908
+ domain_separator_hash: domainSeparatorHash,
909
+ message_hash: messageHash,
910
+ ...data,
911
+ };
912
+ };
913
+
914
+ /**
915
+ * EIP-712 Sign Typed Data
916
+ */
917
+ public async signTypedData({
918
+ version,
919
+ address,
920
+ data,
921
+ index,
922
+ }: {
923
+ address: string;
924
+ data: TypedMessage<any> | TypedDataV1;
925
+ index: number;
926
+ version: SignTypedDataVersion;
927
+ }) {
928
+ return this.executeWithRetry(async () => {
929
+ // Wait between popups
930
+ await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_POPUPS));
931
+
932
+ this.setHdPath('eth', index, 60);
933
+ // Use dynamic path generation for ETH (EVM) - typed data is only used for EVM
934
+
935
+ // V1 typed data is not supported by hardware wallets
936
+ if (version === SignTypedDataVersion.V1) {
937
+ throw new Error(
938
+ 'Trezor: V1 typed data signing is not supported. Please use V3 or V4.'
939
+ );
940
+ }
941
+
942
+ const dataWithHashes = this._transformTypedData(
943
+ data as TypedMessage<any>,
944
+ version
945
+ );
946
+
947
+ // set default values for signTypedData
948
+ // Trezor is stricter than @metamask/eth-sig-util in what it accepts
949
+ const {
950
+ types,
951
+ message = {},
952
+ domain = {},
953
+ primaryType,
954
+ // snake_case since Trezor uses Protobuf naming conventions here
955
+ domain_separator_hash, // eslint-disable-line camelcase
956
+ message_hash, // eslint-disable-line camelcase
957
+ } = dataWithHashes;
958
+
959
+ // This is necessary to avoid popup collision
960
+ // between the unlock & sign trezor popups
961
+
962
+ const response = await TrezorConnect.ethereumSignTypedData({
963
+ path: this.hdPath,
964
+ data: {
965
+ types: {
966
+ ...types,
967
+ EIP712Domain: types.EIP712Domain ? types.EIP712Domain : [],
968
+ },
969
+ message,
970
+ domain,
971
+ primaryType: primaryType as any,
972
+ },
973
+ metamask_v4_compat: version === SignTypedDataVersion.V4,
974
+ // Trezor 1 only supports blindly signing hashes
975
+ domain_separator_hash,
976
+ message_hash: message_hash ? message_hash : '',
977
+ });
978
+
979
+ if (response.success) {
980
+ if (address !== response.payload.address) {
981
+ throw new Error('signature doesnt match the right address');
982
+ }
983
+ return response.payload.signature;
984
+ }
985
+ // @ts-ignore
986
+ throw new Error(response.payload.error || 'Unknown error');
987
+ }, 'signTypedData');
988
+ }
989
+
990
+ /**
991
+ * Verify UTXO address by displaying it on the Trezor device
992
+ * @param accountIndex - The account index
993
+ * @param currency - The currency (coin type)
994
+ * @param slip44 - The slip44 value for the network
995
+ * @returns The verified address
996
+ */
997
+ public async verifyUtxoAddress(
998
+ accountIndex: number,
999
+ currency: string,
1000
+ slip44: number
1001
+ ): Promise<string | undefined> {
1002
+ return this.executeWithRetry(async () => {
1003
+ const fullPath = getAddressDerivationPath(
1004
+ currency,
1005
+ slip44,
1006
+ accountIndex,
1007
+ false, // Not a change address
1008
+ 0
1009
+ );
1010
+
1011
+ try {
1012
+ const { payload, success } = await TrezorConnect.getAddress({
1013
+ path: fullPath,
1014
+ coin: currency,
1015
+ showOnTrezor: true, // This displays the address on device for verification
1016
+ });
1017
+ if (success) {
1018
+ return payload.address;
1019
+ }
1020
+ throw new Error('Address verification cancelled by user');
1021
+ } catch (error) {
1022
+ throw error;
1023
+ }
1024
+ }, 'verifyUtxoAddress');
1025
+ }
1026
+
1027
+ /**
1028
+ * Check Trezor status
1029
+ */
1030
+ public getStatus() {
1031
+ return this.hardwareWalletManager
1032
+ .getStatus()
1033
+ .find((s) => s.type === HardwareWalletType.TREZOR);
1034
+ }
1035
+
1036
+ /**
1037
+ * Clean up resources
1038
+ */
1039
+ public async destroy() {
1040
+ try {
1041
+ // First dispose Trezor Connect
1042
+ this.dispose();
1043
+ // Then destroy hardware wallet manager
1044
+ await this.hardwareWalletManager.destroy();
1045
+ this.initialized = false;
1046
+ } catch (error) {
1047
+ console.error('Error destroying Trezor keyring:', error);
1048
+ }
1049
+ }
1050
+ }