@onekeyfe/hd-core 1.1.2-alpha.0 → 1.1.2-alpha.2

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 (56) hide show
  1. package/dist/api/BaseMethod.d.ts +2 -0
  2. package/dist/api/BaseMethod.d.ts.map +1 -1
  3. package/dist/api/allnetwork/AllNetworkGetAddress.d.ts +4 -28
  4. package/dist/api/allnetwork/AllNetworkGetAddress.d.ts.map +1 -1
  5. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +46 -0
  6. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -0
  7. package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts +8 -0
  8. package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts.map +1 -0
  9. package/dist/api/cosmos/CosmosGetPublicKey.d.ts +1 -1
  10. package/dist/api/cosmos/CosmosGetPublicKey.d.ts.map +1 -1
  11. package/dist/api/evm/EVMGetPublicKey.d.ts +1 -0
  12. package/dist/api/evm/EVMGetPublicKey.d.ts.map +1 -1
  13. package/dist/api/index.d.ts +1 -0
  14. package/dist/api/index.d.ts.map +1 -1
  15. package/dist/core/RequestQueue.d.ts +4 -0
  16. package/dist/core/RequestQueue.d.ts.map +1 -1
  17. package/dist/core/index.d.ts +1 -2
  18. package/dist/core/index.d.ts.map +1 -1
  19. package/dist/device/Device.d.ts +1 -0
  20. package/dist/device/Device.d.ts.map +1 -1
  21. package/dist/events/call.d.ts +9 -0
  22. package/dist/events/call.d.ts.map +1 -1
  23. package/dist/events/core.d.ts +2 -2
  24. package/dist/events/core.d.ts.map +1 -1
  25. package/dist/events/iframe.d.ts +11 -1
  26. package/dist/events/iframe.d.ts.map +1 -1
  27. package/dist/index.d.ts +39 -10
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +272 -104
  30. package/dist/inject.d.ts +3 -0
  31. package/dist/inject.d.ts.map +1 -1
  32. package/dist/types/api/allNetworkGetAddress.d.ts +11 -1
  33. package/dist/types/api/allNetworkGetAddress.d.ts.map +1 -1
  34. package/dist/types/api/evmGetPublicKey.d.ts +1 -0
  35. package/dist/types/api/evmGetPublicKey.d.ts.map +1 -1
  36. package/dist/types/api/index.d.ts +2 -1
  37. package/dist/types/api/index.d.ts.map +1 -1
  38. package/package.json +4 -4
  39. package/src/api/BaseMethod.ts +3 -0
  40. package/src/api/allnetwork/AllNetworkGetAddress.ts +5 -407
  41. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +427 -0
  42. package/src/api/allnetwork/AllNetworkGetAddressByLoop.ts +104 -0
  43. package/src/api/cosmos/CosmosGetPublicKey.ts +1 -1
  44. package/src/api/evm/EVMGetPublicKey.ts +9 -3
  45. package/src/api/index.ts +1 -0
  46. package/src/core/RequestQueue.ts +26 -0
  47. package/src/core/index.ts +28 -2
  48. package/src/device/Device.ts +14 -0
  49. package/src/events/call.ts +10 -0
  50. package/src/events/core.ts +7 -1
  51. package/src/events/iframe.ts +12 -1
  52. package/src/index.ts +2 -1
  53. package/src/inject.ts +47 -0
  54. package/src/types/api/allNetworkGetAddress.ts +17 -1
  55. package/src/types/api/evmGetPublicKey.ts +1 -0
  56. package/src/types/api/index.ts +2 -1
