@swapkit/wallet-hardware 4.8.0 → 4.8.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 (38) hide show
  1. package/dist/keepkey/index.cjs.map +2 -2
  2. package/dist/keepkey/index.js.map +2 -2
  3. package/dist/types/keepkey/chains/utxo.d.ts +5 -379
  4. package/dist/types/keepkey/chains/utxo.d.ts.map +1 -1
  5. package/package.json +5 -7
  6. package/src/index.ts +1 -0
  7. package/src/keepkey/chains/cosmos.ts +69 -0
  8. package/src/keepkey/chains/evm.ts +141 -0
  9. package/src/keepkey/chains/mayachain.ts +98 -0
  10. package/src/keepkey/chains/ripple.ts +88 -0
  11. package/src/keepkey/chains/thorchain.ts +93 -0
  12. package/src/keepkey/chains/utxo.ts +364 -0
  13. package/src/keepkey/coins.ts +67 -0
  14. package/src/keepkey/index.ts +174 -0
  15. package/src/ledger/clients/cosmos.ts +84 -0
  16. package/src/ledger/clients/evm.ts +186 -0
  17. package/src/ledger/clients/near.ts +63 -0
  18. package/src/ledger/clients/sui.ts +130 -0
  19. package/src/ledger/clients/thorchain/common.ts +93 -0
  20. package/src/ledger/clients/thorchain/helpers.ts +120 -0
  21. package/src/ledger/clients/thorchain/index.ts +87 -0
  22. package/src/ledger/clients/thorchain/lib.ts +258 -0
  23. package/src/ledger/clients/thorchain/utils.ts +69 -0
  24. package/src/ledger/clients/tron.ts +85 -0
  25. package/src/ledger/clients/utxo-legacy-adapter.ts +71 -0
  26. package/src/ledger/clients/utxo-psbt.ts +145 -0
  27. package/src/ledger/clients/utxo.ts +359 -0
  28. package/src/ledger/clients/xrp.ts +50 -0
  29. package/src/ledger/cosmosTypes.ts +98 -0
  30. package/src/ledger/helpers/getLedgerAddress.ts +76 -0
  31. package/src/ledger/helpers/getLedgerClient.ts +124 -0
  32. package/src/ledger/helpers/getLedgerTransport.ts +102 -0
  33. package/src/ledger/helpers/index.ts +3 -0
  34. package/src/ledger/index.ts +546 -0
  35. package/src/ledger/interfaces/CosmosLedgerInterface.ts +54 -0
  36. package/src/ledger/types.ts +42 -0
  37. package/src/trezor/evmSigner.ts +210 -0
  38. package/src/trezor/index.ts +847 -0
