-
Notifications
You must be signed in to change notification settings - Fork 456
/
ledger.ts
126 lines (104 loc) · 3.39 KB
/
ledger.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { TransportIniter } from "./options";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const CosmosApp: any = require("ledger-cosmos-js").default;
import TransportWebHID from "@ledgerhq/hw-transport-webhid";
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
import { signatureImport } from "secp256k1";
export enum LedgerInitErrorOn {
Transport,
App,
Unknown,
}
export const LedgerWebUSBIniter: TransportIniter = async () => {
return await TransportWebUSB.create();
};
export const LedgerWebHIDIniter: TransportIniter = async () => {
return await TransportWebHID.create();
};
export class LedgerInitError extends Error {
constructor(public readonly errorOn: LedgerInitErrorOn, message?: string) {
super(message);
// Set the prototype explicitly.
Object.setPrototypeOf(this, LedgerInitError.prototype);
}
}
export class Ledger {
constructor(private readonly cosmosApp: any) {}
static async init(
transportIniter: TransportIniter,
initArgs: any[] = []
): Promise<Ledger> {
const transport = await transportIniter(...initArgs);
try {
const cosmosApp = new CosmosApp(transport);
const ledger = new Ledger(cosmosApp);
const versionResponse = await ledger.getVersion();
// In this case, device is on screen saver.
// However, it is almost same as that the device is not unlocked to user-side.
// So, handle this case as initializing failed in `Transport`.
if (versionResponse.deviceLocked) {
throw new Error("Device is on screen saver");
}
return ledger;
} catch (e) {
if (transport) {
await transport.close();
}
if (e.message === "Device is on screen saver") {
throw new LedgerInitError(LedgerInitErrorOn.Transport, e.message);
}
throw new LedgerInitError(LedgerInitErrorOn.App, e.message);
}
}
async getVersion(): Promise<{
deviceLocked: boolean;
major: number;
minor: number;
patch: number;
targetId: string;
testMode: boolean;
}> {
if (!this.cosmosApp) {
throw new Error("Comsos App not initialized");
}
const result = await this.cosmosApp.getVersion();
if (result.error_message !== "No errors") {
throw new Error(result.error_message);
}
return {
deviceLocked: result.device_locked,
major: result.major,
minor: result.minor,
patch: result.patch,
targetId: result.target_id,
testMode: result.test_mode,
};
}
async getPublicKey(path: number[]): Promise<Uint8Array> {
if (!this.cosmosApp) {
throw new Error("Comsos App not initialized");
}
const result = await this.cosmosApp.publicKey(path);
if (result.error_message !== "No errors") {
throw new Error(result.error_message);
}
return result.compressed_pk;
}
async sign(path: number[], message: Uint8Array): Promise<Uint8Array> {
if (!this.cosmosApp) {
throw new Error("Comsos App not initialized");
}
const result = await this.cosmosApp.sign(path, message);
if (result.error_message !== "No errors") {
throw new Error(result.error_message);
}
// Parse a DER ECDSA signature
return signatureImport(result.signature);
}
async close(): Promise<void> {
return await this.cosmosApp.transport.close();
}
static async isWebHIDSupported(): Promise<boolean> {
return await TransportWebHID.isSupported();
}
}