@@ -0,0 +1,427 @@
1
+ import semver from 'semver';
2
+ import { ERRORS, HardwareError, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
+
4
+ import { serializedPath } from '../helpers/pathUtils';
5
+ import { BaseMethod } from '../BaseMethod';
6
+ import { validateParams } from '../helpers/paramsValidator';
7
+ import { CoreApi } from '../../types';
8
+ import type {
9
+ AllNetworkAddressParams,
10
+ INetwork,
11
+ AllNetworkAddress,
12
+ CommonResponseParams,
13
+ } from '../../types/api/allNetworkGetAddress';
14
+ import { PROTO } from '../../constants';
15
+
16
+ import { findMethod } from '../utils';
17
+ import { IFRAME } from '../../events';
18
+ import { getDeviceFirmwareVersion, getMethodVersionRange } from '../../utils';
19
+ import { Device } from '../../device/Device';
20
+ import { UI_REQUEST } from '../../constants/ui-request';
21
+
22
+ const Mainnet = 'mainnet';
23
+
24
+ export type NetworkConfig = {
25
+ methodName: keyof CoreApi;
26
+ getParams?: (baseParams: AllNetworkAddressParams, chainName?: string, methodName?: string) => any;
27
+ dependOnMethodName?: (keyof CoreApi)[];
28
+ };
29
+
30
+ export type INetworkReal = Exclude<INetwork, 'tbtc' | 'bch' | 'doge' | 'ltc' | 'neurai'>;
31
+
32
+ export type NetworkConfigMap = {
33
+ [K in INetworkReal]: NetworkConfig;
34
+ };
35
+
36
+ export const networkAliases: {
37
+ [key: string]: { name: INetworkReal; coin: string };
38
+ } = {
39
+ tbtc: { name: 'btc', coin: 'Testnet' },
40
+ bch: { name: 'btc', coin: 'Bcash' },
41
+ doge: { name: 'btc', coin: 'Dogecoin' },
42
+ ltc: { name: 'btc', coin: 'Litecoin' },
43
+ neurai: { name: 'btc', coin: 'Neurai' },
44
+ };
45
+
46
+ const networkConfigMap: NetworkConfigMap = {
47
+ btc: {
48
+ methodName: 'btcGetPublicKey',
49
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => ({
50
+ coin: chainName,
51
+ ...baseParams,
52
+ }),
53
+ },
54
+ evm: {
55
+ methodName: 'evmGetAddress',
56
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => {
57
+ const { path, showOnOneKey } = baseParams;
58
+ let chainId;
59
+ if (chainName) {
60
+ chainId = parseInt(chainName);
61
+ }
62
+ return {
63
+ chainId,
64
+ path,
65
+ showOnOneKey,
66
+ };
67
+ },
68
+ },
69
+ sol: {
70
+ methodName: 'solGetAddress',
71
+ },
72
+ algo: {
73
+ methodName: 'algoGetAddress',
74
+ },
75
+ near: {
76
+ methodName: 'nearGetAddress',
77
+ },
78
+ stc: {
79
+ methodName: 'starcoinGetAddress',
80
+ },
81
+ cfx: {
82
+ methodName: 'confluxGetAddress',
83
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => {
84
+ const { path, showOnOneKey } = baseParams;
85
+ return {
86
+ chainId: parseInt(chainName ?? '1029'),
87
+ path,
88
+ showOnOneKey,
89
+ };
90
+ },
91
+ },
92
+ tron: {
93
+ methodName: 'tronGetAddress',
94
+ },
95
+ aptos: {
96
+ methodName: 'aptosGetAddress',
97
+ },
98
+ xrp: {
99
+ methodName: 'xrpGetAddress',
100
+ },
101
+ cosmos: {
102
+ methodName: 'cosmosGetPublicKey',
103
+ getParams: (baseParams: AllNetworkAddressParams) => {
104
+ const { path, prefix, showOnOneKey } = baseParams;
105
+ return {
106
+ hrp: prefix,
107
+ path,
108
+ showOnOneKey,
109
+ };
110
+ },
111
+ },
112
+ ada: {
113
+ methodName: 'cardanoGetAddress',
114
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => {
115
+ const { path, showOnOneKey } = baseParams;
116
+
117
+ const addressPath =
118
+ typeof path === 'string' ? `${path}/0/0` : serializedPath([...path, 0, 0]);
119
+ const stakingPath =
120
+ typeof path === 'string' ? `${path}/2/0` : serializedPath([...path, 2, 0]);
121
+
122
+ let networkId = 1;
123
+ if (chainName) {
124
+ networkId = chainName === Mainnet ? 1 : 0;
125
+ }
126
+
127
+ return {
128
+ addressParameters: {
129
+ addressType: PROTO.CardanoAddressType.BASE,
130
+ path: addressPath,
131
+ stakingPath,
132
+ },
133
+ protocolMagic: 764824073,
134
+ networkId,
135
+ derivationType: PROTO.CardanoDerivationType.ICARUS,
136
+ showOnOneKey,
137
+ address: '',
138
+ isCheck: false,
139
+ };
140
+ },
141
+ },
142
+ sui: {
143
+ methodName: 'suiGetAddress',
144
+ },
145
+ benfen: {
146
+ methodName: 'benfenGetAddress',
147
+ },
148
+ fil: {
149
+ methodName: 'filecoinGetAddress',
150
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => {
151
+ const { path, showOnOneKey } = baseParams;
152
+ let isTestnet = false;
153
+ if (chainName) {
154
+ isTestnet = chainName !== Mainnet;
155
+ }
156
+ return {
157
+ isTestnet,
158
+ path,
159
+ showOnOneKey,
160
+ };
161
+ },
162
+ },
163
+ dot: {
164
+ methodName: 'polkadotGetAddress',
165
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => {
166
+ const { path, prefix, showOnOneKey } = baseParams;
167
+ if (!prefix || !chainName) {
168
+ throw new Error('Invalid params');
169
+ }
170
+ return {
171
+ prefix: parseInt(prefix),
172
+ network: chainName,
173
+ path,
174
+ showOnOneKey,
175
+ };
176
+ },
177
+ },
178
+ kaspa: {
179
+ methodName: 'kaspaGetAddress',
180
+ getParams: (baseParams: AllNetworkAddressParams) => {
181
+ const { path, prefix, showOnOneKey, useTweak } = baseParams;
182
+ return {
183
+ scheme: 'schnorr',
184
+ prefix,
185
+ path,
186
+ showOnOneKey,
187
+ useTweak,
188
+ };
189
+ },
190
+ },
191
+ nexa: {
192
+ methodName: 'nexaGetAddress',
193
+ getParams: (baseParams: AllNetworkAddressParams) => {
194
+ const { path, prefix, showOnOneKey } = baseParams;
195
+ return {
196
+ scheme: 'Schnorr',
197
+ prefix,
198
+ path,
199
+ showOnOneKey,
200
+ };
201
+ },
202
+ },
203
+ dynex: {
204
+ methodName: 'dnxGetAddress',
205
+ },
206
+ nervos: {
207
+ methodName: 'nervosGetAddress',
208
+ getParams: (baseParams: AllNetworkAddressParams, chainName?: string) => {
209
+ const { path, showOnOneKey } = baseParams;
210
+ return {
211
+ network: chainName,
212
+ path,
213
+ showOnOneKey,
214
+ };
215
+ },
216
+ },
217
+ scdo: {
218
+ methodName: 'scdoGetAddress',
219
+ },
220
+ ton: {
221
+ methodName: 'tonGetAddress',
222
+ },
223
+ alph: {
224
+ methodName: 'alephiumGetAddress',
225
+ },
226
+ nostr: {
227
+ methodName: 'nostrGetPublicKey',
228
+ },
229
+ neo: {
230
+ methodName: 'neoGetAddress',
231
+ },
232
+ };
233
+
234
+ type MethodParams = {
235
+ methodName: keyof CoreApi;
236
+ params: Parameters<CoreApi[keyof CoreApi]>[0];
237
+ _originRequestParams: AllNetworkAddressParams;
238
+ _originalIndex: number;
239
+ };
240
+
241
+ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
242
+ {
243
+ address_n: number[];
244
+ show_display: boolean;
245
+ network: string;
246
+ chain_name?: string;
247
+ }[]
248
+ > {
249
+ init() {
250
+ this.checkDeviceId = true;
251
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
252
+
253
+ // check payload
254
+ validateParams(this.payload, [{ name: 'bundle', type: 'array' }]);
255
+
256
+ // check bundle
257
+ this.payload?.bundle?.forEach((batch: AllNetworkAddressParams) => {
258
+ validateParams(batch, [
259
+ { name: 'path', required: true },
260
+ { name: 'network', type: 'string', required: true },
261
+ { name: 'chainName', type: 'string' },
262
+ { name: 'showOnOneKey', type: 'boolean' },
263
+ ]);
264
+ });
265
+ }
266
+
267
+ generateMethodName({
268
+ network,
269
+ payload,
270
+ originalIndex,
271
+ }: {
272
+ network: INetwork;
273
+ payload: AllNetworkAddressParams;
274
+ originalIndex: number;
275
+ }): MethodParams {
276
+ const { name: networkName, coin } = networkAliases[network] || {
277
+ name: network,
278
+ coin: payload?.chainName,
279
+ };
280
+ const config = networkConfigMap[networkName];
281
+ if (!config) {
282
+ throw new Error(`Unsupported network: ${network}`);
283
+ }
284
+
285
+ return {
286
+ methodName: config.methodName,
287
+ params: {
288
+ ...(config?.getParams?.(payload, coin, config.methodName) ?? payload),
289
+ originPayload: payload,
290
+ },
291
+ _originRequestParams: payload,
292
+ _originalIndex: originalIndex,
293
+ };
294
+ }
295
+
296
+ async callMethod(
297
+ methodName: keyof CoreApi,
298
+ params: any & {
299
+ bundle: (any & { _originRequestParams: CommonResponseParams })[];
300
+ }
301
+ ) {
302
+ const method: BaseMethod = findMethod({
303
+ event: IFRAME.CALL,
304
+ type: IFRAME.CALL,
305
+ payload: {
306
+ connectId: this.payload.connectId,
307
+ deviceId: this.payload.deviceId,
308
+ method: methodName,
309
+ ...params,
310
+ },
311
+ });
312
+
313
+ method.connector = this.connector;
314
+ method.postMessage = this.postMessage;
315
+
316
+ let result: AllNetworkAddress[];
317
+ try {
318
+ method.init();
319
+ method.setDevice?.(this.device);
320
+
321
+ preCheckDeviceSupport(this.device, method);
322
+
323
+ const response = await method.run();
324
+
325
+ if (!Array.isArray(response) || response.length === 0) {
326
+ throw new Error('No response');
327
+ }
328
+
329
+ result = response.map((item, index) => ({
330
+ ...params.bundle[index]._originRequestParams,
331
+ success: true,
332
+ payload: item,
333
+ }));
334
+ } catch (e: any) {
335
+ const error = handleSkippableHardwareError(e, this.device, method);
336
+
337
+ if (error) {
338
+ result = params.bundle.map((item: { _originRequestParams: any }) => ({
339
+ ...item._originRequestParams,
340
+ success: false,
341
+ payload: {
342
+ error: error.message,
343
+ code: error.errorCode,
344
+ params: error.params,
345
+ connectId: method.connectId,
346
+ deviceId: method.deviceId,
347
+ },
348
+ }));
349
+ } else {
350
+ throw e;
351
+ }
352
+ }
353
+
354
+ return result;
355
+ }
356
+
357
+ abstract getAllNetworkAddress(): Promise<AllNetworkAddress[]>;
358
+
359
+ async run() {
360
+ return Promise.resolve(this.getAllNetworkAddress());
361
+ }
362
+ }
363
+
364
+ /**
365
+ * @experiment Check if the device supports the method
366
+ * @param device
367
+ * @param method BaseMethod
368
+ */
369
+ function preCheckDeviceSupport(device: Device, method: BaseMethod) {
370
+ const versionRange = getMethodVersionRange(
371
+ device.features,
372
+ type => method.getVersionRange()[type]
373
+ );
374
+ const currentVersion = getDeviceFirmwareVersion(device.features).join('.');
375
+
376
+ if (
377
+ versionRange &&
378
+ semver.valid(versionRange.min) &&
379
+ semver.lt(currentVersion, versionRange.min)
380
+ ) {
381
+ throw ERRORS.createNeedUpgradeFirmwareHardwareError(currentVersion, versionRange.min);
382
+ } else if (method.strictCheckDeviceSupport && !versionRange) {
383
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod);
384
+ }
385
+ }
386
+
387
+ function handleSkippableHardwareError(
388
+ e: any,
389
+ device: Device,
390
+ method: BaseMethod
391
+ ): HardwareError | undefined {
392
+ let error: HardwareError | undefined;
393
+
394
+ if (e instanceof HardwareError && e.errorCode !== HardwareErrorCode.RuntimeError) {
395
+ const { errorCode } = e;
396
+ if (errorCode === HardwareErrorCode.CallMethodInvalidParameter) {
397
+ error = e;
398
+ } else if (errorCode === HardwareErrorCode.CallMethodNeedUpgradeFirmware) {
399
+ error = e;
400
+ } else if (errorCode === HardwareErrorCode.DeviceNotSupportMethod) {
401
+ error = e;
402
+ }
403
+ } else if (e.message?.includes('Failure_UnexpectedMessage')) {
404
+ const versionRange = getMethodVersionRange(
405
+ device.features,
406
+ type => method.getVersionRange()[type]
407
+ );
408
+ const currentVersion = getDeviceFirmwareVersion(device.features).join('.');
409
+
410
+ if (
411
+ versionRange &&
412
+ semver.valid(versionRange.min) &&
413
+ semver.lt(currentVersion, versionRange.min)
414
+ ) {
415
+ error = ERRORS.createNeedUpgradeFirmwareHardwareError(currentVersion, versionRange.min);
416
+ } else {
417
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod, e.message);
418
+ }
419
+ } else if (
420
+ e.message?.toLowerCase()?.includes('forbidden key path') ||
421
+ e.message?.toLowerCase()?.includes('invalid path')
422
+ ) {
423
+ error = ERRORS.TypedError(HardwareErrorCode.CallMethodInvalidParameter, e.message);
424
+ }
425
+
426
+ return error;
427
+ }
@@ -0,0 +1,104 @@
1
+ import { createDeferred } from '@onekeyfe/hd-shared';
2
+ import type {
3
+ AllNetworkAddress,
4
+ AllNetworkGetAddressParamsByLoop,
5
+ } from '../../types/api/allNetworkGetAddress';
6
+
7
+ import { IFRAME } from '../../events';
8
+ import AllNetworkGetAddressBase from './AllNetworkGetAddressBase';
9
+
10
+ export default class AllNetworkGetAddressByLoop extends AllNetworkGetAddressBase {
11
+ async getAllNetworkAddress() {
12
+ const { callbackId, callbackIdFinish } = this.payload as AllNetworkGetAddressParamsByLoop;
13
+ if (!callbackId) {
14
+ throw new Error('callbackId is required');
15
+ }
16
+ if (!callbackIdFinish) {
17
+ throw new Error('callbackIdFinish is required');
18
+ }
19
+
20
+ const bundle = this.payload.bundle || [this.payload];
21
+
22
+ // process callbacks in background
23
+ const callbackPromise = this.processCallbacksInBackground(bundle, callbackId, callbackIdFinish);
24
+ this.device.pendingCallbackPromise = createDeferred(callbackPromise);
25
+
26
+ // register to context for scheduling management
27
+ if (this.context && this.payload.connectId) {
28
+ this.context.registerCallbackTask(this.payload.connectId, this.device.pendingCallbackPromise);
29
+ }
30
+
31
+ // return empty array immediately
32
+ return Promise.resolve([]);
33
+ }
34
+
35
+ private async processCallbacksInBackground(
36
+ bundle: any[],
37
+ callbackId: string,
38
+ callbackIdFinish: string
39
+ ): Promise<void> {
40
+ try {
41
+ const allResults: AllNetworkAddress[] = [];
42
+
43
+ for (let i = 0; i < bundle.length; i++) {
44
+ const item = bundle[i];
45
+
46
+ try {
47
+ const methodParams = this.generateMethodName({
48
+ network: item.network,
49
+ payload: item,
50
+ originalIndex: i,
51
+ });
52
+
53
+ const singleMethodParams = {
54
+ bundle: [methodParams.params],
55
+ };
56
+
57
+ const response = await this.callMethod(methodParams.methodName, singleMethodParams);
58
+
59
+ const singleResult = {
60
+ ...item,
61
+ ...response[0],
62
+ };
63
+ allResults.push(singleResult);
64
+
65
+ this.sendItemCallback(callbackId, singleResult, null, i);
66
+ } catch (error: any) {
67
+ this.sendItemCallback(callbackId, null, error, i);
68
+ // continue to process other items, do not throw error
69
+ console.error(`Error processing item ${i}:`, error);
70
+ }
71
+ }
72
+
73
+ this.sendFinishCallback(callbackIdFinish, allResults);
74
+ } finally {
75
+ this.context?.cancelCallbackTasks(this.payload.connectId);
76
+ }
77
+ }
78
+
79
+ private sendFinishCallback(callbackId: string, data: AllNetworkAddress[]) {
80
+ this.postMessage({
81
+ event: IFRAME.CALLBACK,
82
+ type: IFRAME.CALLBACK,
83
+ payload: {
84
+ callbackId,
85
+ data,
86
+ },
87
+ });
88
+ }
89
+
90
+ private sendItemCallback(callbackId: string, data: any, error: any, itemIndex: number) {
91
+ this.postMessage({
92
+ event: IFRAME.CALLBACK,
93
+ type: IFRAME.CALLBACK,
94
+ payload: {
95
+ callbackId,
96
+ data: {
97
+ ...data,
98
+ index: itemIndex,
99
+ },
100
+ error: error ? { message: error.message, code: error.code } : null,
101
+ },
102
+ });
103
+ }
104
+ }
@@ -2,7 +2,7 @@ import { UI_REQUEST } from '../../constants/ui-request';
2
2
  import { serializedPath, validatePath } from '../helpers/pathUtils';
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
  import { validateParams, validateResult } from '../helpers/paramsValidator';