@@ -0,0 +1,130 @@
1
+ import type Sui from "@ledgerhq/hw-app-sui";
2
+ import {
3
+ Chain,
4
+ type DerivationPathArray,
5
+ derivationPathToString,
6
+ NetworkDerivationPath,
7
+ SwapKitError,
8
+ } from "@swapkit/helpers";
9
+
10
+ import { getLedgerTransport } from "../helpers/getLedgerTransport";
11
+
12
+ export class SuiLedgerInterface {
13
+ derivationPath: string;
14
+ ledgerApp: InstanceType<typeof Sui> | null = null;
15
+ address: string | null = null;
16
+ publicKey: Uint8Array | null = null;
17
+
18
+ constructor(derivationPath?: DerivationPathArray | string) {
19
+ this.derivationPath =
20
+ typeof derivationPath === "string"
21
+ ? derivationPath
22
+ : derivationPathToString(derivationPath || NetworkDerivationPath[Chain.Sui]);
23
+ }
24
+
25
+ /**
26
+ * Transform derivation path for SUI Ledger format.
27
+ * SUI Ledger expects: 44'/784'/0'/0'/0' (all hardened, no 'm/' prefix)
28
+ * Standard format is: m/44'/784'/0'/0/0 (only first 3 hardened, has 'm/' prefix)
29
+ */
30
+ private getLedgerPath(): string {
31
+ return this.derivationPath
32
+ .replace(/^m\//, "") // Remove 'm/' prefix
33
+ .replace(/\/(\d+)\/(\d+)$/, "/$1'/$2'"); // Make last two components hardened
34
+ }
35
+
36
+ private async createTransportAndLedger() {
37
+ if (this.ledgerApp) return;
38
+
39
+ const transport = await getLedgerTransport();
40
+ const SuiApp = (await import("@ledgerhq/hw-app-sui")).default;
41
+ this.ledgerApp = new SuiApp(transport);
42
+ }
43
+
44
+ async connect(): Promise<string> {
45
+ await this.createTransportAndLedger();
46
+
47
+ if (!this.ledgerApp) {
48
+ throw new SwapKitError("wallet_ledger_transport_error");
49
+ }
50
+
51
+ const ledgerPath = this.getLedgerPath();
52
+ const result = await this.ledgerApp.getPublicKey(ledgerPath);
53
+
54
+ if (!result?.publicKey) {
55
+ throw new SwapKitError("wallet_ledger_failed_to_get_address");
56
+ }
57
+
58
+ this.publicKey = result.publicKey;
59
+ this.address = `0x${Buffer.from(result.address).toString("hex")}`;
60
+
61
+ return this.address;
62
+ }
63
+
64
+ toSuiAddress(): string {
65
+ if (!this.address) {
66
+ throw new SwapKitError("wallet_ledger_failed_to_get_address");
67
+ }
68
+ return this.address;
69
+ }
70
+
71
+ async getAddress(): Promise<string> {
72
+ if (this.address) return this.address;
73
+ return await this.connect();
74
+ }
75
+
76
+ async signTransaction(
77
+ input: Uint8Array | { transaction: Uint8Array },
78
+ ): Promise<{ bytes: string; signature: string }> {
79
+ const txBytes = input instanceof Uint8Array ? input : input.transaction;
80
+ await this.createTransportAndLedger();
81
+
82
+ if (!this.ledgerApp) {
83
+ throw new SwapKitError("wallet_ledger_transport_error");
84
+ }
85
+
86
+ if (!this.publicKey) {
87
+ throw new SwapKitError("wallet_ledger_failed_to_get_address");
88
+ }
89
+
90
+ try {
91
+ const ledgerPath = this.getLedgerPath();
92
+
93
+ // SUI intent message format for TransactionData:
94
+ // [intent_scope=0 (TransactionData), intent_version=0, app_id=0] + transaction_bytes
95
+ // The Ledger SUI app expects the INTENT MESSAGE, not raw transaction bytes
96
+ const intentMessage = new Uint8Array(3 + txBytes.length);
97
+ intentMessage[0] = 0; // IntentScope: TransactionData
98
+ intentMessage[1] = 0; // IntentVersion: V0
99
+ intentMessage[2] = 0; // AppId: Sui
100
+ intentMessage.set(txBytes, 3);
101
+
102
+ const result = await this.ledgerApp.signTransaction(ledgerPath, intentMessage);
103
+
104
+ if (!result?.signature) {
105
+ throw new SwapKitError("wallet_ledger_signing_error");
106
+ }
107
+
108
+ // SUI signature format: [scheme_flag (1 byte)] + [signature (64 bytes)] + [public_key (32 bytes)]
109
+ // Scheme flag 0x00 = Ed25519
110
+ const pubKey = this.publicKey.length === 33 ? this.publicKey.slice(1) : this.publicKey;
111
+ if (pubKey.length !== 32) {
112
+ throw new SwapKitError("wallet_ledger_signing_error", { error: "Invalid public key length" });
113
+ }
114
+
115
+ const serializedSignature = new Uint8Array(1 + 64 + 32);
116
+ serializedSignature[0] = 0x00; // Ed25519 scheme flag
117
+ serializedSignature.set(result.signature, 1);
118
+ serializedSignature.set(pubKey, 65);
119
+
120
+ const signatureBase64 = Buffer.from(serializedSignature).toString("base64");
121
+ const bytesBase64 = Buffer.from(txBytes).toString("base64");
122
+
123
+ return { bytes: bytesBase64, signature: signatureBase64 };
124
+ } catch (error) {
125
+ throw new SwapKitError("wallet_ledger_signing_error", { error });
126
+ }
127
+ }
128
+ }
129
+
130
+ export const SuiLedger = (derivationPath?: DerivationPathArray) => new SuiLedgerInterface(derivationPath);
@@ -0,0 +1,93 @@
1
+ export const CLA = 0x55;
2
+ export const CHUNK_SIZE = 250;
3
+ export const APP_KEY = "CSM";
4
+
5
+ export const INS = {
6
+ GET_ADDR_SECP256K1: 0x04,
7
+ GET_VERSION: 0x00,
8
+ INS_PUBLIC_KEY_SECP256K1: 0x01, // Obsolete
9
+ SIGN_SECP256K1: 0x02,
10
+ };
11
+
12
+ export const PAYLOAD_TYPE = { ADD: 0x01, INIT: 0x00, LAST: 0x02 };
13
+
14
+ export const P1_VALUES = { ONLY_RETRIEVE: 0x00, SHOW_ADDRESS_IN_DEVICE: 0x01 };
15
+
16
+ export const P2_VALUES = { JSON: 0x0 };
17
+
18
+ export const ERROR_CODE = { NoError: 0x9000 };
19
+
20
+ const ERROR_DESCRIPTION: any = {
21
+ 1: "U2F: Unknown",
22
+ 2: "U2F: Bad request",
23
+ 3: "U2F: Configuration unsupported",
24
+ 4: "U2F: Device Ineligible",
25
+ 5: "U2F: Timeout",
26
+ 14: "Timeout",
27
+ 25600: "Execution Error",
28
+ 26368: "Wrong Length",
29
+ 26626: "Error deriving keys",
30
+ 27010: "Empty Buffer",
31
+ 27011: "Output buffer too small",
32
+ 27012: "Data is invalid",
33
+ 27013: "Conditions not satisfied",
34
+ 27014: "Transaction rejected",
35
+ 27264: "Bad key handle",
36
+ 27392: "Invalid P1/P2",
37
+ 27904: "Instruction not supported",
38
+ 28160: "App does not seem to be open",
39
+ 28416: "Unknown error",
40
+ 28417: "Sign/verify error",
41
+ 36864: "No errors",
42
+ 36865: "Device is busy",
43
+ };
44
+
45
+ export function errorCodeToString(statusCode: number) {
46
+ if (statusCode in ERROR_DESCRIPTION) return ERROR_DESCRIPTION[statusCode];
47
+ return `Unknown Status Code: ${statusCode}`;
48
+ }
49
+
50
+ function isDict(v: any) {
51
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Date);
52
+ }
53
+
54
+ export function processErrorResponse(response: any) {
55
+ if (response) {
56
+ if (isDict(response)) {
57
+ if (Object.hasOwn(response, "statusCode")) {
58
+ return { error_message: errorCodeToString(response.statusCode), return_code: response.statusCode };
59
+ }
60
+
61
+ if (Object.hasOwn(response, "return_code") && Object.hasOwn(response, "error_message")) {
62
+ return response;
63
+ }
64
+ }
65
+ return { error_message: response.toString(), return_code: 0xffff };
66
+ }
67
+
68
+ return { error_message: response.toString(), return_code: 0xffff };
69
+ }
70
+
71
+ export function getVersion(transport: any) {
72
+ return transport.send(CLA, INS.GET_VERSION, 0, 0).then((response: any) => {
73
+ const errorCodeData = response.slice(-2);
74
+ const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
75
+
76
+ let targetId = 0;
77
+ if (response.length >= 9) {
78
+ targetId = (response[5] << 24) + (response[6] << 16) + (response[7] << 8) + (response[8] << 0);
79
+ }
80
+
81
+ return {
82
+ device_locked: response[4] === 1,
83
+ error_message: errorCodeToString(returnCode),
84
+ major: response[1],
85
+ minor: response[2],
86
+ patch: response[3],
87
+ return_code: returnCode,
88
+ target_id: targetId.toString(16),
89
+ // ///
90
+ test_mode: response[0] !== 0,
91
+ };
92
+ }, processErrorResponse);
93
+ }
@@ -0,0 +1,120 @@
1
+ import { SwapKitError } from "@swapkit/helpers";
2
+ import { CLA, ERROR_CODE, errorCodeToString, INS, P2_VALUES, PAYLOAD_TYPE, processErrorResponse } from "./common";
3
+
4
+ export function serializePathv1(path: number[]) {
5
+ if (path == null || path.length < 3) {
6
+ throw new SwapKitError("wallet_ledger_invalid_params", { reason: "Path too short" });
7
+ }
8
+ if (path.length > 10) {
9
+ throw new SwapKitError("wallet_ledger_invalid_params", { reason: "Path too long" });
10
+ }
11
+ const buf = Buffer.alloc(1 + 4 * path.length);
12
+ buf.writeUInt8(path.length, 0);
13
+ for (let i = 0; i < path.length; i += 1) {
14
+ let v = path[i] || 0;
15
+ if (i < 3) {
16
+ // eslint-disable-next-line no-bitwise
17
+ v |= 0x80000000; // Harden
18
+ }
19
+ buf.writeInt32LE(v, 1 + i * 4);
20
+ }
21
+ return buf;
22
+ }
23
+
24
+ export function signSendChunkv1(app: any, chunkIdx: number, _chunkNum: number, chunk: Buffer, txType = P2_VALUES.JSON) {
25
+ return app.transport
26
+ .send(CLA, INS.SIGN_SECP256K1, chunkIdx, txType, chunk, [ERROR_CODE.NoError, 0x6984, 0x6a80])
27
+ .then((response: any) => {
28
+ const errorCodeData = response.slice(-2);
29
+ const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
30
+ let errorMessage = errorCodeToString(returnCode);
31
+
32
+ if (returnCode === 0x6a80 || returnCode === 0x6984) {
33
+ errorMessage = `${errorMessage} : ${response.slice(0, response.length - 2).toString("ascii")}`;
34
+ }
35
+
36
+ let signature: any = null;
37
+ if (response.length > 2) {
38
+ signature = response.slice(0, response.length - 2);
39
+ }
40
+
41
+ return { error_message: errorMessage, return_code: returnCode, signature };
42
+ }, processErrorResponse);
43
+ }
44
+
45
+ function compressPublicKey(publicKey: Buffer) {
46
+ if (publicKey.length !== 65) {
47
+ throw new SwapKitError("wallet_ledger_invalid_params", {
48
+ reason: "decompressed public key length should be 65 bytes",
49
+ });
50
+ }
51
+ const y = publicKey.slice(33, 65);
52
+
53
+ // @ts-expect-error
54
+ const z = Buffer.from([2 + (y[y.length - 1] & 1)]);
55
+ return Buffer.concat([z, publicKey.slice(1, 33)]);
56
+ }
57
+
58
+ export function publicKeyv1(app: any, data: Buffer) {
59
+ return app.transport
60
+ .send(CLA, INS.INS_PUBLIC_KEY_SECP256K1, 0, 0, data, [ERROR_CODE.NoError])
61
+ .then((response: any) => {
62
+ const errorCodeData = response.slice(-2);
63
+ const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
64
+ const pk = Buffer.from(response.slice(0, 65));
65
+
66
+ return {
67
+ compressed_pk: compressPublicKey(pk),
68
+ error_message: errorCodeToString(returnCode),
69
+ pk,
70
+ return_code: returnCode,
71
+ };
72
+ }, processErrorResponse);
73
+ }
74
+
75
+ export function serializePathv2(path: number[]) {
76
+ if (!path || path.length !== 5) {
77
+ throw new SwapKitError("wallet_ledger_invalid_params", { reason: "Path must be exactly 5 elements" });
78
+ }
79
+
80
+ const buf = Buffer.alloc(20);
81
+ // @ts-expect-error
82
+ buf.writeUInt32LE(0x80000000 + path[0], 0);
83
+ // @ts-expect-error
84
+ buf.writeUInt32LE(0x80000000 + path[1], 4);
85
+ // @ts-expect-error
86
+ buf.writeUInt32LE(0x80000000 + path[2], 8);
87
+ // @ts-expect-error
88
+ buf.writeUInt32LE(path[3], 12);
89
+ // @ts-expect-error
90
+ buf.writeUInt32LE(path[4], 16);
91
+
92
+ return buf;
93
+ }
94
+
95
+ export function signSendChunkv2(app: any, chunkIdx: number, chunkNum: number, chunk: Buffer, txType = P2_VALUES.JSON) {
96
+ let payloadType = PAYLOAD_TYPE.ADD;
97
+ if (chunkIdx === 1) {
98
+ payloadType = PAYLOAD_TYPE.INIT;
99
+ }
100
+ if (chunkIdx === chunkNum) {
101
+ payloadType = PAYLOAD_TYPE.LAST;
102
+ }
103
+
104
+ return signSendChunkv1(app, payloadType, 0, chunk, txType);
105
+ }
106
+
107
+ export function publicKeyv2(app: any, data: Buffer) {
108
+ return app.transport.send(CLA, INS.GET_ADDR_SECP256K1, 0, 0, data, [ERROR_CODE.NoError]).then((response: any) => {
109
+ const errorCodeData = response.slice(-2);
110
+ const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
111
+ const compressedPk = Buffer.from(response.slice(0, 33));
112
+
113
+ return {
114
+ compressed_pk: compressedPk,
115
+ error_message: errorCodeToString(returnCode),
116
+ pk: "OBSOLETE PROPERTY",
117
+ return_code: returnCode,
118
+ };
119
+ }, processErrorResponse);
120
+ }
@@ -0,0 +1,87 @@
1
+ import { base64 } from "@scure/base";
2
+ import { type DerivationPathArray, NetworkDerivationPath, SwapKitError } from "@swapkit/helpers";
3
+
4
+ import { CosmosLedgerInterface } from "../../interfaces/CosmosLedgerInterface";
5
+ import type { GetAddressAndPubKeyResponse } from "../../types";
6
+ import { getSignature } from "./utils";
7
+
8
+ export class THORChainLedger extends CosmosLedgerInterface {
9
+ private pubKey: string | null = null;
10
+
11
+ derivationPath: DerivationPathArray;
12
+
13
+ constructor(derivationPath: DerivationPathArray = NetworkDerivationPath.THOR) {
14
+ super();
15
+ this.chain = "thor";
16
+ this.derivationPath = derivationPath;
17
+ }
18
+
19
+ get pubkey() {
20
+ return this.pubKey;
21
+ }
22
+
23
+ connect = async () => {
24
+ await this.checkOrCreateTransportAndLedger();
25
+ const { compressed_pk, bech32_address }: GetAddressAndPubKeyResponse = await this.getAddressAndPubKey();
26
+
27
+ this.pubKey = base64.encode(compressed_pk);
28
+
29
+ return bech32_address;
30
+ };
31
+
32
+ getAddressAndPubKey = async () => {
33
+ await this.checkOrCreateTransportAndLedger(true);
34
+
35
+ const response: GetAddressAndPubKeyResponse = await this.ledgerApp.getAddressAndPubKey(
36
+ this.derivationPath,
37
+ this.chain,
38
+ );
39
+
40
+ this.validateResponse(response.return_code, response.error_message);
41
+
42
+ return response;
43
+ };
44
+
45
+ showAddressAndPubKey = async () => {
46
+ await this.checkOrCreateTransportAndLedger(true);
47
+
48
+ const response: GetAddressAndPubKeyResponse = await this.ledgerApp.showAddressAndPubKey(
49
+ this.derivationPath,
50
+ this.chain,
51
+ );
52
+
53
+ this.validateResponse(response.return_code, response.error_message);
54
+
55
+ return response;
56
+ };
57
+
58
+ signTransaction = async (rawTx: string, sequence = "0") => {
59
+ await this.checkOrCreateTransportAndLedger(true);
60
+
61
+ const { return_code, error_message, signature } = await this.ledgerApp.sign(this.derivationPath, rawTx);
62
+
63
+ if (!this.pubKey) throw new SwapKitError("wallet_ledger_pubkey_not_found");
64
+
65
+ this.validateResponse(return_code, error_message);
66
+
67
+ return [
68
+ {
69
+ pub_key: { type: "tendermint/PubKeySecp256k1", value: this.pubKey },
70
+ sequence,
71
+ signature: getSignature(signature),
72
+ },
73
+ ];
74
+ };
75
+
76
+ sign = async (message: string) => {
77
+ await this.checkOrCreateTransportAndLedger(true);
78
+
79
+ const { return_code, error_message, signature } = await this.ledgerApp.sign(this.derivationPath, message);
80
+
81
+ if (!this.pubKey) throw new SwapKitError("wallet_ledger_pubkey_not_found");
82
+
83
+ this.validateResponse(return_code, error_message);
84
+
85
+ return getSignature(signature);
86
+ };
87
+ }
@@ -0,0 +1,258 @@
1
+ import type Transport from "@ledgerhq/hw-transport";
2
+ import { SwapKitError } from "@swapkit/helpers";
3
+
4
+ import {
5
+ CHUNK_SIZE,
6
+ CLA,
7
+ ERROR_CODE,
8
+ errorCodeToString,
9
+ getVersion,
10
+ INS,
11
+ P1_VALUES,
12
+ P2_VALUES,
13
+ processErrorResponse,
14
+ } from "./common";
15
+ import {
16
+ publicKeyv1,
17
+ publicKeyv2,
18
+ serializePathv1,
19
+ serializePathv2,
20
+ signSendChunkv1,
21
+ signSendChunkv2,
22
+ } from "./helpers";
23
+
24
+ export class THORChainApp {
25
+ transport: Transport;
26
+ versionResponse: any;
27
+
28
+ constructor(transport: any) {
29
+ if (!transport) {
30
+ throw new SwapKitError("wallet_ledger_transport_not_defined");
31
+ }
32
+
33
+ this.transport = transport;
34
+ }
35
+
36
+ static serializeHRP(hrp: string) {
37
+ if (hrp == null || hrp.length < 3 || hrp.length > 83) {
38
+ throw new SwapKitError("wallet_ledger_invalid_params", { reason: "Invalid HRP" });
39
+ }
40
+ const buf = Buffer.alloc(1 + hrp.length);
41
+ buf.writeUInt8(hrp.length, 0);
42
+ buf.write(hrp, 1);
43
+ return buf;
44
+ }
45
+
46
+ async serializePath(path: number[]) {
47
+ this.versionResponse = await getVersion(this.transport);
48
+
49
+ if (this.versionResponse.return_code !== ERROR_CODE.NoError) {
50
+ throw this.versionResponse;
51
+ }
52
+
53
+ switch (this.versionResponse.major) {
54
+ case 1:
55
+ return serializePathv1(path);
56
+ case 2:
57
+ return serializePathv2(path);
58
+ default:
59
+ return Buffer.alloc(0);
60
+ }
61
+ }
62
+
63
+ async signGetChunks(path: number[], buffer: Buffer) {
64
+ const serializedPath = await this.serializePath(path);
65
+
66
+ const chunks = [];
67
+ chunks.push(serializedPath);
68
+
69
+ for (let i = 0; i < buffer.length; i += CHUNK_SIZE) {
70
+ let end = i + CHUNK_SIZE;
71
+ if (i > buffer.length) {
72
+ end = buffer.length;
73
+ }
74
+ chunks.push(buffer.slice(i, end));
75
+ }
76
+
77
+ return chunks;
78
+ }
79
+
80
+ async getVersion() {
81
+ try {
82
+ this.versionResponse = await getVersion(this.transport);
83
+ return this.versionResponse;
84
+ } catch (e) {
85
+ return processErrorResponse(e);
86
+ }
87
+ }
88
+
89
+ appInfo() {
90
+ return this.transport.send(0xb0, 0x01, 0, 0).then((response: any) => {
91
+ const returnCode = response.readUInt16BE(response.length - 2);
92
+
93
+ let appName = "";
94
+ let appVersion = "";
95
+ let flagLen = 0;
96
+ let flagsValue = 0;
97
+
98
+ if (response[0] !== 1) {
99
+ // Ledger responds with format ID 1. There is no spec for any format != 1
100
+ return { error_message: "response format ID not recognized", return_code: 0x9001 };
101
+ }
102
+
103
+ const appNameLen = response[1];
104
+ appName = response.slice(2, 2 + appNameLen).toString("ascii");
105
+ let idx = 2 + appNameLen;
106
+ const appVersionLen = response[idx];
107
+ idx += 1;
108
+ appVersion = response.slice(idx, idx + appVersionLen).toString("ascii");
109
+ idx += appVersionLen;
110
+ const appFlagsLen = response[idx];
111
+ idx += 1;
112
+ flagLen = appFlagsLen;
113
+ flagsValue = response[idx];
114
+
115
+ return {
116
+ appName,
117
+ appVersion,
118
+ error_message: errorCodeToString(returnCode),
119
+ flag_onboarded: (flagsValue & 4) !== 0,
120
+ flag_pin_validated: (flagsValue & 128) !== 0,
121
+ flag_recovery: (flagsValue & 1) !== 0,
122
+ flag_signed_mcu_code: (flagsValue & 2) !== 0,
123
+ flagLen,
124
+ flagsValue,
125
+ return_code: returnCode,
126
+ };
127
+ }, processErrorResponse);
128
+ }
129
+
130
+ deviceInfo() {
131
+ return this.transport
132
+ .send(0xe0, 0x01, 0, 0, Buffer.from([]), [ERROR_CODE.NoError, 0x6e00])
133
+ .then((response: any) => {
134
+ const errorCodeData = response.slice(-2);
135
+ const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
136
+
137
+ if (returnCode === 0x6e00) {
138
+ return { error_message: "This command is only available in the Dashboard", return_code: returnCode };
139
+ }
140
+
141
+ const targetId = response.slice(0, 4).toString("hex");
142
+
143
+ let pos = 4;
144
+ const secureElementVersionLen = response[pos];
145
+ pos += 1;
146
+ const seVersion = response.slice(pos, pos + secureElementVersionLen).toString();
147
+ pos += secureElementVersionLen;
148
+
149
+ const flagsLen = response[pos];
150
+ pos += 1;
151
+ const flag = response.slice(pos, pos + flagsLen).toString("hex");
152
+ pos += flagsLen;
153
+
154
+ const mcuVersionLen = response[pos];
155
+ pos += 1;
156
+ // Patch issue in mcu version
157
+ let tmp = response.slice(pos, pos + mcuVersionLen);
158
+ if (tmp[mcuVersionLen - 1] === 0) {
159
+ tmp = response.slice(pos, pos + mcuVersionLen - 1);
160
+ }
161
+ const mcuVersion = tmp.toString();
162
+
163
+ return {
164
+ error_message: errorCodeToString(returnCode),
165
+ flag,
166
+ mcuVersion,
167
+ return_code: returnCode,
168
+ seVersion,
169
+ // //
170
+ targetId,
171
+ };
172
+ }, processErrorResponse);
173
+ }
174
+
175
+ async publicKey(path: number[]) {
176
+ try {
177
+ const serializedPath = await this.serializePath(path);
178
+
179
+ switch (this.versionResponse.major) {
180
+ case 1:
181
+ return publicKeyv1(this, serializedPath);
182
+ case 2: {
183
+ const data = Buffer.concat([THORChainApp.serializeHRP("thor"), serializedPath]);
184
+ return publicKeyv2(this, data);
185
+ }
186
+ default:
187
+ return { error_message: "App Version is not supported", return_code: 0x6400 };
188
+ }
189
+ } catch (e) {
190
+ return processErrorResponse(e);
191
+ }
192
+ }
193
+
194
+ async getAddressAndPubKey(path: number[], hrp: string, showInDevice = false) {
195
+ try {
196
+ const serializedPath = await this.serializePath(path);
197
+ const data = Buffer.concat([THORChainApp.serializeHRP(hrp), serializedPath]);
198
+ const response = await this.transport.send(
199
+ CLA,
200
+ INS.GET_ADDR_SECP256K1,
201
+ showInDevice ? P1_VALUES.SHOW_ADDRESS_IN_DEVICE : P1_VALUES.ONLY_RETRIEVE,
202
+ 0,
203
+ data,
204
+ [ERROR_CODE.NoError],
205
+ );
206
+
207
+ const compressedPk = Buffer.from(response.slice(0, 33));
208
+ const bech32Address = Buffer.from(response.slice(33, -2)).toString();
209
+ const returnCode = response.readUInt16BE(response.length - 2);
210
+
211
+ return {
212
+ bech32_address: bech32Address,
213
+ compressed_pk: compressedPk,
214
+ error_message: errorCodeToString(returnCode),
215
+ return_code: returnCode,
216
+ };
217
+ } catch (err) {
218
+ return processErrorResponse(err);
219
+ }
220
+ }
221
+
222
+ showAddressAndPubKey(path: number[], hrp: string) {
223
+ return this.getAddressAndPubKey(path, hrp, true);
224
+ }
225
+
226
+ signSendChunk(chunkIdx: number, chunkNum: number, chunk: Buffer, txType = P2_VALUES.JSON) {
227
+ switch (this.versionResponse.major) {
228
+ case 1:
229
+ return signSendChunkv1(this, chunkIdx, chunkNum, chunk, txType);
230
+ case 2:
231
+ return signSendChunkv2(this, chunkIdx, chunkNum, chunk, txType);
232
+ default:
233
+ return { error_message: "App Version is not supported", return_code: 0x6400 };
234
+ }
235
+ }
236
+
237
+ async sign(path: number[], message: string, txType = P2_VALUES.JSON) {
238
+ const buffer = Buffer.from(message);
239
+ let chunks: Buffer[] = [];
240
+ let response: any;
241
+ try {
242
+ chunks = await this.signGetChunks(path, buffer);
243
+ response = await this.signSendChunk(1, chunks.length, chunks[0] as Buffer, txType);
244
+ } catch (error) {
245
+ processErrorResponse(error);
246
+ }
247
+ let result = { error_message: response.error_message, return_code: response.return_code, signature: null };
248
+
249
+ for (let i = 1; i < chunks.length; i += 1) {
250
+ result = await this.signSendChunk(1 + i, chunks.length, chunks[i] as Buffer, txType);
251
+ if (result.return_code !== ERROR_CODE.NoError) {
252
+ break;
253
+ }
254
+ }
255
+
256
+ return { error_message: result.error_message, return_code: result.return_code, signature: result.signature };
257
+ }
258
+ }