@onekeyfe/hardware-cli 1.1.25-alpha.0

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.
package/src/chains.ts ADDED
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Chain Resolver — maps chain identifiers to the correct SDK API calls.
3
+ * Handles derivation path defaults and chain-specific parameter transformations.
4
+ *
5
+ * Reference: developer-portal docs at
6
+ * content/en/hardware-sdk/chains/<chain>/<method>.mdx
7
+ * content/en/hardware-sdk/core-api-guide.mdx (HD path section)
8
+ *
9
+ * Type definitions: packages/core/src/types/api/*.ts
10
+ */
11
+
12
+ import type { CoreApi } from '@onekeyfe/hd-core';
13
+
14
+ // Default BIP44 derivation paths per chain
15
+ // Sources: developer-portal core-api-guide.mdx + chain-specific docs
16
+ const DEFAULT_PATHS: Record<string, string> = {
17
+ evm: "m/44'/60'/0'/0/0", // EIP-44 standard
18
+ btc: "m/84'/0'/0'/0/0", // Native SegWit (bech32). Also: m/44' (legacy), m/49' (nested segwit), m/86' (taproot)
19
+ sol: "m/44'/501'/0'/0'", // Fully hardened per Solana spec
20
+ tron: "m/44'/195'/0'/0/0",
21
+ cosmos: "m/44'/118'/0'/0/0",
22
+ cardano: "m/1852'/1815'/0'/0/0", // Shelley-era
23
+ polkadot: "m/44'/354'/0'/0'/0'",
24
+ aptos: "m/44'/637'/0'/0'/0'", // Fully hardened
25
+ sui: "m/44'/784'/0'/0'/0'", // Fully hardened
26
+ near: "m/44'/397'/0'", // Short path per NEAR spec
27
+ xrp: "m/44'/144'/0'/0/0",
28
+ stellar: "m/44'/148'/0'",
29
+ ton: "m/44'/607'/0'",
30
+ nostr: "m/44'/1237'/0'/0/0",
31
+ filecoin: "m/44'/461'/0'/0/0",
32
+ kaspa: "m/44'/111111'/0'/0/0",
33
+ algo: "m/44'/283'/0'/0/0",
34
+ conflux: "m/44'/503'/0'/0/0",
35
+ nervos: "m/44'/309'/0'/0/0",
36
+ alephium: "m/44'/1234'/0'/0/0",
37
+ neo: "m/44'/888'/0'/0/0",
38
+ starcoin: "m/44'/101010'/0'/0'/0'",
39
+ nem: "m/44'/43'/0'/0'/0'",
40
+ dnx: "m/44'/29538'/0'/0'/0'",
41
+ scdo: "m/44'/541'/0'/0/0",
42
+ benfen: "m/44'/728'/0'/0'/0'",
43
+ nexa: "m/44'/29223'/0'/0/0",
44
+ };
45
+
46
+ // Chain name aliases for fuzzy matching
47
+ const CHAIN_ALIASES: Record<string, string> = {
48
+ ethereum: 'evm',
49
+ eth: 'evm',
50
+ bitcoin: 'btc',
51
+ solana: 'sol',
52
+ dot: 'polkadot',
53
+ ada: 'cardano',
54
+ atom: 'cosmos',
55
+ apt: 'aptos',
56
+ ripple: 'xrp',
57
+ xlm: 'stellar',
58
+ fil: 'filecoin',
59
+ ckb: 'nervos',
60
+ alph: 'alephium',
61
+ cfx: 'conflux',
62
+ stc: 'starcoin',
63
+ xem: 'nem',
64
+ bfc: 'benfen',
65
+ };
66
+
67
+ function resolveChain(chain: string): string {
68
+ const normalized = chain.toLowerCase().trim();
69
+ return CHAIN_ALIASES[normalized] || normalized;
70
+ }
71
+
72
+ function getDefaultPath(chain: string): string {
73
+ const resolved = resolveChain(chain);
74
+ const path = DEFAULT_PATHS[resolved];
75
+ if (!path) {
76
+ throw new Error(
77
+ `Unsupported chain: ${chain}. Supported: ${Object.keys(DEFAULT_PATHS).join(', ')}`
78
+ );
79
+ }
80
+ return path;
81
+ }
82
+
83
+ /**
84
+ * Common params passed to all SDK methods.
85
+ * Reference: packages/core/src/types/api/export.ts (CommonParams)
86
+ */
87
+ export interface CommonCLIParams {
88
+ connectId?: string;
89
+ deviceId?: string;
90
+ passphraseState?: string;
91
+ useEmptyPassphrase?: boolean;
92
+ }
93
+
94
+ export interface GetAddressParams extends CommonCLIParams {
95
+ chain: string;
96
+ path?: string;
97
+ showOnDevice?: boolean;
98
+ }
99
+
100
+ export async function resolveGetAddress(sdk: CoreApi, params: GetAddressParams) {
101
+ const chain = resolveChain(params.chain);
102
+ const path = params.path || getDefaultPath(chain);
103
+ const showOnOneKey = params.showOnDevice ?? true;
104
+ const connectId = params.connectId || '';
105
+ const deviceId = params.deviceId || '';
106
+
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ const chainMethodMap: Record<string, () => Promise<any>> = {
109
+ evm: () => sdk.evmGetAddress(connectId, deviceId, { path, showOnOneKey }),
110
+ btc: () => sdk.btcGetAddress(connectId, deviceId, { path, showOnOneKey, coin: 'btc' }),
111
+ sol: () => sdk.solGetAddress(connectId, deviceId, { path, showOnOneKey }),
112
+ tron: () => sdk.tronGetAddress(connectId, deviceId, { path, showOnOneKey }),
113
+ cosmos: () => sdk.cosmosGetAddress(connectId, deviceId, { path, showOnOneKey }),
114
+ // Cardano requires networkId, protocolMagic, derivationType
115
+ cardano: () =>
116
+ sdk.cardanoGetAddress(connectId, deviceId, {
117
+ addressParameters: { path, addressType: 0 } as any,
118
+ networkId: 1, // mainnet
119
+ protocolMagic: 764824073, // mainnet magic
120
+ derivationType: 1, // Icarus
121
+ showOnOneKey,
122
+ }),
123
+ // Polkadot requires network param
124
+ polkadot: () =>
125
+ sdk.polkadotGetAddress(connectId, deviceId, {
126
+ path,
127
+ prefix: 0,
128
+ network: 'polkadot',
129
+ showOnOneKey,
130
+ }),
131
+ aptos: () => sdk.aptosGetAddress(connectId, deviceId, { path, showOnOneKey }),
132
+ sui: () => sdk.suiGetAddress(connectId, deviceId, { path, showOnOneKey }),
133
+ near: () => sdk.nearGetAddress(connectId, deviceId, { path, showOnOneKey }),
134
+ xrp: () => sdk.xrpGetAddress(connectId, deviceId, { path, showOnOneKey }),
135
+ stellar: () => sdk.stellarGetAddress(connectId, deviceId, { path, showOnOneKey }),
136
+ ton: () => sdk.tonGetAddress(connectId, deviceId, { path, showOnOneKey }),
137
+ filecoin: () => sdk.filecoinGetAddress(connectId, deviceId, { path, showOnOneKey }),
138
+ kaspa: () => sdk.kaspaGetAddress(connectId, deviceId, { path, showOnOneKey, prefix: 'kaspa' }),
139
+ algo: () => sdk.algoGetAddress(connectId, deviceId, { path, showOnOneKey }),
140
+ conflux: () => sdk.confluxGetAddress(connectId, deviceId, { path, showOnOneKey }),
141
+ nervos: () => sdk.nervosGetAddress(connectId, deviceId, { path, showOnOneKey, network: 'ckb' }),
142
+ alephium: () => sdk.alephiumGetAddress(connectId, deviceId, { path, showOnOneKey, group: 0 }),
143
+ neo: () => sdk.neoGetAddress(connectId, deviceId, { path, showOnOneKey }),
144
+ starcoin: () => sdk.starcoinGetAddress(connectId, deviceId, { path, showOnOneKey }),
145
+ nem: () => sdk.nemGetAddress(connectId, deviceId, { path, showOnOneKey, network: 104 }),
146
+ dnx: () => sdk.dnxGetAddress(connectId, deviceId, { path, showOnOneKey }),
147
+ scdo: () => sdk.scdoGetAddress(connectId, deviceId, { path, showOnOneKey }),
148
+ benfen: () => sdk.benfenGetAddress(connectId, deviceId, { path, showOnOneKey }),
149
+ nexa: () => sdk.nexaGetAddress(connectId, deviceId, { path, showOnOneKey, prefix: 'nexa' }),
150
+ };
151
+
152
+ const method = chainMethodMap[chain];
153
+ if (!method) {
154
+ throw new Error(`getAddress not supported for chain: ${chain}`);
155
+ }
156
+
157
+ const result = await method();
158
+ if (result == null) {
159
+ return { success: false, error: 'No response from device', chain, path };
160
+ }
161
+ return { ...(result as object), chain, path };
162
+ }
163
+
164
+ export interface GetPublicKeyParams extends CommonCLIParams {
165
+ chain: string;
166
+ path?: string;
167
+ }
168
+
169
+ export async function resolveGetPublicKey(sdk: CoreApi, params: GetPublicKeyParams) {
170
+ const chain = resolveChain(params.chain);
171
+ const path = params.path || getDefaultPath(chain);
172
+ const connectId = params.connectId || '';
173
+ const deviceId = params.deviceId || '';
174
+
175
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
+ const chainMethodMap: Record<string, () => Promise<any>> = {
177
+ evm: () => sdk.evmGetPublicKey(connectId, deviceId, { path }),
178
+ btc: () => sdk.btcGetPublicKey(connectId, deviceId, { path, coin: 'btc' }),
179
+ aptos: () => sdk.aptosGetPublicKey(connectId, deviceId, { path }),
180
+ cosmos: () => sdk.cosmosGetPublicKey(connectId, deviceId, { path }),
181
+ sui: () => sdk.suiGetPublicKey(connectId, deviceId, { path }),
182
+ starcoin: () => sdk.starcoinGetPublicKey(connectId, deviceId, { path }),
183
+ nostr: () => sdk.nostrGetPublicKey(connectId, deviceId, { path }),
184
+ benfen: () => sdk.benfenGetPublicKey(connectId, deviceId, { path }),
185
+ cardano: () => sdk.cardanoGetPublicKey(connectId, deviceId, { path, derivationType: 1 }),
186
+ };
187
+
188
+ const method = chainMethodMap[chain];
189
+ if (!method) {
190
+ throw new Error(
191
+ `getPublicKey not supported for chain: ${chain}. Supported: ${Object.keys(
192
+ chainMethodMap
193
+ ).join(', ')}`
194
+ );
195
+ }
196
+
197
+ const result = await method();
198
+ return { ...(result as object), chain, path };
199
+ }
200
+
201
+ export interface SignTransactionParams extends CommonCLIParams {
202
+ chain: string;
203
+ path?: string;
204
+ transaction: Record<string, unknown>;
205
+ }
206
+
207
+ export async function resolveSignTransaction(sdk: CoreApi, params: SignTransactionParams) {
208
+ const chain = resolveChain(params.chain);
209
+ const path = params.path || getDefaultPath(chain);
210
+ const connectId = params.connectId || '';
211
+ const deviceId = params.deviceId || '';
212
+ const tx = params.transaction;
213
+
214
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
215
+ const chainMethodMap: Record<string, () => Promise<any>> = {
216
+ // EVM: chainId is inside the transaction object, not top-level
217
+ evm: () => sdk.evmSignTransaction(connectId, deviceId, { path, transaction: tx as any }),
218
+ // BTC: requires inputs/outputs/refTxs/coin format
219
+ btc: () => sdk.btcSignTransaction(connectId, deviceId, { coin: 'btc', ...(tx as any) }),
220
+ sol: () => sdk.solSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
221
+ tron: () => sdk.tronSignTransaction(connectId, deviceId, { path, transaction: tx as any }),
222
+ cosmos: () =>
223
+ sdk.cosmosSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
224
+ aptos: () => sdk.aptosSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
225
+ sui: () => sdk.suiSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
226
+ near: () => sdk.nearSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
227
+ // XRP: implementation reads path + transaction from payload despite type declaration
228
+ // eslint-disable-next-line @typescript-eslint/ban-types
229
+ xrp: () =>
230
+ (sdk.xrpSignTransaction as (...args: unknown[]) => Promise<unknown>)(connectId, deviceId, {
231
+ path,
232
+ transaction: tx,
233
+ }),
234
+ stellar: () =>
235
+ sdk.stellarSignTransaction(connectId, deviceId, {
236
+ path,
237
+ networkPassphrase: tx.networkPassphrase as string,
238
+ transaction: tx.transaction as any,
239
+ }),
240
+ // Polkadot: requires prefix param
241
+ polkadot: () =>
242
+ sdk.polkadotSignTransaction(connectId, deviceId, {
243
+ path,
244
+ rawTx: tx.rawTx as string,
245
+ network: (tx.network as string) || 'polkadot',
246
+ prefix: (tx.prefix as number) ?? 0,
247
+ }),
248
+ filecoin: () =>
249
+ sdk.filecoinSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
250
+ kaspa: () => sdk.kaspaSignTransaction(connectId, deviceId, { ...(tx as any) }),
251
+ algo: () => sdk.algoSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
252
+ conflux: () =>
253
+ sdk.confluxSignTransaction(connectId, deviceId, { path, transaction: tx as any }),
254
+ nervos: () => sdk.nervosSignTransaction(connectId, deviceId, { ...(tx as any) }),
255
+ alephium: () =>
256
+ sdk.alephiumSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
257
+ neo: () => sdk.neoSignTransaction(connectId, deviceId, { path, ...(tx as any) }),
258
+ dnx: () => sdk.dnxSignTransaction(connectId, deviceId, { path, ...(tx as any) }),
259
+ // SCDO: flat params (nonce, gasPrice, gasLimit, to, value, data), not wrapped
260
+ scdo: () => sdk.scdoSignTransaction(connectId, deviceId, { path, ...(tx as any) }),
261
+ benfen: () =>
262
+ sdk.benfenSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
263
+ nexa: () => sdk.nexaSignTransaction(connectId, deviceId, { ...(tx as any) }),
264
+ cardano: () => sdk.cardanoSignTransaction(connectId, deviceId, { ...(tx as any) }),
265
+ starcoin: () =>
266
+ sdk.starcoinSignTransaction(connectId, deviceId, { path, rawTx: tx.rawTx as string }),
267
+ };
268
+
269
+ const method = chainMethodMap[chain];
270
+ if (!method) {
271
+ throw new Error(`signTransaction not supported for chain: ${chain}`);
272
+ }
273
+
274
+ const result = await method();
275
+ return { ...(result as object), chain, path };
276
+ }
277
+
278
+ export interface SignMessageParams extends CommonCLIParams {
279
+ chain: string;
280
+ path?: string;
281
+ message: string;
282
+ }
283
+
284
+ export async function resolveSignMessage(sdk: CoreApi, params: SignMessageParams) {
285
+ const chain = resolveChain(params.chain);
286
+ const path = params.path || getDefaultPath(chain);
287
+ const connectId = params.connectId || '';
288
+ const deviceId = params.deviceId || '';
289
+
290
+ // Most chains use `messageHex` (hex-encoded). CLI accepts either:
291
+ // - Already hex-encoded string (starts with "0x" or matches /^[0-9a-fA-F]+$/)
292
+ // - Plain text string (auto-converted to hex)
293
+ const raw = params.message;
294
+ const isHex = /^(0x)?[0-9a-fA-F]+$/.test(raw);
295
+ const msg = isHex ? raw.replace(/^0x/, '') : Buffer.from(raw, 'utf8').toString('hex');
296
+
297
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
298
+ const chainMethodMap: Record<string, () => Promise<any>> = {
299
+ evm: () => sdk.evmSignMessage(connectId, deviceId, { path, messageHex: msg }),
300
+ // BTC: uses messageHex, not message
301
+ btc: () => sdk.btcSignMessage(connectId, deviceId, { path, messageHex: msg, coin: 'btc' }),
302
+ sol: () => sdk.solSignMessage(connectId, deviceId, { path, messageHex: msg }),
303
+ // Tron: uses messageHex
304
+ tron: () => sdk.tronSignMessage(connectId, deviceId, { path, messageHex: msg }),
305
+ aptos: () =>
306
+ sdk.aptosSignMessage(connectId, deviceId, { path, payload: { message: msg } as any }),
307
+ sui: () => sdk.suiSignMessage(connectId, deviceId, { path, messageHex: msg }),
308
+ // Conflux: uses messageHex
309
+ conflux: () => sdk.confluxSignMessage(connectId, deviceId, { path, messageHex: msg }),
310
+ // Starcoin: uses messageHex
311
+ starcoin: () => sdk.starcoinSignMessage(connectId, deviceId, { path, messageHex: msg }),
312
+ // TON: tonSignMessage is transfer-signing, pass JSON params
313
+ ton: () => {
314
+ const tonParams = JSON.parse(msg);
315
+ return sdk.tonSignMessage(connectId, deviceId, { path, ...tonParams });
316
+ },
317
+ // Nostr: event must be a NostrEvent object (kind, content, tags, created_at)
318
+ nostr: () => {
319
+ const event = JSON.parse(msg);
320
+ return sdk.nostrSignEvent(connectId, deviceId, { path, event });
321
+ },
322
+ // SCDO: uses messageHex
323
+ scdo: () => sdk.scdoSignMessage(connectId, deviceId, { path, messageHex: msg }),
324
+ // Alephium: requires messageType
325
+ alephium: () =>
326
+ sdk.alephiumSignMessage(connectId, deviceId, {
327
+ path,
328
+ messageHex: msg,
329
+ messageType: 'alephium',
330
+ }),
331
+ benfen: () => sdk.benfenSignMessage(connectId, deviceId, { path, messageHex: msg }),
332
+ // Cardano: uses `message` field, requires networkId
333
+ cardano: () =>
334
+ sdk.cardanoSignMessage(connectId, deviceId, {
335
+ path,
336
+ message: msg,
337
+ derivationType: 1,
338
+ networkId: 1,
339
+ }),
340
+ };
341
+
342
+ const method = chainMethodMap[chain];
343
+ if (!method) {
344
+ throw new Error(
345
+ `signMessage not supported for chain: ${chain}. Supported: ${Object.keys(chainMethodMap).join(
346
+ ', '
347
+ )}`
348
+ );
349
+ }
350
+
351
+ const result = await method();
352
+ return { ...(result as object), chain, path };
353
+ }
354
+
355
+ export interface BatchGetAddressParams extends CommonCLIParams {
356
+ bundle: Array<{
357
+ chain: string;
358
+ path?: string;
359
+ showOnDevice?: boolean;
360
+ }>;
361
+ }
362
+
363
+ export async function resolveBatchGetAddress(sdk: CoreApi, params: BatchGetAddressParams) {
364
+ // #13 FIX: Collect per-item results with error handling for partial failures
365
+ const results: Array<Record<string, unknown>> = [];
366
+ for (const item of params.bundle) {
367
+ try {
368
+ const result = await resolveGetAddress(sdk, {
369
+ chain: item.chain,
370
+ path: item.path,
371
+ showOnDevice: item.showOnDevice ?? false,
372
+ connectId: params.connectId,
373
+ deviceId: params.deviceId,
374
+ });
375
+ results.push(result);
376
+ } catch (err) {
377
+ results.push({
378
+ chain: item.chain,
379
+ path: item.path,
380
+ success: false,
381
+ error: err instanceof Error ? err.message : String(err),
382
+ });
383
+ }
384
+ }
385
+ const allSuccess = results.every(r => r.success !== false);
386
+ return { success: allSuccess, addresses: results };
387
+ }