@metamask/eth-ledger-bridge-keyring 0.14.0 → 1.0.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/CHANGELOG.md +81 -0
- package/README.md +7 -14
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger-bridge.d.ts +34 -0
- package/dist/ledger-bridge.js +3 -0
- package/dist/ledger-bridge.js.map +1 -0
- package/dist/ledger-iframe-bridge.d.ts +85 -0
- package/dist/ledger-iframe-bridge.js +150 -0
- package/dist/ledger-iframe-bridge.js.map +1 -0
- package/dist/ledger-keyring.d.ts +91 -0
- package/dist/ledger-keyring.js +531 -0
- package/dist/ledger-keyring.js.map +1 -0
- package/package.json +68 -36
- package/index.js +0 -651
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.0.0]
|
|
10
|
+
### Changed
|
|
11
|
+
- **BREAKING:** Separate the bridge from the keyring ([#156](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/156))
|
|
12
|
+
- The Ledger bridge is now a separate class (`LedgerIframeBridge`), which must be constructed separately from the keyring and passed in as a constructor argument.
|
|
13
|
+
- The bridge initialization has been moved from the keyring constructor to the keyring `init` method. The bridge is expected to be passed to the keyring uninitialized, and the keyring `init` method is expected to be called after keyring construction (before the keyring is used).
|
|
14
|
+
- The keyring constructor no longer accepts keyring state. Instead, any pre-existing keyring state should be passed to the `deserialize` method after construction.
|
|
15
|
+
- **BREAKING:** Export changed from default to named ([#174](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/174))
|
|
16
|
+
- The keyring is exported as `LedgerKeyring`
|
|
17
|
+
- Add TypeScript types ([#174](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/174))
|
|
18
|
+
|
|
19
|
+
## [0.15.0]
|
|
20
|
+
### Changed
|
|
21
|
+
- **BREAKING:** @ethereumjs/tx upgraded to major version 4, which includes a shift from storing numerical values as BNs to storing them as native BigInts. This is a breaking change for users of this keyring who access the values of the tx object, or that use those tx objects to interact with other libraries that depend on @ethereumsjs/tx versions under 4.0.0. ([#181](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/181))
|
|
22
|
+
|
|
23
|
+
## [0.14.0]
|
|
24
|
+
### Changed
|
|
25
|
+
- **BREAKING:** The minimum version of Node.js required for this package has been bumped to v14. ([#169](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/169))
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Fix incorrect `v` for EIP-712 signatures and `personal_sign` ([#152](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/152))
|
|
29
|
+
|
|
30
|
+
## [0.13.0]
|
|
31
|
+
### Added
|
|
32
|
+
- hdk.publicKey and hdk.chainCode should not be updated when unlocking using hdPath for an account. ([#146](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/146))
|
|
33
|
+
|
|
34
|
+
## [0.12.0]
|
|
35
|
+
### Added
|
|
36
|
+
- Add a new `destroy` method which will remove the `message` event listener from window. ([#145](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/145))
|
|
37
|
+
|
|
38
|
+
## [0.11.0]
|
|
39
|
+
### Added
|
|
40
|
+
- Add a new `isConnected` method which allows determining if the device is last known to be connected. ([#131](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/131))
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Messaging now runs off of message IDs instead of assuming the response received is from the last message sent, which will not always been true. ([#132](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/132))
|
|
44
|
+
|
|
45
|
+
## [0.10.0]
|
|
46
|
+
### Added
|
|
47
|
+
- Add a new `attemptMakeApp` method which allows clients to attempt a creation of the Ledger transport for the purposes of detecting/catching potential connection errors. ([#126](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/126))
|
|
48
|
+
|
|
49
|
+
## [0.9.0]
|
|
50
|
+
### Changed
|
|
51
|
+
- `updateTransportMethod` no longer defaults its parameter to false, and now names the param sent with the `'ledger-update-transport'` message `transportType`. This better is to support the use of an enum, instead of a boolean, for specifying transport preferences. ([#114](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/114))
|
|
52
|
+
|
|
53
|
+
## [0.8.0]
|
|
54
|
+
### Added
|
|
55
|
+
- Allow ledger-bridge iframe to connect Ledger wia WebHID, when it is supported by the current browser ([#107](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/107))
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
- Reject with an Error object if unlocking is not successful ([#104](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/104))
|
|
59
|
+
- Ensure that logs of errors only have a single `Error:` string in the message ([#105](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/105))
|
|
60
|
+
|
|
61
|
+
## [0.7.0]
|
|
62
|
+
### Changed
|
|
63
|
+
- Remove unused `events` and `ethereumjs-tx` dependencies ([#101](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/101), [#102](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/102))
|
|
64
|
+
- Update eth-ledger-bridge-keyring to support EIP-1559 transactions ([#98](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/98), [#97](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/97), [#96](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/96))
|
|
65
|
+
|
|
66
|
+
## [0.6.0]
|
|
67
|
+
### Added
|
|
68
|
+
- Support new versions of ethereumjs/tx ([#68](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/68))
|
|
69
|
+
|
|
70
|
+
[Unreleased]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v1.0.0...HEAD
|
|
71
|
+
[1.0.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.15.0...v1.0.0
|
|
72
|
+
[0.15.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.14.0...v0.15.0
|
|
73
|
+
[0.14.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.13.0...v0.14.0
|
|
74
|
+
[0.13.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.12.0...v0.13.0
|
|
75
|
+
[0.12.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.11.0...v0.12.0
|
|
76
|
+
[0.11.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.10.0...v0.11.0
|
|
77
|
+
[0.10.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.9.0...v0.10.0
|
|
78
|
+
[0.9.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.8.0...v0.9.0
|
|
79
|
+
[0.8.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.7.0...v0.8.0
|
|
80
|
+
[0.7.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/compare/v0.6.0...v0.7.0
|
|
81
|
+
[0.6.0]: https://github.com/MetaMask/eth-ledger-bridge-keyring/releases/tag/v0.6.0
|
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
eth-ledger-bridge-keyring [](https://circleci.com/gh/MetaMask/eth-ledger-bridge-keyring)
|
|
2
|
-
==================
|
|
1
|
+
# eth-ledger-bridge-keyring [](https://circleci.com/gh/MetaMask/eth-ledger-bridge-keyring)
|
|
3
2
|
|
|
4
3
|
An implementation of MetaMask's [Keyring interface](https://github.com/MetaMask/eth-simple-keyring#the-keyring-class-protocol), that uses a Ledger hardware wallet for all cryptographic operations.
|
|
5
4
|
|
|
@@ -16,17 +15,15 @@ device. However there are a number of differences:
|
|
|
16
15
|
|
|
17
16
|
- Because extensions have limited access to browser features, there's no easy way to interact wth the Ledger Hardware wallet from the MetaMask extension. This library implements a workaround to those restrictions by injecting (on demand) an iframe to the background page of the extension, (which is hosted [here](https://metamask.github.io/eth-ledger-bridge-keyring/index.html).
|
|
18
17
|
|
|
19
|
-
The iframe is allowed to interact with the Ledger device (since U2F requires SSL and the iframe is hosted under https) using the libraries from [LedgerJS](https://github.com/LedgerHQ/ledgerjs)
|
|
18
|
+
The iframe is allowed to interact with the Ledger device (since U2F requires SSL and the iframe is hosted under https) using the libraries from [LedgerJS](https://github.com/LedgerHQ/ledgerjs) _hw-app-eth_ and _hw-transport-u2f_ and establishes a two-way communication channel with the extension via postMessage.
|
|
20
19
|
|
|
21
20
|
The iframe code it's hosted in the same repo under the branch [gh-pages](https://github.com/MetaMask/eth-ledger-bridge-keyring/tree/gh-pages) and it's being served via github pages. In the future we might move it under the metamask.io domain.
|
|
22
21
|
|
|
23
|
-
Usage
|
|
24
|
-
-----
|
|
22
|
+
## Usage
|
|
25
23
|
|
|
26
24
|
In addition to all the known methods from the [Keyring class protocol](https://github.com/MetaMask/eth-simple-keyring#the-keyring-class-protocol),
|
|
27
25
|
there are a few others:
|
|
28
26
|
|
|
29
|
-
|
|
30
27
|
- **isUnlocked** : Returns true if we have the public key in memory, which allows to generate the list of accounts at any time
|
|
31
28
|
|
|
32
29
|
- **unlock** : Connects to the Ledger device and exports the extended public key, which is later used to read the available ethereum addresses inside the Ledger account.
|
|
@@ -41,16 +38,12 @@ there are a few others:
|
|
|
41
38
|
|
|
42
39
|
- **forgetDevice** : removes all the device info from memory so the next interaction with the keyring will prompt the user to connect the Ledger device and export the account information
|
|
43
40
|
|
|
44
|
-
Testing
|
|
45
|
-
-------
|
|
46
|
-
Run the following command:
|
|
41
|
+
## Testing and Linting
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
yarn test
|
|
50
|
-
```
|
|
43
|
+
Run `yarn test` to run the tests once. To run tests on file changes, run `yarn test:watch`.
|
|
51
44
|
|
|
45
|
+
Run `yarn lint` to run the linter, or run `yarn lint:fix` to run the linter and fix any automatically fixable issues.
|
|
52
46
|
|
|
47
|
+
## Attributions
|
|
53
48
|
|
|
54
|
-
Attributions
|
|
55
|
-
-------
|
|
56
49
|
This code was inspired by [eth-ledger-keyring](https://github.com/jamespic/eth-ledger-keyring) and [eth-hd-keyring](https://github.com/MetaMask/eth-hd-keyring)
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ledger-keyring"), exports);
|
|
18
|
+
__exportStar(require("./ledger-iframe-bridge"), exports);
|
|
19
|
+
__exportStar(require("./ledger-bridge"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,yDAAuC;AACvC,kDAAgC","sourcesContent":["export * from './ledger-keyring';\nexport * from './ledger-iframe-bridge';\nexport * from './ledger-bridge';\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';
|
|
2
|
+
export declare type GetPublicKeyParams = {
|
|
3
|
+
hdPath: string;
|
|
4
|
+
};
|
|
5
|
+
export declare type GetPublicKeyResponse = Awaited<ReturnType<LedgerHwAppEth['getAddress']>> & {
|
|
6
|
+
chainCode: string;
|
|
7
|
+
};
|
|
8
|
+
export declare type LedgerSignTransactionParams = {
|
|
9
|
+
hdPath: string;
|
|
10
|
+
tx: string;
|
|
11
|
+
};
|
|
12
|
+
export declare type LedgerSignTransactionResponse = Awaited<ReturnType<LedgerHwAppEth['signTransaction']>>;
|
|
13
|
+
export declare type LedgerSignMessageParams = {
|
|
14
|
+
hdPath: string;
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
export declare type LedgerSignMessageResponse = Awaited<ReturnType<LedgerHwAppEth['signPersonalMessage']>>;
|
|
18
|
+
export declare type LedgerSignTypedDataParams = {
|
|
19
|
+
hdPath: string;
|
|
20
|
+
domainSeparatorHex: string;
|
|
21
|
+
hashStructMessageHex: string;
|
|
22
|
+
};
|
|
23
|
+
export declare type LedgerSignTypedDataResponse = Awaited<ReturnType<LedgerHwAppEth['signEIP712HashedMessage']>>;
|
|
24
|
+
export interface LedgerBridge {
|
|
25
|
+
isDeviceConnected: boolean;
|
|
26
|
+
init(bridgeUrl: string): Promise<void>;
|
|
27
|
+
destroy(): Promise<void>;
|
|
28
|
+
attemptMakeApp(): Promise<boolean>;
|
|
29
|
+
updateTransportMethod(transportType: string): Promise<boolean>;
|
|
30
|
+
getPublicKey(params: GetPublicKeyParams): Promise<GetPublicKeyResponse>;
|
|
31
|
+
deviceSignTransaction(params: LedgerSignTransactionParams): Promise<LedgerSignTransactionResponse>;
|
|
32
|
+
deviceSignMessage(params: LedgerSignMessageParams): Promise<LedgerSignMessageResponse>;
|
|
33
|
+
deviceSignTypedData(params: LedgerSignTypedDataParams): Promise<LedgerSignTypedDataResponse>;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\n\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType<LedgerHwAppEth['getAddress']>\n> & {\n chainCode: string;\n};\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType<LedgerHwAppEth['signTransaction']>\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType<LedgerHwAppEth['signPersonalMessage']>\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType<LedgerHwAppEth['signEIP712HashedMessage']>\n>;\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface LedgerBridge {\n isDeviceConnected: boolean;\n\n init(bridgeUrl: string): Promise<void>;\n\n destroy(): Promise<void>;\n\n attemptMakeApp(): Promise<boolean>;\n\n updateTransportMethod(transportType: string): Promise<boolean>;\n\n getPublicKey(params: GetPublicKeyParams): Promise<GetPublicKeyResponse>;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise<LedgerSignTransactionResponse>;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise<LedgerSignMessageResponse>;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise<LedgerSignTypedDataResponse>;\n}\n"]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { GetPublicKeyParams, GetPublicKeyResponse, LedgerBridge, LedgerSignMessageParams, LedgerSignMessageResponse, LedgerSignTransactionParams, LedgerSignTransactionResponse, LedgerSignTypedDataParams, LedgerSignTypedDataResponse } from './ledger-bridge';
|
|
2
|
+
export declare enum IFrameMessageAction {
|
|
3
|
+
LedgerConnectionChange = "ledger-connection-change",
|
|
4
|
+
LedgerUnlock = "ledger-unlock",
|
|
5
|
+
LedgerMakeApp = "ledger-make-app",
|
|
6
|
+
LedgerUpdateTransport = "ledger-update-transport",
|
|
7
|
+
LedgerSignTransaction = "ledger-sign-transaction",
|
|
8
|
+
LedgerSignPersonalMessage = "ledger-sign-personal-message",
|
|
9
|
+
LedgerSignTypedData = "ledger-sign-typed-data"
|
|
10
|
+
}
|
|
11
|
+
declare type IFrameMessageResponse<TAction extends IFrameMessageAction> = {
|
|
12
|
+
action: TAction;
|
|
13
|
+
messageId: number;
|
|
14
|
+
} & ({
|
|
15
|
+
action: IFrameMessageAction.LedgerConnectionChange;
|
|
16
|
+
payload: {
|
|
17
|
+
connected: boolean;
|
|
18
|
+
};
|
|
19
|
+
} | ({
|
|
20
|
+
action: IFrameMessageAction.LedgerMakeApp;
|
|
21
|
+
} & ({
|
|
22
|
+
success: true;
|
|
23
|
+
} | {
|
|
24
|
+
success: false;
|
|
25
|
+
error?: unknown;
|
|
26
|
+
})) | {
|
|
27
|
+
action: IFrameMessageAction.LedgerUpdateTransport;
|
|
28
|
+
success: boolean;
|
|
29
|
+
} | ({
|
|
30
|
+
action: IFrameMessageAction.LedgerUnlock;
|
|
31
|
+
} & ({
|
|
32
|
+
success: true;
|
|
33
|
+
payload: GetPublicKeyResponse;
|
|
34
|
+
} | {
|
|
35
|
+
success: false;
|
|
36
|
+
payload: {
|
|
37
|
+
error: Error;
|
|
38
|
+
};
|
|
39
|
+
})) | ({
|
|
40
|
+
action: IFrameMessageAction.LedgerSignTransaction;
|
|
41
|
+
} & ({
|
|
42
|
+
success: true;
|
|
43
|
+
payload: LedgerSignTransactionResponse;
|
|
44
|
+
} | {
|
|
45
|
+
success: false;
|
|
46
|
+
payload: {
|
|
47
|
+
error: Error;
|
|
48
|
+
};
|
|
49
|
+
})) | ({
|
|
50
|
+
action: IFrameMessageAction.LedgerSignPersonalMessage | IFrameMessageAction.LedgerSignTypedData;
|
|
51
|
+
} & ({
|
|
52
|
+
success: true;
|
|
53
|
+
payload: LedgerSignMessageResponse | LedgerSignTypedDataResponse;
|
|
54
|
+
} | {
|
|
55
|
+
success: false;
|
|
56
|
+
payload: {
|
|
57
|
+
error: Error;
|
|
58
|
+
};
|
|
59
|
+
})));
|
|
60
|
+
export declare class LedgerIframeBridge implements LedgerBridge {
|
|
61
|
+
#private;
|
|
62
|
+
iframe?: HTMLIFrameElement;
|
|
63
|
+
iframeLoaded: boolean;
|
|
64
|
+
eventListener?: (eventMessage: {
|
|
65
|
+
origin: string;
|
|
66
|
+
data: IFrameMessageResponse<IFrameMessageAction>;
|
|
67
|
+
}) => void;
|
|
68
|
+
isDeviceConnected: boolean;
|
|
69
|
+
currentMessageId: number;
|
|
70
|
+
messageCallbacks: Record<number, (response: IFrameMessageResponse<IFrameMessageAction>) => void>;
|
|
71
|
+
delayedPromise?: {
|
|
72
|
+
resolve: (value: boolean) => void;
|
|
73
|
+
reject: (error: unknown) => void;
|
|
74
|
+
transportType: string;
|
|
75
|
+
};
|
|
76
|
+
init(bridgeUrl: string): Promise<void>;
|
|
77
|
+
destroy(): Promise<void>;
|
|
78
|
+
attemptMakeApp(): Promise<boolean>;
|
|
79
|
+
updateTransportMethod(transportType: string): Promise<boolean>;
|
|
80
|
+
getPublicKey(params: GetPublicKeyParams): Promise<GetPublicKeyResponse>;
|
|
81
|
+
deviceSignTransaction(params: LedgerSignTransactionParams): Promise<LedgerSignTransactionResponse>;
|
|
82
|
+
deviceSignMessage(params: LedgerSignMessageParams): Promise<LedgerSignMessageResponse>;
|
|
83
|
+
deviceSignTypedData(params: LedgerSignTypedDataParams): Promise<LedgerSignTypedDataResponse>;
|
|
84
|
+
}
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _LedgerIframeBridge_instances, _LedgerIframeBridge_deviceActionMessage, _LedgerIframeBridge_setupIframe, _LedgerIframeBridge_getOrigin, _LedgerIframeBridge_eventListener, _LedgerIframeBridge_sendMessage;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.LedgerIframeBridge = exports.IFrameMessageAction = void 0;
|
|
10
|
+
const LEDGER_IFRAME_ID = 'LEDGER-IFRAME';
|
|
11
|
+
var IFrameMessageAction;
|
|
12
|
+
(function (IFrameMessageAction) {
|
|
13
|
+
IFrameMessageAction["LedgerConnectionChange"] = "ledger-connection-change";
|
|
14
|
+
IFrameMessageAction["LedgerUnlock"] = "ledger-unlock";
|
|
15
|
+
IFrameMessageAction["LedgerMakeApp"] = "ledger-make-app";
|
|
16
|
+
IFrameMessageAction["LedgerUpdateTransport"] = "ledger-update-transport";
|
|
17
|
+
IFrameMessageAction["LedgerSignTransaction"] = "ledger-sign-transaction";
|
|
18
|
+
IFrameMessageAction["LedgerSignPersonalMessage"] = "ledger-sign-personal-message";
|
|
19
|
+
IFrameMessageAction["LedgerSignTypedData"] = "ledger-sign-typed-data";
|
|
20
|
+
})(IFrameMessageAction = exports.IFrameMessageAction || (exports.IFrameMessageAction = {}));
|
|
21
|
+
class LedgerIframeBridge {
|
|
22
|
+
constructor() {
|
|
23
|
+
_LedgerIframeBridge_instances.add(this);
|
|
24
|
+
this.iframeLoaded = false;
|
|
25
|
+
this.isDeviceConnected = false;
|
|
26
|
+
this.currentMessageId = 0;
|
|
27
|
+
this.messageCallbacks = {};
|
|
28
|
+
}
|
|
29
|
+
async init(bridgeUrl) {
|
|
30
|
+
__classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_setupIframe).call(this, bridgeUrl);
|
|
31
|
+
this.eventListener = __classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_eventListener).bind(this, bridgeUrl);
|
|
32
|
+
window.addEventListener('message', this.eventListener);
|
|
33
|
+
}
|
|
34
|
+
async destroy() {
|
|
35
|
+
if (this.eventListener) {
|
|
36
|
+
window.removeEventListener('message', this.eventListener);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async attemptMakeApp() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
__classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_sendMessage).call(this, {
|
|
42
|
+
action: IFrameMessageAction.LedgerMakeApp,
|
|
43
|
+
}, (response) => {
|
|
44
|
+
if (response.success) {
|
|
45
|
+
resolve(true);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
reject(response.error);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async updateTransportMethod(transportType) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
// If the iframe isn't loaded yet, let's store the desired transportType value and
|
|
56
|
+
// optimistically return a successful promise
|
|
57
|
+
if (!this.iframeLoaded) {
|
|
58
|
+
this.delayedPromise = {
|
|
59
|
+
resolve,
|
|
60
|
+
reject,
|
|
61
|
+
transportType,
|
|
62
|
+
};
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
__classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_sendMessage).call(this, {
|
|
66
|
+
action: IFrameMessageAction.LedgerUpdateTransport,
|
|
67
|
+
params: { transportType },
|
|
68
|
+
}, ({ success }) => {
|
|
69
|
+
if (success) {
|
|
70
|
+
return resolve(true);
|
|
71
|
+
}
|
|
72
|
+
return reject(new Error('Ledger transport could not be updated'));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async getPublicKey(params) {
|
|
77
|
+
return __classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_deviceActionMessage).call(this, IFrameMessageAction.LedgerUnlock, params);
|
|
78
|
+
}
|
|
79
|
+
async deviceSignTransaction(params) {
|
|
80
|
+
return __classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_deviceActionMessage).call(this, IFrameMessageAction.LedgerSignTransaction, params);
|
|
81
|
+
}
|
|
82
|
+
async deviceSignMessage(params) {
|
|
83
|
+
return __classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_deviceActionMessage).call(this, IFrameMessageAction.LedgerSignPersonalMessage, params);
|
|
84
|
+
}
|
|
85
|
+
async deviceSignTypedData(params) {
|
|
86
|
+
return __classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_deviceActionMessage).call(this, IFrameMessageAction.LedgerSignTypedData, params);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.LedgerIframeBridge = LedgerIframeBridge;
|
|
90
|
+
_LedgerIframeBridge_instances = new WeakSet(), _LedgerIframeBridge_deviceActionMessage = async function _LedgerIframeBridge_deviceActionMessage(...[action, params]) {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
__classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_sendMessage).call(this, {
|
|
93
|
+
action,
|
|
94
|
+
params,
|
|
95
|
+
}, ({ success, payload }) => {
|
|
96
|
+
if (success) {
|
|
97
|
+
return resolve(payload);
|
|
98
|
+
}
|
|
99
|
+
return reject(payload.error);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}, _LedgerIframeBridge_setupIframe = function _LedgerIframeBridge_setupIframe(bridgeUrl) {
|
|
103
|
+
this.iframe = document.createElement('iframe');
|
|
104
|
+
this.iframe.src = bridgeUrl;
|
|
105
|
+
this.iframe.allow = `hid 'src'`;
|
|
106
|
+
this.iframe.onload = async () => {
|
|
107
|
+
// If the ledger live preference was set before the iframe is loaded,
|
|
108
|
+
// set it after the iframe has loaded
|
|
109
|
+
this.iframeLoaded = true;
|
|
110
|
+
if (this.delayedPromise) {
|
|
111
|
+
try {
|
|
112
|
+
const result = await this.updateTransportMethod(this.delayedPromise.transportType);
|
|
113
|
+
this.delayedPromise.resolve(result);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
this.delayedPromise.reject(error);
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
delete this.delayedPromise;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
document.head.appendChild(this.iframe);
|
|
124
|
+
}, _LedgerIframeBridge_getOrigin = function _LedgerIframeBridge_getOrigin(bridgeUrl) {
|
|
125
|
+
const tmp = bridgeUrl.split('/');
|
|
126
|
+
tmp.splice(-1, 1);
|
|
127
|
+
return tmp.join('/');
|
|
128
|
+
}, _LedgerIframeBridge_eventListener = function _LedgerIframeBridge_eventListener(bridgeUrl, eventMessage) {
|
|
129
|
+
if (eventMessage.origin !== __classPrivateFieldGet(this, _LedgerIframeBridge_instances, "m", _LedgerIframeBridge_getOrigin).call(this, bridgeUrl)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (eventMessage.data) {
|
|
133
|
+
const messageCallback = this.messageCallbacks[eventMessage.data.messageId];
|
|
134
|
+
if (messageCallback) {
|
|
135
|
+
messageCallback(eventMessage.data);
|
|
136
|
+
}
|
|
137
|
+
else if (eventMessage.data.action === IFrameMessageAction.LedgerConnectionChange) {
|
|
138
|
+
this.isDeviceConnected = eventMessage.data.payload.connected;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}, _LedgerIframeBridge_sendMessage = function _LedgerIframeBridge_sendMessage(message, callback) {
|
|
142
|
+
this.currentMessageId += 1;
|
|
143
|
+
const postMsg = Object.assign(Object.assign({}, message), { messageId: this.currentMessageId, target: LEDGER_IFRAME_ID });
|
|
144
|
+
this.messageCallbacks[this.currentMessageId] = callback;
|
|
145
|
+
if (!this.iframeLoaded || !this.iframe || !this.iframe.contentWindow) {
|
|
146
|
+
throw new Error('The iframe is not loaded yet');
|
|
147
|
+
}
|
|
148
|
+
this.iframe.contentWindow.postMessage(postMsg, '*');
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=ledger-iframe-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-iframe-bridge.js","sourceRoot":"","sources":["../src/ledger-iframe-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetPublicKeyParams,\n GetPublicKeyResponse,\n LedgerBridge,\n LedgerSignMessageParams,\n LedgerSignMessageResponse,\n LedgerSignTransactionParams,\n LedgerSignTransactionResponse,\n LedgerSignTypedDataParams,\n LedgerSignTypedDataResponse,\n} from './ledger-bridge';\n\nconst LEDGER_IFRAME_ID = 'LEDGER-IFRAME';\n\nexport enum IFrameMessageAction {\n LedgerConnectionChange = 'ledger-connection-change',\n LedgerUnlock = 'ledger-unlock',\n LedgerMakeApp = 'ledger-make-app',\n LedgerUpdateTransport = 'ledger-update-transport',\n LedgerSignTransaction = 'ledger-sign-transaction',\n LedgerSignPersonalMessage = 'ledger-sign-personal-message',\n LedgerSignTypedData = 'ledger-sign-typed-data',\n}\n\ntype IFrameMessageResponse<TAction extends IFrameMessageAction> = {\n action: TAction;\n messageId: number;\n} & (\n | {\n action: IFrameMessageAction.LedgerConnectionChange;\n payload: { connected: boolean };\n }\n | ({\n action: IFrameMessageAction.LedgerMakeApp;\n } & ({ success: true } | { success: false; error?: unknown }))\n | {\n action: IFrameMessageAction.LedgerUpdateTransport;\n success: boolean;\n }\n | ({\n action: IFrameMessageAction.LedgerUnlock;\n } & (\n | { success: true; payload: GetPublicKeyResponse }\n | { success: false; payload: { error: Error } }\n ))\n | ({\n action: IFrameMessageAction.LedgerSignTransaction;\n } & (\n | { success: true; payload: LedgerSignTransactionResponse }\n | { success: false; payload: { error: Error } }\n ))\n | ({\n action:\n | IFrameMessageAction.LedgerSignPersonalMessage\n | IFrameMessageAction.LedgerSignTypedData;\n } & (\n | {\n success: true;\n payload: LedgerSignMessageResponse | LedgerSignTypedDataResponse;\n }\n | { success: false; payload: { error: Error } }\n ))\n);\n\ntype IFrameMessage<TAction extends IFrameMessageAction> = {\n action: TAction;\n params?: Readonly<Record<string, unknown>>;\n};\n\ntype IFramePostMessage<TAction extends IFrameMessageAction> =\n IFrameMessage<TAction> & {\n messageId: number;\n target: typeof LEDGER_IFRAME_ID;\n };\n\nexport class LedgerIframeBridge implements LedgerBridge {\n iframe?: HTMLIFrameElement;\n\n iframeLoaded = false;\n\n eventListener?: (eventMessage: {\n origin: string;\n data: IFrameMessageResponse<IFrameMessageAction>;\n }) => void;\n\n isDeviceConnected = false;\n\n currentMessageId = 0;\n\n messageCallbacks: Record<\n number,\n (response: IFrameMessageResponse<IFrameMessageAction>) => void\n > = {};\n\n delayedPromise?: {\n resolve: (value: boolean) => void;\n reject: (error: unknown) => void;\n transportType: string;\n };\n\n async init(bridgeUrl: string) {\n this.#setupIframe(bridgeUrl);\n\n this.eventListener = this.#eventListener.bind(this, bridgeUrl);\n\n window.addEventListener('message', this.eventListener);\n }\n\n async destroy() {\n if (this.eventListener) {\n window.removeEventListener('message', this.eventListener);\n }\n }\n\n async attemptMakeApp(): Promise<boolean> {\n return new Promise((resolve, reject) => {\n this.#sendMessage(\n {\n action: IFrameMessageAction.LedgerMakeApp,\n },\n (response) => {\n if (response.success) {\n resolve(true);\n } else {\n reject(response.error);\n }\n },\n );\n });\n }\n\n async updateTransportMethod(transportType: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n // If the iframe isn't loaded yet, let's store the desired transportType value and\n // optimistically return a successful promise\n if (!this.iframeLoaded) {\n this.delayedPromise = {\n resolve,\n reject,\n transportType,\n };\n return;\n }\n\n this.#sendMessage(\n {\n action: IFrameMessageAction.LedgerUpdateTransport,\n params: { transportType },\n },\n ({ success }) => {\n if (success) {\n return resolve(true);\n }\n return reject(new Error('Ledger transport could not be updated'));\n },\n );\n });\n }\n\n async getPublicKey(\n params: GetPublicKeyParams,\n ): Promise<GetPublicKeyResponse> {\n return this.#deviceActionMessage(IFrameMessageAction.LedgerUnlock, params);\n }\n\n async deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise<LedgerSignTransactionResponse> {\n return this.#deviceActionMessage(\n IFrameMessageAction.LedgerSignTransaction,\n params,\n );\n }\n\n async deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise<LedgerSignMessageResponse> {\n return this.#deviceActionMessage(\n IFrameMessageAction.LedgerSignPersonalMessage,\n params,\n );\n }\n\n async deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise<LedgerSignTypedDataResponse> {\n return this.#deviceActionMessage(\n IFrameMessageAction.LedgerSignTypedData,\n params,\n );\n }\n\n async #deviceActionMessage(\n action: IFrameMessageAction.LedgerUnlock,\n params: GetPublicKeyParams,\n ): Promise<GetPublicKeyResponse>;\n\n async #deviceActionMessage(\n action: IFrameMessageAction.LedgerSignTransaction,\n params: LedgerSignTransactionParams,\n ): Promise<LedgerSignTransactionResponse>;\n\n async #deviceActionMessage(\n action: IFrameMessageAction.LedgerSignPersonalMessage,\n params: LedgerSignMessageParams,\n ): Promise<LedgerSignMessageResponse>;\n\n async #deviceActionMessage(\n action: IFrameMessageAction.LedgerSignTypedData,\n params: LedgerSignTypedDataParams,\n ): Promise<LedgerSignTypedDataResponse>;\n\n async #deviceActionMessage(\n ...[action, params]:\n | [IFrameMessageAction.LedgerUnlock, GetPublicKeyParams]\n | [IFrameMessageAction.LedgerSignTransaction, LedgerSignTransactionParams]\n | [IFrameMessageAction.LedgerSignPersonalMessage, LedgerSignMessageParams]\n | [IFrameMessageAction.LedgerSignTypedData, LedgerSignTypedDataParams]\n ) {\n return new Promise((resolve, reject) => {\n this.#sendMessage(\n {\n action,\n params,\n },\n ({ success, payload }) => {\n if (success) {\n return resolve(payload);\n }\n return reject(payload.error);\n },\n );\n });\n }\n\n #setupIframe(bridgeUrl: string) {\n this.iframe = document.createElement('iframe');\n this.iframe.src = bridgeUrl;\n this.iframe.allow = `hid 'src'`;\n this.iframe.onload = async () => {\n // If the ledger live preference was set before the iframe is loaded,\n // set it after the iframe has loaded\n this.iframeLoaded = true;\n if (this.delayedPromise) {\n try {\n const result = await this.updateTransportMethod(\n this.delayedPromise.transportType,\n );\n this.delayedPromise.resolve(result);\n } catch (error) {\n this.delayedPromise.reject(error);\n } finally {\n delete this.delayedPromise;\n }\n }\n };\n document.head.appendChild(this.iframe);\n }\n\n #getOrigin(bridgeUrl: string) {\n const tmp = bridgeUrl.split('/');\n tmp.splice(-1, 1);\n return tmp.join('/');\n }\n\n #eventListener(\n bridgeUrl: string,\n eventMessage: {\n origin: string;\n data: IFrameMessageResponse<IFrameMessageAction>;\n },\n ) {\n if (eventMessage.origin !== this.#getOrigin(bridgeUrl)) {\n return;\n }\n\n if (eventMessage.data) {\n const messageCallback =\n this.messageCallbacks[eventMessage.data.messageId];\n if (messageCallback) {\n messageCallback(eventMessage.data);\n } else if (\n eventMessage.data.action === IFrameMessageAction.LedgerConnectionChange\n ) {\n this.isDeviceConnected = eventMessage.data.payload.connected;\n }\n }\n }\n\n #sendMessage<TAction extends IFrameMessageAction>(\n message: IFrameMessage<TAction>,\n callback: (response: IFrameMessageResponse<TAction>) => void,\n ) {\n this.currentMessageId += 1;\n\n const postMsg: IFramePostMessage<TAction> = {\n ...message,\n messageId: this.currentMessageId,\n target: LEDGER_IFRAME_ID,\n };\n\n this.messageCallbacks[this.currentMessageId] = callback as (\n response: IFrameMessageResponse<IFrameMessageAction>,\n ) => void;\n\n if (!this.iframeLoaded || !this.iframe || !this.iframe.contentWindow) {\n throw new Error('The iframe is not loaded yet');\n }\n\n this.iframe.contentWindow.postMessage(postMsg, '*');\n }\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { TypedTransaction } from '@ethereumjs/tx';
|
|
3
|
+
import * as sigUtil from 'eth-sig-util';
|
|
4
|
+
import type OldEthJsTransaction from 'ethereumjs-tx';
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
import HDKey from 'hdkey';
|
|
7
|
+
import { LedgerBridge } from './ledger-bridge';
|
|
8
|
+
declare enum NetworkApiUrls {
|
|
9
|
+
Ropsten = "http://api-ropsten.etherscan.io",
|
|
10
|
+
Kovan = "http://api-kovan.etherscan.io",
|
|
11
|
+
Rinkeby = "https://api-rinkeby.etherscan.io",
|
|
12
|
+
Mainnet = "https://api.etherscan.io"
|
|
13
|
+
}
|
|
14
|
+
export declare type AccountDetails = {
|
|
15
|
+
index?: number;
|
|
16
|
+
bip44?: boolean;
|
|
17
|
+
hdPath?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare type LedgerBridgeKeyringOptions = {
|
|
20
|
+
hdPath: string;
|
|
21
|
+
accounts: readonly string[];
|
|
22
|
+
accountDetails: Readonly<Record<string, AccountDetails>>;
|
|
23
|
+
accountIndexes: Readonly<Record<string, number>>;
|
|
24
|
+
bridgeUrl: string;
|
|
25
|
+
implementFullBIP44: boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare class LedgerKeyring extends EventEmitter {
|
|
28
|
+
#private;
|
|
29
|
+
static type: string;
|
|
30
|
+
readonly type: string;
|
|
31
|
+
page: number;
|
|
32
|
+
perPage: number;
|
|
33
|
+
unlockedAccount: number;
|
|
34
|
+
accounts: readonly string[];
|
|
35
|
+
accountDetails: Record<string, AccountDetails>;
|
|
36
|
+
hdk: HDKey;
|
|
37
|
+
hdPath: string;
|
|
38
|
+
paths: Record<string, number>;
|
|
39
|
+
network: NetworkApiUrls;
|
|
40
|
+
implementFullBIP44: boolean;
|
|
41
|
+
bridgeUrl: string;
|
|
42
|
+
bridge: LedgerBridge;
|
|
43
|
+
constructor({ bridge }: {
|
|
44
|
+
bridge: LedgerBridge;
|
|
45
|
+
});
|
|
46
|
+
init(): Promise<void>;
|
|
47
|
+
destroy(): Promise<void>;
|
|
48
|
+
serialize(): Promise<{
|
|
49
|
+
hdPath: string;
|
|
50
|
+
accounts: readonly string[];
|
|
51
|
+
accountDetails: Record<string, AccountDetails>;
|
|
52
|
+
bridgeUrl: string;
|
|
53
|
+
implementFullBIP44: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
deserialize(opts?: Partial<LedgerBridgeKeyringOptions>): Promise<void>;
|
|
56
|
+
isUnlocked(): boolean;
|
|
57
|
+
isConnected(): boolean;
|
|
58
|
+
setAccountToUnlock(index: number | string): void;
|
|
59
|
+
setHdPath(hdPath: string): void;
|
|
60
|
+
unlock(hdPath?: string, updateHdk?: boolean): Promise<string>;
|
|
61
|
+
addAccounts(amount?: number): Promise<string[]>;
|
|
62
|
+
getFirstPage(): Promise<{
|
|
63
|
+
address: string;
|
|
64
|
+
balance: number | null;
|
|
65
|
+
index: number;
|
|
66
|
+
}[]>;
|
|
67
|
+
getNextPage(): Promise<{
|
|
68
|
+
address: string;
|
|
69
|
+
balance: number | null;
|
|
70
|
+
index: number;
|
|
71
|
+
}[]>;
|
|
72
|
+
getPreviousPage(): Promise<{
|
|
73
|
+
address: string;
|
|
74
|
+
balance: number | null;
|
|
75
|
+
index: number;
|
|
76
|
+
}[]>;
|
|
77
|
+
getAccounts(): Promise<string[]>;
|
|
78
|
+
removeAccount(address: string): void;
|
|
79
|
+
attemptMakeApp(): Promise<boolean>;
|
|
80
|
+
updateTransportMethod(transportType: string): Promise<boolean>;
|
|
81
|
+
signTransaction(address: string, tx: TypedTransaction | OldEthJsTransaction): Promise<TypedTransaction | OldEthJsTransaction>;
|
|
82
|
+
signMessage(withAccount: string, data: string): Promise<string>;
|
|
83
|
+
signPersonalMessage(withAccount: string, message: string): Promise<string>;
|
|
84
|
+
unlockAccountByAddress(address: string): Promise<string | undefined>;
|
|
85
|
+
signTypedData(withAccount: string, data: sigUtil.EIP712TypedData, options?: {
|
|
86
|
+
version?: string;
|
|
87
|
+
}): Promise<string>;
|
|
88
|
+
exportAccount(): void;
|
|
89
|
+
forgetDevice(): void;
|
|
90
|
+
}
|
|
91
|
+
export {};
|