5
- import { CosmosAddress, CosmosGetPublicKeyParams } from '../../types';
5
+ import type { CosmosAddress, CosmosGetPublicKeyParams } from '../../types';
6
6
  import { batchGetPublickeys } from '../helpers/batchGetPublickeys';
7
7
 
8
8
  export default class CosmosGetPublicKey extends BaseMethod<any> {
@@ -13,6 +13,8 @@ import { batchGetPublickeys } from '../helpers/batchGetPublickeys';
13
13
  export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneKey[]> {
14
14
  hasBundle = false;
15
15
 
16
+ confirmShowOnOneKey = false;
17
+
16
18
  useBatch = false;
17
19
 
18
20
  init() {
@@ -20,9 +22,13 @@ export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneK
20
22
  this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
21
23
 
22
24
  this.hasBundle = !!this.payload?.bundle;
23
- this.useBatch = this.payload?.bundle?.every(
24
- (item: EVMGetPublicKeyParams) => item.showOnOneKey !== true
25
+
26
+ this.confirmShowOnOneKey = this.payload?.bundle?.some(
27
+ (item: EVMGetPublicKeyParams) => !!item.showOnOneKey
25
28
  );
29
+
30
+ this.useBatch = !this.confirmShowOnOneKey && this.hasBundle && this.payload.useBatch;
31
+
26
32
  const payload = this.hasBundle ? this.payload : { bundle: [this.payload] };
27
33
 
28
34
  // check payload
@@ -66,7 +72,7 @@ export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneK
66
72
  async run() {
67
73
  const responses: EVMPublicKey[] = [];
68
74
 
69
- if (this.useBatch && this.hasBundle && supportBatchPublicKey(this.device?.features)) {
75
+ if (this.useBatch && supportBatchPublicKey(this.device?.features)) {
70
76
  try {
71
77
  const res = await batchGetPublickeys(this.device, this.params, 'secp256k1', 60, {
72
78
  includeNode: false,
package/src/api/index.ts CHANGED
@@ -41,6 +41,7 @@ export { default as promptWebDeviceAccess } from './PromptWebDeviceAccess';
41
41
  export { default as cipherKeyValue } from './CipherKeyValue';
42
42
 
43
43
  export { default as allNetworkGetAddress } from './allnetwork/AllNetworkGetAddress';
44
+ export { default as allNetworkGetAddressByLoop } from './allnetwork/AllNetworkGetAddressByLoop';
44
45
 
45
46
  export { default as btcGetAddress } from './btc/BTCGetAddress';
46
47
  export { default as btcGetPublicKey } from './btc/BTCGetPublicKey';
@@ -13,6 +13,8 @@ export type RequestTask = {
13
13
  export default class RequestQueue {
14
14
  private requestQueue = new Map<number, RequestTask>();
15
15
 
16
+ private pendingCallbackTasks = new Map<string, Deferred<void>>();
17
+
16
18
  // 生成唯一请求ID
17
19
  public generateRequestId = (method?: BaseMethod) => {
18
20
  if (method && method.responseID != null) {
@@ -104,4 +106,28 @@ export default class RequestQueue {
104
106
  public releaseTask(requestId: number) {
105
107
  this.requestQueue.delete(requestId);
106
108
  }
109
+
110
+ public registerPendingCallbackTask(connectId: string, callbackPromise: Deferred<void>) {
111
+ this.pendingCallbackTasks.set(connectId, callbackPromise);
112
+
113
+ callbackPromise.promise.finally(() => {
114
+ Log.debug(`Callback task completed for connectId: ${connectId}`);
115
+ this.pendingCallbackTasks.delete(connectId);
116
+ });
117
+ }
118
+
119
+ public async waitForPendingCallbackTasks(connectId: string): Promise<void> {
120
+ const pendingTask = this.pendingCallbackTasks.get(connectId);
121
+ if (pendingTask) {
122
+ Log.debug(`Waiting for pending callback task to complete for connectId: ${connectId}`);
123
+ await pendingTask.promise;
124
+ }
125
+ }
126
+
127
+ public cancelCallbackTasks(connectId: string) {
128
+ const pendingTask = this.pendingCallbackTasks.get(connectId);
129
+ if (pendingTask) {
130
+ pendingTask.resolve();
131
+ }
132
+ }
107
133
  }
package/src/core/index.ts CHANGED
@@ -52,7 +52,7 @@ import { getSynchronize } from '../utils/getSynchronize';
52
52
 
53
53
  const Log = getLogger(LoggerNames.Core);
54
54
 
55
- type CoreContext = ReturnType<Core['getCoreContext']>;
55
+ export type CoreContext = ReturnType<Core['getCoreContext']>;
56
56
 
57
57
  function hasDeriveCardano(method: BaseMethod): boolean {
58
58
  if (
@@ -195,6 +195,11 @@ const onCallDevice = async (
195
195
  DevicePool.clearDeviceCache(method.payload.connectId);
196
196
  }
197
197
 
198
+ // 等待之前的 callback 任务完成(确保设备不会并发调用)
199
+ if (method.connectId) {
200
+ await context.waitForCallbackTasks(method.connectId);
201
+ }
202
+
198
203
  await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
199
204
 
200
205
  const task = requestQueue.createTask(method);
@@ -225,6 +230,7 @@ const onCallDevice = async (
225
230
 
226
231
  Log.debug('Call API - setDevice: ', device.mainId);
227
232
  method.setDevice?.(device);
233
+ method.context = context;
228
234
 
229
235
  device.on(DEVICE.PIN, onDevicePinHandler);
230
236
  device.on(DEVICE.BUTTON, onDeviceButtonHandler);
@@ -240,6 +246,10 @@ const onCallDevice = async (
240
246
  );
241
247
 
242
248
  try {
249
+ if (method.connectId) {
250
+ await context.waitForCallbackTasks(method.connectId);
251
+ }
252
+
243
253
  await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
244
254
 
245
255
  const inner = async (): Promise<void> => {
@@ -710,6 +720,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
710
720
  // }
711
721
  // setPrePendingCallPromise(device?.interruptionFromUser());
712
722
  // requestQueue.abortRequestsByConnectId(connectId);
723
+
724
+ // cancel callback tasks
725
+ requestQueue.cancelCallbackTasks(connectId);
726
+
713
727
  const requestIds = requestQueue.getRequestTasksId();
714
728
  Log.debug(
715
729
  `Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(
@@ -947,7 +961,7 @@ const removeUiPromise = (promise: Deferred<any>) => {
947
961
  export default class Core extends EventEmitter {
948
962
  private requestQueue = new RequestQueue();
949
963
 
950
- // 上一个请求的 promise 完成,后续需要清理的工作,需要在下一次请求前完成
964
+ // background task
951
965
  private prePendingCallPromise: Promise<void> | undefined;
952
966
 
953
967
  private methodSynchronize = getSynchronize();
@@ -960,6 +974,13 @@ export default class Core extends EventEmitter {
960
974
  setPrePendingCallPromise: (promise: Promise<void> | undefined) => {
961
975
  this.prePendingCallPromise = promise;
962
976
  },
977
+ // callback 任务管理
978
+ registerCallbackTask: (connectId: string, callbackPromise: Deferred<any>) => {
979
+ this.requestQueue.registerPendingCallbackTask(connectId, callbackPromise);
980
+ },
981
+ waitForCallbackTasks: (connectId: string) =>
982
+ this.requestQueue.waitForPendingCallbackTasks(connectId),
983
+ cancelCallbackTasks: (connectId: string) => this.requestQueue.cancelCallbackTasks(connectId),
963
984
  };
964
985
  }
965
986
 
@@ -1010,6 +1031,11 @@ export default class Core extends EventEmitter {
1010
1031
  cancel(this.getCoreContext(), message.payload.connectId);
1011
1032
  break;
1012
1033
  }
1034
+ case IFRAME.CALLBACK: {
1035
+ Log.log('callback message: ', message);
1036
+ postMessage(message);
1037
+ break;
1038
+ }
1013
1039
  default:
1014
1040
  break;
1015
1041
  }