Merge pull request #446 from CosmWasm/445-ledger-transport-agnostic

Pass transport to Ledger signer
This commit is contained in:
Will Clark
2020-10-06 17:12:28 +02:00
committed by GitHub
13 changed files with 156 additions and 184 deletions
+24
View File
@@ -41,6 +41,9 @@ jobs:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install libusb
command: sudo apt-get install libusb-1.0-0-dev
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -111,6 +114,11 @@ jobs:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install libusb
command: |
wget -O "$HOME/libusb.deb" http://de.archive.ubuntu.com/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0-dev_1.0.20-1_amd64.deb
sudo dpkg -i "$HOME/libusb.deb"
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -214,6 +222,11 @@ jobs:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install libusb
command: |
wget -O "$HOME/libusb.deb" http://de.archive.ubuntu.com/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0-dev_1.0.20-1_amd64.deb
sudo dpkg -i "$HOME/libusb.deb"
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -298,6 +311,11 @@ jobs:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install libusb
command: |
wget -O "$HOME/libusb.deb" http://de.archive.ubuntu.com/ubuntu/pool/main/libu/libusb-1.0/libusb-1.0-0-dev_1.0.20-1_amd64.deb
sudo dpkg -i "$HOME/libusb.deb"
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -344,6 +362,9 @@ jobs:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install libusb
command: sudo apt-get install libusb-1.0-0-dev
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -391,6 +412,9 @@ jobs:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install libusb
command: sudo apt-get install libusb-1.0-0-dev
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
+5 -14
View File
@@ -4,25 +4,16 @@
## Supported platforms
This library works with Node.js as well as certain browsers. We use the
This library must be provided with a Ledger transport, and has been tested with
appropriate transports for Node.js as well as certain browsers. In our demo code
we use
[@ledgerhq/hw-transport-node-hid](https://github.com/LedgerHQ/ledgerjs/tree/master/packages/hw-transport-node-hid)
to connect to Ledger devices from Node.js. We use the
[@ledgerhq/hw-transport-webusb](https://github.com/LedgerHQ/ledgerjs/tree/master/packages/hw-transport-webusb)
library to connect to Ledger devices from the browser via USB. You can check the
support status of this library
[here](https://github.com/LedgerHQ/ledgerjs/tree/master/packages/hw-transport-webusb#support-status).
Note the optional dependencies:
```json
"optionalDependencies": {
"@ledgerhq/hw-transport-node-hid": "^5.23.2",
"@ledgerhq/hw-transport-webusb": "^5.23.0"
}
```
If you are using this library with Node.js you must install
`@ledgerhq/hw-transport-node-hid`. Youll need `@ledgerhq/hw-transport-webusb`
for the browser.
## Running the demo
### Node.js
+2 -2
View File
@@ -19,7 +19,7 @@
</ol>
</div>
<div>
<button onclick="getAccounts()">
<button onclick="getAccounts(window.signer)">
Get Accounts
</button>
</div>
@@ -38,7 +38,7 @@
</textarea>
</div>
<div>
<button onclick="sign()">
<button onclick="sign(window.signer)">
Sign Message
</button>
</div>
+5 -3
View File
@@ -1,14 +1,16 @@
const demo = require("../build/demo/node");
async function run() {
const signer = await demo.createSigner();
const accountNumbers = [0, 1, 2, 10];
const accounts = await demo.getAccounts();
const accounts = await demo.getAccounts(signer);
console.info("Accounts from Ledger device:");
console.table(accounts.map((account, i) => ({ ...account, accountNumber: accountNumbers[i] })));
const accountNumber0 = 0;
const address0 = accounts[accountNumber0].address;
const signature0 = await demo.sign(accountNumber0, address0, address0);
const signature0 = await demo.sign(signer, accountNumber0, address0, address0);
console.info(`Signature from Ledger device for account number 0 (${address0}):`);
console.info(signature0);
@@ -17,7 +19,7 @@ async function run() {
const accountNumber10 = 10;
const address10 = accounts[accountNumbers.findIndex((n) => n === accountNumber10)].address;
const signature1 = await demo.sign(accountNumber10, address10, address10);
const signature1 = await demo.sign(signer, accountNumber10, address10, address10);
console.info(`Signature from Ledger device for account number 10 (${address10}):`);
console.info(signature1);
}
+1 -4
View File
@@ -47,11 +47,8 @@
"ledger-cosmos-js": "^2.1.7",
"semver": "^7.3.2"
},
"optionalDependencies": {
"@ledgerhq/hw-transport-node-hid": "^5.23.2",
"@ledgerhq/hw-transport-webusb": "^5.23.0"
},
"devDependencies": {
"@ledgerhq/hw-transport": "^5.25.0",
"@ledgerhq/hw-transport-node-hid": "^5.23.2",
"@ledgerhq/hw-transport-webusb": "^5.23.0",
"@types/ledgerhq__hw-transport-node-hid": "^4.22.1",
+13 -5
View File
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toBase64 } from "@cosmjs/encoding";
import { makeCosmoshubPath, makeSignDoc, StdFee, StdSignature } from "@cosmjs/launchpad";
import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
import { LedgerSigner } from "../ledgersigner";
@@ -12,12 +13,18 @@ const defaultFee: StdFee = {
const defaultMemo = "Some memo";
const defaultSequence = "0";
const signer = new LedgerSigner({
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2), makeCosmoshubPath(10)],
});
export async function createSigner(): Promise<LedgerSigner> {
const interactiveTimeout = 120_000;
const ledgerTransport = await TransportNodeHid.create(interactiveTimeout, interactiveTimeout);
return new LedgerSigner(ledgerTransport, {
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2), makeCosmoshubPath(10)],
});
}
export async function getAccounts(): Promise<
export async function getAccounts(
signer: LedgerSigner,
): Promise<
ReadonlyArray<{
readonly algo: string;
readonly address: string;
@@ -29,6 +36,7 @@ export async function getAccounts(): Promise<
}
export async function sign(
signer: LedgerSigner,
accountNumber: number,
fromAddress: string,
toAddress: string,
+24 -7
View File
@@ -2,6 +2,7 @@ import { toBase64 } from "@cosmjs/encoding";
import { AccountData, makeCosmoshubPath, StdSignDoc } from "@cosmjs/launchpad";
import { Uint53 } from "@cosmjs/math";
import { assert } from "@cosmjs/utils";
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
import { LedgerSigner } from "../ledgersigner";
@@ -43,11 +44,6 @@ function createSignDoc(accountNumber: number, address: string): string {
return JSON.stringify(signDoc, null, 2);
}
const signer = new LedgerSigner({
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2)],
});
window.updateMessage = (accountNumberInput: unknown) => {
assert(typeof accountNumberInput === "string");
const accountNumber = Uint53.fromString(accountNumberInput).toNumber();
@@ -63,7 +59,20 @@ window.updateMessage = (accountNumberInput: unknown) => {
signDocTextArea.textContent = createSignDoc(accountNumber, address);
};
window.getAccounts = async function getAccounts(): Promise<void> {
window.createSigner = async function createSigner(): Promise<LedgerSigner> {
const interactiveTimeout = 120_000;
const ledgerTransport = await TransportWebUSB.create(interactiveTimeout, interactiveTimeout);
return new LedgerSigner(ledgerTransport, {
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2)],
});
};
window.getAccounts = async function getAccounts(signer: LedgerSigner | undefined): Promise<void> {
if (signer === undefined) {
console.error("Please wait for transport to connect");
return;
}
const accountNumberInput = document.getElementById("account-number");
const addressInput = document.getElementById("address");
const accountsDiv = document.getElementById("accounts");
@@ -88,7 +97,11 @@ window.getAccounts = async function getAccounts(): Promise<void> {
}
};
window.sign = async function sign(): Promise<void> {
window.sign = async function sign(signer: LedgerSigner | undefined): Promise<void> {
if (signer === undefined) {
console.error("Please wait for transport to connect");
return;
}
const signatureDiv = document.getElementById("signature");
signatureDiv.textContent = "Loading...";
@@ -102,3 +115,7 @@ window.sign = async function sign(): Promise<void> {
signatureDiv.textContent = error;
}
};
window.onload = async function onLoad(): Promise<void> {
window.signer = await window.createSigner();
};
+15 -119
View File
@@ -18,37 +18,13 @@ export interface LedgerAppErrorResponse {
}
/* eslint-enable */
interface ConnectedApp {
/** The transport used by the app */
readonly transport: Transport;
readonly app: CosmosApp;
}
/** Time to establish a connection in milliseconds */
const defaultOpenTimeout = 120_000;
const requiredCosmosAppVersion = "1.5.3";
function isWindows(platform: string): boolean {
return platform.indexOf("Win") > -1;
}
function verifyBrowserIsSupported(platform: string, userAgent: string | null): void {
if (isWindows(platform)) {
throw new Error("Windows is not currently supported.");
}
const isChromeOrBrave = userAgent && /chrome|crios/i.test(userAgent) && !/edge|opr\//i.test(userAgent);
if (!isChromeOrBrave) {
throw new Error("Your browser does not support Ledger devices.");
}
}
function unharden(hdPath: HdPath): number[] {
return hdPath.map((n) => (n.isHardened() ? n.toNumber() - 2 ** 31 : n.toNumber()));
}
const cosmosHdPath = makeCosmoshubPath(0);
const cosmosBech32Prefix = "cosmos";
const requiredCosmosAppVersion = "1.5.3";
export interface LaunchpadLedgerOptions {
readonly hdPaths?: readonly HdPath[];
@@ -60,11 +36,9 @@ export class LaunchpadLedger {
private readonly testModeAllowed: boolean;
private readonly hdPaths: readonly HdPath[];
private readonly prefix: string;
private connectedApp: ConnectedApp | null;
public readonly platform: string;
public readonly userAgent: string | null;
private readonly app: CosmosApp;
public constructor(options: LaunchpadLedgerOptions = {}) {
public constructor(transport: Transport, options: LaunchpadLedgerOptions = {}) {
const defaultOptions = {
hdPaths: [cosmosHdPath],
prefix: cosmosBech32Prefix,
@@ -77,22 +51,14 @@ export class LaunchpadLedger {
this.testModeAllowed = testModeAllowed;
this.hdPaths = hdPaths;
this.prefix = prefix;
this.connectedApp = null;
try {
this.platform = navigator.platform;
this.userAgent = navigator.userAgent;
} catch (error) {
this.platform = "node";
this.userAgent = null;
}
this.app = new CosmosApp(transport);
}
public async getCosmosAppVersion(): Promise<string> {
await this.ensureConnected();
assert(this.connectedApp, "Cosmos Ledger App is not connected");
await this.verifyCosmosAppIsOpen();
assert(this.app, "Cosmos Ledger App is not connected");
const response = await this.connectedApp.app.getVersion();
const response = await this.app.getVersion();
this.handleLedgerErrors(response);
// eslint-disable-next-line @typescript-eslint/naming-convention
const { major, minor, patch, test_mode: testMode } = response as VersionResponse;
@@ -101,12 +67,12 @@ export class LaunchpadLedger {
}
public async getPubkey(hdPath?: HdPath): Promise<Uint8Array> {
await this.ensureConnected();
assert(this.connectedApp, "Cosmos Ledger App is not connected");
await this.verifyDeviceIsReady();
assert(this.app, "Cosmos Ledger App is not connected");
const hdPathToUse = hdPath || this.hdPaths[0];
// ledger-cosmos-js hardens the first three indices
const response = await this.connectedApp.app.publicKey(unharden(hdPathToUse));
const response = await this.app.publicKey(unharden(hdPathToUse));
this.handleLedgerErrors(response);
return Uint8Array.from((response as PublicKeyResponse).compressed_pk);
}
@@ -125,85 +91,16 @@ export class LaunchpadLedger {
}
public async sign(message: Uint8Array, hdPath?: HdPath): Promise<Uint8Array> {
await this.ensureConnected();
assert(this.connectedApp, "Cosmos Ledger App is not connected");
await this.verifyDeviceIsReady();
assert(this.app, "Cosmos Ledger App is not connected");
const hdPathToUse = hdPath || this.hdPaths[0];
// ledger-cosmos-js hardens the first three indices
const response = await this.connectedApp.app.sign(unharden(hdPathToUse), fromUtf8(message));
const response = await this.app.sign(unharden(hdPathToUse), fromUtf8(message));
this.handleLedgerErrors(response, "Transaction signing request was rejected by the user");
return Secp256k1Signature.fromDer((response as SignResponse).signature).toFixedLength();
}
public async disconnect(): Promise<void> {
if (this.connectedApp) {
await this.connectedApp.transport.close();
this.connectedApp = null;
}
}
private async ensureConnected(): Promise<void> {
// assume good connection if connected once
if (this.connectedApp) {
return;
}
if (this.platform !== "node") {
verifyBrowserIsSupported(this.platform, this.userAgent);
}
const transport = await this.createTransport(defaultOpenTimeout);
this.connectedApp = {
transport: transport,
app: new CosmosApp(transport),
};
await this.verifyDeviceIsReady();
}
/**
* @param openTimeout The time to establish a connection in milliseconds. This is
* [passed into as the second argument into Transport.open](https://github.com/LedgerHQ/ledgerjs/blob/v5.25.2/packages/hw-transport/src/Transport.js#L235),
* which is ignored by both [TransportWebUSB.open](https://github.com/LedgerHQ/ledgerjs/blob/v5.25.2/packages/hw-transport-webusb/src/TransportWebUSB.js#L116)
* and [TransportNodeHid.open](https://github.com/LedgerHQ/ledgerjs/blob/v5.25.2/packages/hw-transport-node-hid/src/TransportNodeHid.js#L115).
*/
private async createTransport(openTimeout: number): Promise<Transport> {
// HACK: Use a variable to get webpack to ignore this
const nodeJsTransportPackageName = "@ledgerhq/hw-transport-node-hid";
/* eslint-disable-next-line @typescript-eslint/naming-convention */
const { default: TransportClass } =
this.platform === "node"
? await import(nodeJsTransportPackageName)
: await import("@ledgerhq/hw-transport-webusb");
try {
const transport = await TransportClass.create(openTimeout);
return transport;
} catch (error) {
const trimmedErrorMessage = error.message.trim();
if (trimmedErrorMessage.startsWith("No WebUSB interface found for your Ledger device")) {
throw new Error(
"Could not connect to a Ledger device. Please use Ledger Live to upgrade the Ledger firmware to version 1.5.5 or later.",
);
}
if (trimmedErrorMessage.startsWith("Unable to claim interface")) {
throw new Error("Could not access Ledger device. Is it being used in another tab?");
}
if (trimmedErrorMessage.startsWith("Not supported")) {
throw new Error(
"Your browser does not seem to support WebUSB yet. Try updating it to the latest version.",
);
}
if (trimmedErrorMessage.startsWith("No device selected")) {
throw new Error(
"You did not select a Ledger device. If you did not see your Ledger, check if the Ledger is plugged in and unlocked.",
);
}
throw error;
}
}
private verifyAppMode(testMode: boolean): void {
if (testMode && !this.testModeAllowed) {
throw new Error(`DANGER: The Cosmos Ledger app is in test mode and should not be used on mainnet!`);
@@ -211,10 +108,9 @@ export class LaunchpadLedger {
}
private async getOpenAppName(): Promise<string> {
await this.ensureConnected();
assert(this.connectedApp, "Cosmos Ledger App is not connected");
assert(this.app, "Cosmos Ledger App is not connected");
const response = await this.connectedApp.app.appInfo();
const response = await this.app.appInfo();
this.handleLedgerErrors(response);
return (response as AppInfoResponse).appName;
}
@@ -12,12 +12,29 @@ import {
StdFee,
} from "@cosmjs/launchpad";
import { assert, sleep } from "@cosmjs/utils";
import Transport from "@ledgerhq/hw-transport";
import { LedgerSigner } from "./ledgersigner";
import { pendingWithoutLedger, pendingWithoutWasmd, wasmd } from "./testutils.spec";
const interactiveTimeout = 120_000;
async function createTransport(): Promise<Transport> {
let platform: string;
try {
platform = navigator.platform;
} catch (error) {
platform = "node";
}
// HACK: Use a variable to get webpack to ignore this
const nodeJsTransportPackageName = "@ledgerhq/hw-transport-node-hid";
const { default: TransportClass } =
platform === "node"
? await import(nodeJsTransportPackageName)
: await import("@ledgerhq/hw-transport-webusb");
return TransportClass.create(interactiveTimeout, interactiveTimeout);
}
describe("LedgerSigner", () => {
const defaultChainId = "testing";
const defaultFee: StdFee = {
@@ -28,11 +45,20 @@ describe("LedgerSigner", () => {
const defaultSequence = "0";
const defaultAccountNumber = "42";
const defaultRecipient = "cosmos1p6xs63q4g7np99ttv5nd3yzkt8n4qxa47w8aea";
let transport: Transport;
beforeEach(async () => {
transport = await createTransport();
});
afterEach(async () => {
await transport.close();
});
describe("getAccount", () => {
it("works", async () => {
pendingWithoutLedger();
const signer = new LedgerSigner({
const signer = new LedgerSigner(transport, {
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(10)],
});
@@ -56,8 +82,6 @@ describe("LedgerSigner", () => {
pubkey: fromBase64("A2ZnLEcbpyjS30H5UF1vezq29aBcT9oo5EARATIW9Cpj"),
},
]);
await signer.disconnect();
});
});
@@ -71,7 +95,7 @@ describe("LedgerSigner", () => {
"returns valid signature",
async () => {
pendingWithoutLedger();
const signer = new LedgerSigner({
const signer = new LedgerSigner(transport, {
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(10)],
});
@@ -104,8 +128,6 @@ describe("LedgerSigner", () => {
fistAccount.pubkey,
);
expect(valid).toEqual(true);
await signer.disconnect();
},
interactiveTimeout,
);
@@ -115,7 +137,7 @@ describe("LedgerSigner", () => {
async () => {
pendingWithoutLedger();
pendingWithoutWasmd();
const signer = new LedgerSigner({
const signer = new LedgerSigner(transport, {
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(10)],
});
@@ -124,8 +146,6 @@ describe("LedgerSigner", () => {
const client = new SigningCosmosClient(wasmd.endpoint, fistAccount.address, signer);
const result = await client.sendTokens(defaultRecipient, coins(1234567, "ucosm"));
assert(isBroadcastTxSuccess(result));
await signer.disconnect();
},
interactiveTimeout,
);
@@ -7,6 +7,7 @@ import {
StdSignDoc,
} from "@cosmjs/launchpad";
import { serializeSignDoc, SignResponse } from "@cosmjs/launchpad";
import Transport from "@ledgerhq/hw-transport";
import { LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger";
@@ -15,9 +16,9 @@ export class LedgerSigner implements OfflineSigner {
private readonly hdPaths: readonly HdPath[];
private accounts?: readonly AccountData[];
public constructor(options: LaunchpadLedgerOptions = {}) {
public constructor(transport: Transport, options: LaunchpadLedgerOptions = {}) {
this.hdPaths = options.hdPaths || [makeCosmoshubPath(0)];
this.ledger = new LaunchpadLedger(options);
this.ledger = new LaunchpadLedger(transport, options);
}
public async getAccounts(): Promise<readonly AccountData[]> {
@@ -52,8 +53,4 @@ export class LedgerSigner implements OfflineSigner {
signature: encodeSecp256k1Signature(accountForAddress.pubkey, signature),
};
}
public async disconnect(): Promise<void> {
return this.ledger.disconnect();
}
}
+4 -13
View File
@@ -1,4 +1,6 @@
/// <reference types="ledgerhq__hw-transport" />
import { HdPath } from "@cosmjs/crypto";
import Transport from "@ledgerhq/hw-transport";
export interface LedgerAppErrorResponse {
readonly error_message?: string;
readonly device_locked?: boolean;
@@ -12,24 +14,13 @@ export declare class LaunchpadLedger {
private readonly testModeAllowed;
private readonly hdPaths;
private readonly prefix;
private connectedApp;
readonly platform: string;
readonly userAgent: string | null;
constructor(options?: LaunchpadLedgerOptions);
private readonly app;
constructor(transport: Transport, options?: LaunchpadLedgerOptions);
getCosmosAppVersion(): Promise<string>;
getPubkey(hdPath?: HdPath): Promise<Uint8Array>;
getPubkeys(): Promise<readonly Uint8Array[]>;
getCosmosAddress(pubkey?: Uint8Array): Promise<string>;
sign(message: Uint8Array, hdPath?: HdPath): Promise<Uint8Array>;
disconnect(): Promise<void>;
private ensureConnected;
/**
* @param openTimeout The time to establish a connection in milliseconds. This is
* [passed into as the second argument into Transport.open](https://github.com/LedgerHQ/ledgerjs/blob/v5.25.2/packages/hw-transport/src/Transport.js#L235),
* which is ignored by both [TransportWebUSB.open](https://github.com/LedgerHQ/ledgerjs/blob/v5.25.2/packages/hw-transport-webusb/src/TransportWebUSB.js#L116)
* and [TransportNodeHid.open](https://github.com/LedgerHQ/ledgerjs/blob/v5.25.2/packages/hw-transport-node-hid/src/TransportNodeHid.js#L115).
*/
private createTransport;
private verifyAppMode;
private getOpenAppName;
private verifyAppVersion;
+3 -2
View File
@@ -1,12 +1,13 @@
/// <reference types="ledgerhq__hw-transport" />
import { AccountData, OfflineSigner, StdSignDoc } from "@cosmjs/launchpad";
import { SignResponse } from "@cosmjs/launchpad";
import Transport from "@ledgerhq/hw-transport";
import { LaunchpadLedgerOptions } from "./launchpadledger";
export declare class LedgerSigner implements OfflineSigner {
private readonly ledger;
private readonly hdPaths;
private accounts?;
constructor(options?: LaunchpadLedgerOptions);
constructor(transport: Transport, options?: LaunchpadLedgerOptions);
getAccounts(): Promise<readonly AccountData[]>;
sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse>;
disconnect(): Promise<void>;
}
+28
View File
@@ -315,6 +315,15 @@
"@ledgerhq/logs" "^5.23.0"
rxjs "^6.6.3"
"@ledgerhq/devices@^5.25.0":
version "5.25.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.25.0.tgz#c9522d7495e7be41fad59eb7d5e4e3e1030fd205"
integrity sha512-3wsxVDkUYd5ERA5KVy99wEGbHAkfsuWbQ8b2ruzr8JX9wIxUS8mu1XkdoDt5M+H4jXP9C8ZGj9YT3VsiiIzfxg==
dependencies:
"@ledgerhq/errors" "^5.25.0"
"@ledgerhq/logs" "^5.25.0"
rxjs "^6.6.3"
"@ledgerhq/errors@^4.78.0":
version "4.78.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-4.78.0.tgz#23daf3af54d03b1bda3e616002b555da1bdb705a"
@@ -325,6 +334,11 @@
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.23.0.tgz#30a0338dafba8264556011604abed08bf24979f3"
integrity sha512-qtpX8aFrUUlYfOMu7BxTvxqUa8CniE+tEBpVEjYUhVbFdVJjM4ouwJD++RtQkMAU2c5jE7xb12WnUnf5BlAgLQ==
"@ledgerhq/errors@^5.25.0":
version "5.25.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.25.0.tgz#2a24b7ad6a88719046de4fcdf0b4c27b9ba79523"
integrity sha512-+FyyweA9odaWRwRkS35Wf0n2gC4tGsBlgW6VMhT7m1kcwVzUqNFyQzqMKqYvYEAga0P0X59wE+IAlqokkG7Usg==
"@ledgerhq/hw-transport-node-hid-noevents@^5.23.2":
version "5.23.2"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.23.2.tgz#0ee4b54c62d23b0f0cce258fe2c612f71b8d454a"
@@ -378,6 +392,15 @@
"@ledgerhq/errors" "^5.23.0"
events "^3.2.0"
"@ledgerhq/hw-transport@^5.25.0":
version "5.25.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.25.0.tgz#e04e2130fedcb433ee68f25125e6ac49efa0aede"
integrity sha512-q6hhWWwbSZPmzdTaQMowY+uQYwCC4xRqRQFQFgF6BjZFn2n+AwGOEU9mDmHnjL6XxkQfj8CjWuPJSRRv5OLmlg==
dependencies:
"@ledgerhq/devices" "^5.25.0"
"@ledgerhq/errors" "^5.25.0"
events "^3.2.0"
"@ledgerhq/logs@^4.72.0":
version "4.72.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-4.72.0.tgz#43df23af013ad1135407e5cf33ca6e4c4c7708d5"
@@ -388,6 +411,11 @@
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.23.0.tgz#7a86b1e6479c8aa8e8b9affe00eb8e369efdbc3b"
integrity sha512-88M8RkVHl44k6MAhfrYhx25opnJV24/2XpuTUVklID11f9rBdE+6RZ9OMs39dyX2sDv7TuzIPi5nTRoCqZMDYw==
"@ledgerhq/logs@^5.25.0":
version "5.25.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.25.0.tgz#94dd930a33fd05e3897532f554e31397a45247b6"
integrity sha512-OPlkvufjwJWQR+Jp7C6GaEeub+ZHzh3x0xyQMVLL62O7VGrhpzjwq2il/aLNt687k7mKJ+Vg6lFL1B6gO1ZiIg==
"@lerna/add@3.21.0":
version "3.21.0"
resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b"