@exodus/hw-ledger 1.0.0 → 1.2.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 +33 -0
- package/lib/index.d.ts +12 -2
- package/lib/index.js +12 -2
- package/lib/module/assets/bitcoin.d.ts +9 -0
- package/lib/module/assets/bitcoin.js +50 -0
- package/lib/module/assets/ethereum.d.ts +9 -0
- package/lib/module/assets/ethereum.js +118 -0
- package/lib/{assets → module/assets}/index.d.ts +6 -1
- package/lib/module/assets/index.js +10 -0
- package/lib/module/assets/polygon.d.ts +6 -0
- package/lib/module/assets/polygon.js +7 -0
- package/lib/module/assets/solana.d.ts +9 -0
- package/lib/module/assets/solana.js +36 -0
- package/lib/module/assets/utils.d.ts +6 -0
- package/lib/module/assets/utils.js +37 -0
- package/lib/{device.d.ts → module/device.d.ts} +7 -4
- package/lib/module/device.js +127 -0
- package/lib/{discovery.d.ts → module/discovery.d.ts} +2 -3
- package/lib/{discovery.js → module/discovery.js} +15 -9
- package/lib/module/errors.d.ts +13 -0
- package/lib/module/errors.js +19 -0
- package/lib/module/index.d.ts +2 -0
- package/lib/module/index.js +2 -0
- package/lib/module/management/application.d.ts +20 -0
- package/lib/module/management/application.js +68 -0
- package/lib/module/types.d.ts +2 -0
- package/lib/module/types.js +1 -0
- package/package.json +33 -14
- package/lib/assets/bitcoin.d.ts +0 -3
- package/lib/assets/bitcoin.js +0 -21
- package/lib/assets/index.js +0 -4
- package/lib/device.js +0 -33
- /package/lib/{constants.d.ts → module/constants.d.ts} +0 -0
- /package/lib/{constants.js → module/constants.js} +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [1.2.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@1.1.0...@exodus/hw-ledger@1.2.0) (2023-11-27)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- make hw-ledger a feature ([#4882](https://github.com/ExodusMovement/exodus-hydra/issues/4882)) ([33d93fb](https://github.com/ExodusMovement/exodus-hydra/commit/33d93fb0f4bb507e3788c0450a3e413e79815433))
|
|
11
|
+
|
|
12
|
+
## [1.1.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@1.0.0...@exodus/hw-ledger@1.1.0) (2023-08-21)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- **hw-ledger:** add application management functions ([#2586](https://github.com/ExodusMovement/exodus-hydra/issues/2586)) ([125e9ef](https://github.com/ExodusMovement/exodus-hydra/commit/125e9efc835a56a66375e43f9072404af28f466d))
|
|
17
|
+
- **hw-ledger:** add bitcoin transaction & message signing support ([#2530](https://github.com/ExodusMovement/exodus-hydra/issues/2530)) ([5d17826](https://github.com/ExodusMovement/exodus-hydra/commit/5d17826335f6e85ba3b9179bb7a2343f686751fb))
|
|
18
|
+
- **hw-ledger:** add ethereum transaction signing ([#2315](https://github.com/ExodusMovement/exodus-hydra/issues/2315)) ([5b5c60e](https://github.com/ExodusMovement/exodus-hydra/commit/5b5c60e950948d24cea672e37dc171e1d7348343))
|
|
19
|
+
- **hw-ledger:** add polygon support ([#2293](https://github.com/ExodusMovement/exodus-hydra/issues/2293)) ([fd66005](https://github.com/ExodusMovement/exodus-hydra/commit/fd660055b0813917dd4055c408f8731ea448ad4f))
|
|
20
|
+
- **hw-ledger:** add prebuilt firmware ([#2212](https://github.com/ExodusMovement/exodus-hydra/issues/2212)) ([80667a4](https://github.com/ExodusMovement/exodus-hydra/commit/80667a4237a103c08e55dbf634a193dda0875ca0))
|
|
21
|
+
- **hw-ledger:** add solana transaction & message signing ([#2357](https://github.com/ExodusMovement/exodus-hydra/issues/2357)) ([2ca2a4e](https://github.com/ExodusMovement/exodus-hydra/commit/2ca2a4e93f813b1777953355c4d271033e7d98d0))
|
|
22
|
+
- **hw-ledger:** add Taproot address support ([#2442](https://github.com/ExodusMovement/exodus-hydra/issues/2442)) ([4d8350e](https://github.com/ExodusMovement/exodus-hydra/commit/4d8350ecbdca500e6b69a36ce83c7b4c63bb0e8f))
|
|
23
|
+
- **hw-ledger:** automatically open the application ([#2801](https://github.com/ExodusMovement/exodus-hydra/issues/2801)) ([80e04b4](https://github.com/ExodusMovement/exodus-hydra/commit/80e04b49ee886cc3ca9b65695a567d06b0909928))
|
|
24
|
+
- **hw-ledger:** bitcoin address derivation ([#2181](https://github.com/ExodusMovement/exodus-hydra/issues/2181)) ([19b77ec](https://github.com/ExodusMovement/exodus-hydra/commit/19b77ec223d998454843d82c4c47a4350e77299e))
|
|
25
|
+
- **hw-ledger:** ethereum address generation ([#2213](https://github.com/ExodusMovement/exodus-hydra/issues/2213)) ([1c5b326](https://github.com/ExodusMovement/exodus-hydra/commit/1c5b326d7fbbcf66ff6cb820c35c1e1d1d127821))
|
|
26
|
+
- **hw-ledger:** ethereum message signing ([#3058](https://github.com/ExodusMovement/exodus-hydra/issues/3058)) ([df07d2d](https://github.com/ExodusMovement/exodus-hydra/commit/df07d2d861cc20c1db366b47dc98802dd8e73f9c))
|
|
27
|
+
- **hw-ledger:** solana address derivation ([#2211](https://github.com/ExodusMovement/exodus-hydra/issues/2211)) ([1c73d28](https://github.com/ExodusMovement/exodus-hydra/commit/1c73d28798801016966a8034a3538b9abc4cbf16))
|
|
28
|
+
- **ledger:** add ethereum xpub support ([#3403](https://github.com/ExodusMovement/exodus-hydra/issues/3403)) ([7de98d2](https://github.com/ExodusMovement/exodus-hydra/commit/7de98d22080bcb46a9fe88e30d631a5ace3a923d))
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
- **hw-ledger:** support NFT transactions on Nano S ([#2961](https://github.com/ExodusMovement/exodus-hydra/issues/2961)) ([1161a80](https://github.com/ExodusMovement/exodus-hydra/commit/1161a807c581211132d56ec2912bc27b683207d1))
|
|
33
|
+
- missing dependencies ([#2408](https://github.com/ExodusMovement/exodus-hydra/issues/2408)) ([7791f2a](https://github.com/ExodusMovement/exodus-hydra/commit/7791f2aa10f318b0a3c8f932ec686fbe0cf16617))
|
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
declare const hardwareWalletLedger: () => {
|
|
2
|
+
id: string;
|
|
3
|
+
definitions: {
|
|
4
|
+
definition: {
|
|
5
|
+
id: string;
|
|
6
|
+
type: string;
|
|
7
|
+
factory: (deps: import("./module").Dependencies) => import("./module").LedgerDiscovery;
|
|
8
|
+
dependencies: string[];
|
|
9
|
+
};
|
|
10
|
+
}[];
|
|
11
|
+
};
|
|
12
|
+
export default hardwareWalletLedger;
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import ledgerDiscoveryDefinition from './module';
|
|
2
|
+
const hardwareWalletLedger = () => {
|
|
3
|
+
return {
|
|
4
|
+
id: 'hardwareWalletLedger',
|
|
5
|
+
definitions: [
|
|
6
|
+
{
|
|
7
|
+
definition: ledgerDiscoveryDefinition,
|
|
8
|
+
},
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export default hardwareWalletLedger;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type Transport from '@ledgerhq/hw-transport';
|
|
2
|
+
import type { HardwareWalletDevice } from '@exodus/hw-common';
|
|
3
|
+
declare function createHandler(transport: Transport): Promise<HardwareWalletDevice>;
|
|
4
|
+
declare const metadata: {
|
|
5
|
+
applicationName: string;
|
|
6
|
+
supportedVersions: string;
|
|
7
|
+
handler: typeof createHandler;
|
|
8
|
+
};
|
|
9
|
+
export default metadata;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import bs58 from 'bs58';
|
|
2
|
+
import { AppClient, DefaultWalletPolicy, PsbtV2 } from 'ledger-bitcoin';
|
|
3
|
+
import { splitBip44DerivationPath } from './utils';
|
|
4
|
+
import assert from 'minimalistic-assert';
|
|
5
|
+
async function createHandler(transport) {
|
|
6
|
+
const app = new AppClient(transport);
|
|
7
|
+
const getWalletPolicy = async (purpose, coin, account) => {
|
|
8
|
+
const fpr = await app.getMasterFingerprint();
|
|
9
|
+
const xpub = await app.getExtendedPubkey(`m/${purpose}/${coin}/${account}`);
|
|
10
|
+
const desc = { "44'": 'pkh', "84'": 'wpkh', "86'": 'tr' }[purpose];
|
|
11
|
+
return new DefaultWalletPolicy(`${desc}(@0/**)`, `[${fpr}/${purpose}/${coin}/${account}]${xpub}`);
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
getAddress: async (params) => {
|
|
15
|
+
const [purpose, coin, account, change, address] = splitBip44DerivationPath(params.derivationPath);
|
|
16
|
+
const walletPolicy = await getWalletPolicy(purpose, coin, account);
|
|
17
|
+
return app.getWalletAddress(walletPolicy, null, Number(change), Number(address), false);
|
|
18
|
+
},
|
|
19
|
+
getXPub: async (params) => {
|
|
20
|
+
return app.getExtendedPubkey(params.derivationPath);
|
|
21
|
+
},
|
|
22
|
+
getPublicKey: async (params) => {
|
|
23
|
+
return bs58.decode(await app.getExtendedPubkey(params.derivationPath)).slice(45, 45 + 33);
|
|
24
|
+
},
|
|
25
|
+
signTransaction: async (params) => {
|
|
26
|
+
assert(Buffer.isBuffer(params.unsignedTx.txData), 'unsignedTx.txData must be PSBT buffer');
|
|
27
|
+
const psbtBuffer = params.unsignedTx.txData;
|
|
28
|
+
const psbt = new PsbtV2();
|
|
29
|
+
psbt.deserialize(psbtBuffer);
|
|
30
|
+
const [purpose, coin, account] = splitBip44DerivationPath(params.derivationPaths[0]);
|
|
31
|
+
const walletPolicy = await getWalletPolicy(purpose, coin, account);
|
|
32
|
+
const signatures = await app.signPsbt(psbtBuffer, walletPolicy, null);
|
|
33
|
+
for (const [inputIndex, partialSignature] of signatures) {
|
|
34
|
+
psbt.setInputPartialSig(inputIndex, partialSignature.pubkey, partialSignature.signature);
|
|
35
|
+
}
|
|
36
|
+
return psbt.serialize();
|
|
37
|
+
},
|
|
38
|
+
signMessage: async (params) => {
|
|
39
|
+
assert(Buffer.isBuffer(params.message.rawMessage), 'rawMessage must be buffer');
|
|
40
|
+
const signedMessageHex = await app.signMessage(params.message.rawMessage, params.derivationPath);
|
|
41
|
+
return Buffer.from(signedMessageHex, 'base64');
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const metadata = {
|
|
46
|
+
applicationName: 'Bitcoin',
|
|
47
|
+
supportedVersions: '^2.1.0',
|
|
48
|
+
handler: createHandler,
|
|
49
|
+
};
|
|
50
|
+
export default metadata;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type Transport from '@ledgerhq/hw-transport';
|
|
2
|
+
import type { HardwareWalletDevice } from '@exodus/hw-common';
|
|
3
|
+
declare function createHandler(transport: Transport): Promise<HardwareWalletDevice>;
|
|
4
|
+
declare const metadata: {
|
|
5
|
+
applicationName: string;
|
|
6
|
+
supportedVersions: string;
|
|
7
|
+
handler: typeof createHandler;
|
|
8
|
+
};
|
|
9
|
+
export default metadata;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import secp256k1 from '@exodus/secp256k1';
|
|
2
|
+
import * as ethers from '@exodus/ethersproject-transactions';
|
|
3
|
+
import AppEth, { ledgerService, isEIP712Message } from '@ledgerhq/hw-app-eth';
|
|
4
|
+
import { TypedDataUtils, SignTypedDataVersion, } from '@metamask/eth-sig-util';
|
|
5
|
+
import assert from 'minimalistic-assert';
|
|
6
|
+
import { buildFauxXPUB } from './utils';
|
|
7
|
+
function hashEIP712Message(typedData) {
|
|
8
|
+
const version = SignTypedDataVersion.V4;
|
|
9
|
+
const { sanitizeData, hashStruct } = TypedDataUtils;
|
|
10
|
+
const sanitizedData = sanitizeData(typedData);
|
|
11
|
+
const domainSeparator = hashStruct('EIP712Domain', sanitizedData.domain, sanitizedData.types, version);
|
|
12
|
+
let hashStructMessage = Buffer.alloc(0);
|
|
13
|
+
if (sanitizedData.primaryType !== 'EIP712Domain') {
|
|
14
|
+
hashStructMessage = hashStruct('' + sanitizedData.primaryType, sanitizedData.message, sanitizedData.types, version);
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
domainSeparator,
|
|
18
|
+
hashStructMessage,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function createHandler(transport) {
|
|
22
|
+
const app = new AppEth(transport);
|
|
23
|
+
return {
|
|
24
|
+
getAddress: async (params) => {
|
|
25
|
+
const { address } = await app.getAddress(params.derivationPath.replace('m/', ''));
|
|
26
|
+
return address;
|
|
27
|
+
},
|
|
28
|
+
getXPub: async (params) => {
|
|
29
|
+
const { publicKey, chainCode } = await app.getAddress(params.derivationPath.replace('m/', ''), false, true);
|
|
30
|
+
const compressedPublicKey = Buffer.from(secp256k1.publicKeyConvert(Buffer.from(publicKey, 'hex'), true));
|
|
31
|
+
return buildFauxXPUB(compressedPublicKey, Buffer.from(chainCode, 'hex'));
|
|
32
|
+
},
|
|
33
|
+
getPublicKey: async (params) => {
|
|
34
|
+
const { publicKey } = await app.getAddress(params.derivationPath.replace('m/', ''));
|
|
35
|
+
const compressedPublicKey = secp256k1.publicKeyConvert(Buffer.from(publicKey, 'hex'), true);
|
|
36
|
+
return Buffer.from(compressedPublicKey);
|
|
37
|
+
},
|
|
38
|
+
signTransaction: async (params) => {
|
|
39
|
+
assert(Array.isArray(params.derivationPaths) && params.derivationPaths.length === 1, 'derivationsPath array must be one element long');
|
|
40
|
+
assert(params.unsignedTx.txData, 'txData must be defined');
|
|
41
|
+
const deserializedTransaction = Buffer.isBuffer(params.unsignedTx.txData)
|
|
42
|
+
? ethers.parse(params.unsignedTx.txData)
|
|
43
|
+
: params.unsignedTx.txData;
|
|
44
|
+
delete deserializedTransaction.r;
|
|
45
|
+
delete deserializedTransaction.s;
|
|
46
|
+
delete deserializedTransaction.v;
|
|
47
|
+
const serializedTransactionHex = ethers.serialize(deserializedTransaction).slice(2);
|
|
48
|
+
const signTransaction = async ({ derivationPath, serializedTransactionHex, withNFT = true, }) => {
|
|
49
|
+
const resolution = await ledgerService.resolveTransaction(serializedTransactionHex, app.loadConfig, {
|
|
50
|
+
externalPlugins: true,
|
|
51
|
+
erc20: true,
|
|
52
|
+
nft: withNFT,
|
|
53
|
+
});
|
|
54
|
+
return app.signTransaction(derivationPath, serializedTransactionHex, resolution);
|
|
55
|
+
};
|
|
56
|
+
let response = null;
|
|
57
|
+
try {
|
|
58
|
+
response = await signTransaction({
|
|
59
|
+
derivationPath: params.derivationPaths[0],
|
|
60
|
+
serializedTransactionHex,
|
|
61
|
+
withNFT: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof Error && err.message.includes('EthAppNftNotSupported')) {
|
|
66
|
+
response = await signTransaction({
|
|
67
|
+
derivationPath: params.derivationPaths[0],
|
|
68
|
+
serializedTransactionHex,
|
|
69
|
+
withNFT: false,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const { r, s, v } = response;
|
|
77
|
+
const serializedSignedTransaction = ethers.serialize(deserializedTransaction, {
|
|
78
|
+
r: '0x' + r,
|
|
79
|
+
s: '0x' + s,
|
|
80
|
+
v: Number.parseInt(v, 16),
|
|
81
|
+
});
|
|
82
|
+
return Buffer.from(serializedSignedTransaction.slice(2), 'hex');
|
|
83
|
+
},
|
|
84
|
+
signMessage: async (params) => {
|
|
85
|
+
const message = params.message;
|
|
86
|
+
let signingPromise;
|
|
87
|
+
if (message.rawMessage) {
|
|
88
|
+
assert(Buffer.isBuffer(params.message.rawMessage), 'rawMessage must be a buffer');
|
|
89
|
+
signingPromise = app.signPersonalMessage(params.derivationPath, Buffer.from(message.rawMessage).toString('hex'));
|
|
90
|
+
}
|
|
91
|
+
else if (message.EIP712Message) {
|
|
92
|
+
assert(isEIP712Message(message.EIP712Message), 'EIP712Message must be EIP712 conformant message');
|
|
93
|
+
signingPromise = app
|
|
94
|
+
.signEIP712Message(params.derivationPath, message.EIP712Message)
|
|
95
|
+
.catch((err) => {
|
|
96
|
+
if (err.message.includes('INS_NOT_SUPPORTED')) {
|
|
97
|
+
const { domainSeparator, hashStructMessage } = hashEIP712Message(message.EIP712Message);
|
|
98
|
+
return app.signEIP712HashedMessage(params.derivationPath, domainSeparator.toString('hex'), hashStructMessage.toString('hex'));
|
|
99
|
+
}
|
|
100
|
+
throw err;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
assert(false, 'rawMessage or EIP712Message must be specified');
|
|
105
|
+
}
|
|
106
|
+
const { r, s, v } = await signingPromise;
|
|
107
|
+
const vNorm = (v - 27).toString(16);
|
|
108
|
+
const vHex = vNorm.length < 2 ? '0' + vNorm : vNorm;
|
|
109
|
+
return Buffer.from(`${r}${s}${vHex}`, 'hex');
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const metadata = {
|
|
114
|
+
applicationName: 'Ethereum',
|
|
115
|
+
supportedVersions: '^1.10.0',
|
|
116
|
+
handler: createHandler,
|
|
117
|
+
};
|
|
118
|
+
export default metadata;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type Transport from '@ledgerhq/hw-transport';
|
|
2
2
|
import type { HardwareWalletDevice } from '@exodus/hw-common';
|
|
3
3
|
type HandlerFactory = (transport: Transport) => Promise<HardwareWalletDevice>;
|
|
4
|
-
|
|
4
|
+
type AssetMetadata = {
|
|
5
|
+
applicationName: string;
|
|
6
|
+
supportedVersions: string;
|
|
7
|
+
handler: HandlerFactory;
|
|
8
|
+
};
|
|
9
|
+
export declare const assetApplications: Record<string, AssetMetadata>;
|
|
5
10
|
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type Transport from '@ledgerhq/hw-transport';
|
|
2
|
+
import type { HardwareWalletDevice } from '@exodus/hw-common';
|
|
3
|
+
declare function createHandler(transport: Transport): Promise<HardwareWalletDevice>;
|
|
4
|
+
declare const metadata: {
|
|
5
|
+
applicationName: string;
|
|
6
|
+
supportedVersions: string;
|
|
7
|
+
handler: typeof createHandler;
|
|
8
|
+
};
|
|
9
|
+
export default metadata;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import AppSolana from '@ledgerhq/hw-app-solana';
|
|
2
|
+
import assert from 'minimalistic-assert';
|
|
3
|
+
import bs58 from 'bs58';
|
|
4
|
+
import { XPubUnsupportedError } from '@exodus/hw-common';
|
|
5
|
+
async function createHandler(transport) {
|
|
6
|
+
const app = new AppSolana(transport);
|
|
7
|
+
return {
|
|
8
|
+
getAddress: async (params) => {
|
|
9
|
+
const { address } = await app.getAddress(params.derivationPath.replace('m/', ''));
|
|
10
|
+
return bs58.encode(address);
|
|
11
|
+
},
|
|
12
|
+
getXPub: async (params) => {
|
|
13
|
+
throw new XPubUnsupportedError();
|
|
14
|
+
},
|
|
15
|
+
getPublicKey: async (params) => {
|
|
16
|
+
const { address } = await app.getAddress(params.derivationPath.replace('m/', ''));
|
|
17
|
+
return address;
|
|
18
|
+
},
|
|
19
|
+
signTransaction: async (params) => {
|
|
20
|
+
assert(Array.isArray(params.derivationPaths) && params.derivationPaths.length === 1, 'derivationsPath array must be one element long');
|
|
21
|
+
const { signature } = await app.signTransaction(params.derivationPaths[0].replace('m/', ''), params.unsignedTx.txData);
|
|
22
|
+
return signature;
|
|
23
|
+
},
|
|
24
|
+
signMessage: async (params) => {
|
|
25
|
+
assert(Buffer.isBuffer(params.message.rawMessage), 'rawMessage must be a buffer');
|
|
26
|
+
const { signature } = await app.signOffchainMessage(params.derivationPath.replace('m/', ''), params.message.rawMessage);
|
|
27
|
+
return signature;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const metadata = {
|
|
32
|
+
applicationName: 'Solana',
|
|
33
|
+
supportedVersions: '^1.4.0',
|
|
34
|
+
handler: createHandler,
|
|
35
|
+
};
|
|
36
|
+
export default metadata;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export declare function splitDerivationPath(derivationPath: string): string[];
|
|
3
|
+
type Bip44PathArray = [string, string, string, string, string];
|
|
4
|
+
export declare function splitBip44DerivationPath(derivationPath: string): Bip44PathArray;
|
|
5
|
+
export declare function buildFauxXPUB(publicKey: Buffer, chainCode: Buffer): string;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert';
|
|
2
|
+
import bs58check from 'bs58check';
|
|
3
|
+
export function splitDerivationPath(derivationPath) {
|
|
4
|
+
assert(typeof derivationPath === 'string', 'derivationPath must be string');
|
|
5
|
+
const parts = derivationPath.split('/');
|
|
6
|
+
assert(parts.length > 1, 'invalid derivationPath: must have length > 1');
|
|
7
|
+
assert(parts[0] === 'm', 'invalid derivationPath: must start with m');
|
|
8
|
+
return parts.slice(1);
|
|
9
|
+
}
|
|
10
|
+
function assertBip44Path(parts) {
|
|
11
|
+
assert(parts.length === 5, 'invalid bip44 derivation path: length must be 5');
|
|
12
|
+
assert(parts.every((part) => typeof part === 'string'));
|
|
13
|
+
}
|
|
14
|
+
export function splitBip44DerivationPath(derivationPath) {
|
|
15
|
+
const parts = splitDerivationPath(derivationPath);
|
|
16
|
+
assertBip44Path(parts);
|
|
17
|
+
const [purpose, coin, account, change, address] = parts;
|
|
18
|
+
assert(purpose.endsWith("'"), 'invalid derivationPath: purpose must be hardened');
|
|
19
|
+
assert(coin.endsWith("'"), 'invalid derivationPath: coin must be hardened');
|
|
20
|
+
assert(account.endsWith("'"), 'invalid derivationPath: account must be hardened');
|
|
21
|
+
return [purpose, coin, account, change, address];
|
|
22
|
+
}
|
|
23
|
+
export function buildFauxXPUB(publicKey, chainCode) {
|
|
24
|
+
const LEN = 78;
|
|
25
|
+
const XPUB_PREFIX = 76067358;
|
|
26
|
+
const buffer = Buffer.allocUnsafe(LEN);
|
|
27
|
+
const fingerprint = 0;
|
|
28
|
+
const depth = 0;
|
|
29
|
+
const index = 0;
|
|
30
|
+
buffer.writeUInt32BE(XPUB_PREFIX, 0);
|
|
31
|
+
buffer.writeUInt8(depth, 4);
|
|
32
|
+
buffer.writeUInt32BE(fingerprint, 5);
|
|
33
|
+
buffer.writeUInt32BE(index, 9);
|
|
34
|
+
chainCode.copy(buffer, 13);
|
|
35
|
+
publicKey.copy(buffer, 45);
|
|
36
|
+
return bs58check.encode(buffer);
|
|
37
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { GetAddressParams, GetPublicKeyParams, GetXPubParams, HardwareWalletDevice, SignMessageParams, SignTransactionParams } from '@exodus/hw-common';
|
|
2
|
-
import type
|
|
2
|
+
import type { TransportFactory } from './types';
|
|
3
|
+
export type Descriptor = unknown;
|
|
4
|
+
export type Dependencies = {
|
|
5
|
+
transportFactory: TransportFactory;
|
|
6
|
+
descriptor: Descriptor;
|
|
7
|
+
};
|
|
3
8
|
export declare class LedgerDevice implements HardwareWalletDevice {
|
|
4
9
|
#private;
|
|
5
|
-
constructor({
|
|
6
|
-
transport: Transport;
|
|
7
|
-
});
|
|
10
|
+
constructor({ transportFactory, descriptor }: Dependencies);
|
|
8
11
|
getAddress: (params: GetAddressParams) => Promise<string>;
|
|
9
12
|
getXPub: (params: GetXPubParams) => Promise<string>;
|
|
10
13
|
getPublicKey: (params: GetPublicKeyParams) => Promise<Readonly<Uint8Array>>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import { AssetUnsupportedError, DeviceLockedError, DeviceConnectionError, UserRefusedError, } from '@exodus/hw-common';
|
|
3
|
+
import { assetApplications } from './assets';
|
|
4
|
+
import applicationFactory from './management/application';
|
|
5
|
+
import { UnsupportedVersionError } from './errors';
|
|
6
|
+
export class LedgerDevice {
|
|
7
|
+
#transportFactory;
|
|
8
|
+
#descriptor;
|
|
9
|
+
constructor({ transportFactory, descriptor }) {
|
|
10
|
+
this.#transportFactory = transportFactory;
|
|
11
|
+
this.#descriptor = descriptor;
|
|
12
|
+
}
|
|
13
|
+
#runInSession = async (operation) => {
|
|
14
|
+
const transport = await this.#transportFactory.open(this.#descriptor);
|
|
15
|
+
try {
|
|
16
|
+
return await operation(transport);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
throw transformToCommonError(err);
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
await transport.close();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
#getAssetApplication = (assetName) => {
|
|
26
|
+
const handlerFactory = assetApplications[assetName];
|
|
27
|
+
if (!handlerFactory)
|
|
28
|
+
throw new AssetUnsupportedError(assetName);
|
|
29
|
+
return handlerFactory;
|
|
30
|
+
};
|
|
31
|
+
#listApplications = async () => {
|
|
32
|
+
return this.#runInSession(async (transport) => {
|
|
33
|
+
const handler = await applicationFactory(transport);
|
|
34
|
+
return handler.listApplications();
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
#getInformation = async () => {
|
|
38
|
+
return this.#runInSession(async (transport) => {
|
|
39
|
+
const handler = await applicationFactory(transport);
|
|
40
|
+
return handler.getInformation();
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
#openApplication = async (applicationName) => {
|
|
44
|
+
return this.#runInSession(async (transport) => {
|
|
45
|
+
const handler = await applicationFactory(transport);
|
|
46
|
+
return handler.openApplication(applicationName);
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
#quitApplication = async () => {
|
|
50
|
+
return this.#runInSession(async (transport) => {
|
|
51
|
+
const handler = await applicationFactory(transport);
|
|
52
|
+
return handler.quitApplication();
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
#ensureApplicationIsOpened = async (assetName) => {
|
|
56
|
+
const { applicationName, supportedVersions } = this.#getAssetApplication(assetName);
|
|
57
|
+
const currentApplicationInfo = await this.#getInformation();
|
|
58
|
+
let desiredApplicationInfo = null;
|
|
59
|
+
if (applicationName === currentApplicationInfo.name) {
|
|
60
|
+
desiredApplicationInfo = currentApplicationInfo;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
await this.#quitApplication();
|
|
64
|
+
await this.#openApplication(applicationName);
|
|
65
|
+
desiredApplicationInfo = await this.#getInformation();
|
|
66
|
+
}
|
|
67
|
+
const isSupportedVersion = semver.satisfies(desiredApplicationInfo.version, supportedVersions);
|
|
68
|
+
if (!isSupportedVersion) {
|
|
69
|
+
throw new UnsupportedVersionError(desiredApplicationInfo.version, supportedVersions);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
getAddress = async (params) => {
|
|
73
|
+
await this.#ensureApplicationIsOpened(params.assetName);
|
|
74
|
+
return this.#runInSession(async (transport) => {
|
|
75
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
76
|
+
return handler.getAddress(params);
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
getXPub = async (params) => {
|
|
80
|
+
await this.#ensureApplicationIsOpened(params.assetName);
|
|
81
|
+
return this.#runInSession(async (transport) => {
|
|
82
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
83
|
+
return handler.getXPub(params);
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
getPublicKey = async (params) => {
|
|
87
|
+
await this.#ensureApplicationIsOpened(params.assetName);
|
|
88
|
+
return this.#runInSession(async (transport) => {
|
|
89
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
90
|
+
return handler.getPublicKey(params);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
signTransaction = async (params) => {
|
|
94
|
+
await this.#ensureApplicationIsOpened(params.assetName);
|
|
95
|
+
return this.#runInSession(async (transport) => {
|
|
96
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
97
|
+
return handler.signTransaction(params);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
signMessage = async (params) => {
|
|
101
|
+
await this.#ensureApplicationIsOpened(params.assetName);
|
|
102
|
+
return this.#runInSession(async (transport) => {
|
|
103
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
104
|
+
return handler.signMessage(params);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function transformToCommonError(_err) {
|
|
109
|
+
const err = _err;
|
|
110
|
+
if (err.name === 'LockedDeviceError') {
|
|
111
|
+
return new DeviceLockedError();
|
|
112
|
+
}
|
|
113
|
+
if (err.name === 'UserRefusedOnDevice') {
|
|
114
|
+
return new UserRefusedError();
|
|
115
|
+
}
|
|
116
|
+
if ([
|
|
117
|
+
'HwTransportError',
|
|
118
|
+
'TransportError',
|
|
119
|
+
'TransportRaceCondition',
|
|
120
|
+
'UnresponsiveDeviceError',
|
|
121
|
+
'DisconnectedDevice',
|
|
122
|
+
'DisconnectedDeviceDuringOperation',
|
|
123
|
+
].includes(err.name)) {
|
|
124
|
+
return new DeviceConnectionError(err);
|
|
125
|
+
}
|
|
126
|
+
return err;
|
|
127
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import ExodusModule from '@exodus/module';
|
|
2
|
-
import
|
|
3
|
-
import { Logger } from '~/shared/types';
|
|
2
|
+
import { Logger } from '../shared/types';
|
|
4
3
|
import type { HardwareWalletDescriptor, HardwareWalletDiscovery } from '@exodus/hw-common';
|
|
5
|
-
type TransportFactory
|
|
4
|
+
import type { TransportFactory } from './types';
|
|
6
5
|
export type Dependencies = {
|
|
7
6
|
logger: Logger;
|
|
8
7
|
transportsFactories: {
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import ExodusModule from '@exodus/module';
|
|
2
|
-
import assert from 'minimalistic-assert';
|
|
3
2
|
import { APDU_PORT } from './constants';
|
|
4
3
|
import { LedgerDevice } from './device';
|
|
5
|
-
|
|
4
|
+
import { UnknownTransportError } from './errors';
|
|
5
|
+
const MODULE_ID = 'ledgerDiscovery';
|
|
6
6
|
export class LedgerDiscovery extends ExodusModule {
|
|
7
7
|
#transportsFactories;
|
|
8
8
|
constructor({ logger, transportsFactories }) {
|
|
9
9
|
super({ name: MODULE_ID, logger });
|
|
10
|
-
|
|
10
|
+
if (!(typeof transportsFactories === 'object' &&
|
|
11
11
|
Object.keys(transportsFactories).length > 0 &&
|
|
12
12
|
Object.entries(transportsFactories).every(([transportName, transportFactory]) => ['usb', 'bluetooth', 'tcp'].includes(transportName) &&
|
|
13
|
-
|
|
13
|
+
transportFactory !== undefined &&
|
|
14
|
+
typeof transportFactory.list === 'function' &&
|
|
15
|
+
typeof transportFactory.open === 'function')))
|
|
16
|
+
throw new UnknownTransportError();
|
|
14
17
|
this.#transportsFactories = transportsFactories;
|
|
15
18
|
}
|
|
16
19
|
list = async () => {
|
|
@@ -22,22 +25,25 @@ export class LedgerDiscovery extends ExodusModule {
|
|
|
22
25
|
const usbDeviceList = deviceList.map((hidDevice) => ({
|
|
23
26
|
manufacturer: 'ledger',
|
|
24
27
|
transport: 'usb',
|
|
25
|
-
get: async () => new LedgerDevice({
|
|
28
|
+
get: async () => new LedgerDevice({ transportFactory, descriptor: hidDevice }),
|
|
26
29
|
}));
|
|
27
30
|
descriptors.push(...usbDeviceList);
|
|
28
31
|
break;
|
|
29
32
|
case 'bluetooth':
|
|
30
|
-
this._logger.error(
|
|
33
|
+
this._logger.error('bluetooth is not supported yet!');
|
|
31
34
|
break;
|
|
32
35
|
case 'tcp':
|
|
33
36
|
descriptors.push({
|
|
34
37
|
manufacturer: 'ledger',
|
|
35
38
|
transport: 'tcp',
|
|
36
|
-
get: async () => new LedgerDevice({
|
|
39
|
+
get: async () => new LedgerDevice({
|
|
40
|
+
transportFactory,
|
|
41
|
+
descriptor: { apduPort: APDU_PORT },
|
|
42
|
+
}),
|
|
37
43
|
});
|
|
38
44
|
break;
|
|
39
45
|
default:
|
|
40
|
-
throw new
|
|
46
|
+
throw new UnknownTransportError(transportName);
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
49
|
return descriptors;
|
|
@@ -48,5 +54,5 @@ export default {
|
|
|
48
54
|
id: MODULE_ID,
|
|
49
55
|
type: 'module',
|
|
50
56
|
factory: createLedgerDiscovery,
|
|
51
|
-
dependencies: ['logger'],
|
|
57
|
+
dependencies: ['logger', 'transportsFactories'],
|
|
52
58
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HardwareWalletError } from '@exodus/hw-common';
|
|
2
|
+
export declare class UnknownTransportError extends HardwareWalletError {
|
|
3
|
+
name: string;
|
|
4
|
+
constructor(transportName?: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class UnsupportedVersionError extends HardwareWalletError {
|
|
7
|
+
name: string;
|
|
8
|
+
constructor(actualVersion: string, supportedVersions: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class RequiresBlindSigningError extends HardwareWalletError {
|
|
11
|
+
name: string;
|
|
12
|
+
constructor();
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HardwareWalletError } from '@exodus/hw-common';
|
|
2
|
+
export class UnknownTransportError extends HardwareWalletError {
|
|
3
|
+
name = 'UnknownTransportError';
|
|
4
|
+
constructor(transportName) {
|
|
5
|
+
super(`unknown transport type ${transportName || ''}`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class UnsupportedVersionError extends HardwareWalletError {
|
|
9
|
+
name = 'UnsupportedVersionError';
|
|
10
|
+
constructor(actualVersion, supportedVersions) {
|
|
11
|
+
super(`version does not meet version requirements: ${actualVersion} does not match ${supportedVersions}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class RequiresBlindSigningError extends HardwareWalletError {
|
|
15
|
+
name = 'RequiresBlindSigningError';
|
|
16
|
+
constructor() {
|
|
17
|
+
super('signing operation requires that blind signing is enabled on the device');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type Transport from '@ledgerhq/hw-transport';
|
|
3
|
+
export type GetInformationResponse = {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
flags: Buffer;
|
|
7
|
+
};
|
|
8
|
+
export type ListAppResponse = {
|
|
9
|
+
name: string;
|
|
10
|
+
hash: string;
|
|
11
|
+
hash_code_data: string;
|
|
12
|
+
blocks?: number;
|
|
13
|
+
flags?: number;
|
|
14
|
+
}[];
|
|
15
|
+
export default function createApplicationManager(transport: Transport): Promise<{
|
|
16
|
+
getInformation: () => Promise<GetInformationResponse>;
|
|
17
|
+
listApplications: () => Promise<ListAppResponse>;
|
|
18
|
+
openApplication: (name: string) => Promise<void>;
|
|
19
|
+
quitApplication: () => Promise<void>;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export default async function createApplicationManager(transport) {
|
|
2
|
+
return {
|
|
3
|
+
getInformation: async () => {
|
|
4
|
+
const r = await transport.send(0xb0, 0x01, 0x00, 0x00);
|
|
5
|
+
let i = 0;
|
|
6
|
+
const format = r[i++];
|
|
7
|
+
if (format !== 1) {
|
|
8
|
+
throw new Error('getAppAndVersion: format not supported');
|
|
9
|
+
}
|
|
10
|
+
const nameLength = Number(r[i++]);
|
|
11
|
+
const name = r.slice(i, (i += nameLength)).toString('ascii');
|
|
12
|
+
const versionLength = Number(r[i++]);
|
|
13
|
+
const version = r.slice(i, (i += versionLength)).toString('ascii');
|
|
14
|
+
const flagLength = Number(r[i++]);
|
|
15
|
+
const flags = r.slice(i, (i += flagLength));
|
|
16
|
+
return {
|
|
17
|
+
name,
|
|
18
|
+
version,
|
|
19
|
+
flags,
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
listApplications: async () => {
|
|
23
|
+
const payload = await transport.send(0xe0, 0xde, 0, 0);
|
|
24
|
+
const apps = [];
|
|
25
|
+
let data = payload;
|
|
26
|
+
while (data.length > 2) {
|
|
27
|
+
if (payload[0] !== 0x01) {
|
|
28
|
+
throw new Error('unknown listApps format');
|
|
29
|
+
}
|
|
30
|
+
let i = 1;
|
|
31
|
+
while (i < data.length - 2) {
|
|
32
|
+
const length = data[i];
|
|
33
|
+
i++;
|
|
34
|
+
const blocks = data.readUInt16BE(i);
|
|
35
|
+
i += 2;
|
|
36
|
+
const flags = data.readUInt16BE(i);
|
|
37
|
+
i += 2;
|
|
38
|
+
const hashCodeData = data.slice(i, i + 32).toString('hex');
|
|
39
|
+
i += 32;
|
|
40
|
+
const hash = data.slice(i, i + 32).toString('hex');
|
|
41
|
+
i += 32;
|
|
42
|
+
const nameLength = Number(data[i]);
|
|
43
|
+
i++;
|
|
44
|
+
if (length !== nameLength + 70) {
|
|
45
|
+
throw new Error('invalid listApps length data');
|
|
46
|
+
}
|
|
47
|
+
const name = data.slice(i, i + nameLength).toString('ascii');
|
|
48
|
+
i += nameLength;
|
|
49
|
+
apps.push({
|
|
50
|
+
name,
|
|
51
|
+
hash,
|
|
52
|
+
hash_code_data: hashCodeData,
|
|
53
|
+
blocks,
|
|
54
|
+
flags,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
data = await transport.send(0xe0, 0xdf, 0, 0);
|
|
58
|
+
}
|
|
59
|
+
return apps;
|
|
60
|
+
},
|
|
61
|
+
openApplication: async (name) => {
|
|
62
|
+
await transport.send(0xe0, 0xd8, 0x00, 0x00, Buffer.from(name, 'ascii'));
|
|
63
|
+
},
|
|
64
|
+
quitApplication: async () => {
|
|
65
|
+
await transport.send(0xb0, 0xa7, 0x00, 0x00);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,46 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/hw-ledger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "The ledger module",
|
|
5
|
-
"author": "Exodus Movement Inc",
|
|
5
|
+
"author": "Exodus Movement Inc.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/ExodusMovement/exodus-hydra.git"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://github.com/ExodusMovement/exodus-hydra/tree/master/
|
|
10
|
+
"homepage": "https://github.com/ExodusMovement/exodus-hydra/tree/master/features/hw-ledger",
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Ahw-ledger"
|
|
14
14
|
},
|
|
15
|
+
"main": "lib/index.js",
|
|
15
16
|
"files": [
|
|
16
17
|
"lib",
|
|
17
|
-
"README.md",
|
|
18
|
-
"CHANGELOG.md"
|
|
18
|
+
"./README.md",
|
|
19
|
+
"./CHANGELOG.md"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
|
21
22
|
"build": "tsc --build tsconfig.build.json",
|
|
22
23
|
"build:watch": "tsc --build tsconfig.build.json && (concurrently 'tsc --build -w tsconfig.build.json')",
|
|
23
24
|
"clean": "tsc --build --clean",
|
|
24
|
-
"test": "jest --
|
|
25
|
+
"test": "jest --testMatch='**/*.unit.test.js' --coverageDirectory coverage/unit",
|
|
26
|
+
"zemu:install": "docker pull zondax/builder-zemu:speculos-745fe46f81cfc18114b94a37beaefb58a09c0fcd",
|
|
27
|
+
"test:integration": "yarn zemu:install && jest --runInBand",
|
|
25
28
|
"lint": "eslint . --ignore-path ../../.gitignore",
|
|
26
29
|
"lint:fix": "yarn lint --fix"
|
|
27
30
|
},
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"@
|
|
32
|
+
"@exodus/ethersproject-transactions": "^5.4.2-exodus.0",
|
|
33
|
+
"@exodus/hw-common": "^1.0.0",
|
|
34
|
+
"@exodus/secp256k1": "^4.0.2-exodus.0",
|
|
35
|
+
"@ledgerhq/hw-app-eth": "^6.33.6",
|
|
36
|
+
"@ledgerhq/hw-app-solana": "^7.0.10",
|
|
30
37
|
"@ledgerhq/hw-transport": "^6.28.5",
|
|
31
|
-
"
|
|
38
|
+
"@metamask/eth-sig-util": "^4.0.1",
|
|
39
|
+
"bs58": "^4.0.0",
|
|
40
|
+
"bs58check": "^2.1.2",
|
|
41
|
+
"ledger-bitcoin": "^0.2.2",
|
|
42
|
+
"minimalistic-assert": "^1.0.1",
|
|
43
|
+
"semver": "^7.3.5"
|
|
32
44
|
},
|
|
33
45
|
"devDependencies": {
|
|
34
|
-
"@exodus/
|
|
35
|
-
"@exodus/module": "^1.2.
|
|
36
|
-
"@ledgerhq/hw-transport-node-hid": "^6.27.
|
|
46
|
+
"@exodus/bip32": "^2.0.0",
|
|
47
|
+
"@exodus/module": "^1.2.2",
|
|
48
|
+
"@ledgerhq/hw-transport-node-hid": "^6.27.17",
|
|
37
49
|
"@ledgerhq/hw-transport-node-speculos": "^6.27.16",
|
|
50
|
+
"@types/bs58": "^4.0.1",
|
|
51
|
+
"@types/bs58check": "^2.1.0",
|
|
38
52
|
"@types/jest-when": "^3.5.2",
|
|
39
53
|
"@types/minimalistic-assert": "^1.0.1",
|
|
54
|
+
"@types/secp256k1": "^4.0.3",
|
|
55
|
+
"@zondax/zemu": "^0.43.1",
|
|
40
56
|
"concurrently": "^7.6.0",
|
|
41
|
-
"eslint": "^8.
|
|
57
|
+
"eslint": "^8.44.0",
|
|
58
|
+
"istanbul-combine": "^0.3.0",
|
|
42
59
|
"jest": "^29.1.2",
|
|
43
|
-
"jest-when": "^3.5.2"
|
|
60
|
+
"jest-when": "^3.5.2",
|
|
61
|
+
"ts-jest": "^29.0.3",
|
|
62
|
+
"typescript": "^4.7.3"
|
|
44
63
|
},
|
|
45
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "df9526a0bdc326e1a9918c3f76ba2197bf42a920"
|
|
46
65
|
}
|
package/lib/assets/bitcoin.d.ts
DELETED
package/lib/assets/bitcoin.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import AppBtc from '@ledgerhq/hw-app-btc';
|
|
2
|
-
export default async function (transport) {
|
|
3
|
-
const app = new AppBtc({ transport });
|
|
4
|
-
return {
|
|
5
|
-
getAddress: async (params) => {
|
|
6
|
-
throw new Error('implement me');
|
|
7
|
-
},
|
|
8
|
-
getXPub: async (params) => {
|
|
9
|
-
throw new Error('implement me');
|
|
10
|
-
},
|
|
11
|
-
getPublicKey: async (params) => {
|
|
12
|
-
throw new Error('implement me');
|
|
13
|
-
},
|
|
14
|
-
signTransaction: async (params) => {
|
|
15
|
-
throw new Error('implement me');
|
|
16
|
-
},
|
|
17
|
-
signMessage: async (params) => {
|
|
18
|
-
throw new Error('implement me');
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
package/lib/assets/index.js
DELETED
package/lib/device.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import assert from 'minimalistic-assert';
|
|
2
|
-
import { assetHandlerFactories } from './assets';
|
|
3
|
-
export class LedgerDevice {
|
|
4
|
-
#transport;
|
|
5
|
-
constructor({ transport }) {
|
|
6
|
-
this.#transport = transport;
|
|
7
|
-
}
|
|
8
|
-
#getAssetHandler = async (assetName) => {
|
|
9
|
-
const handlerFactory = assetHandlerFactories[assetName];
|
|
10
|
-
assert(handlerFactory, `ledger does not support ${assetName}`);
|
|
11
|
-
return handlerFactory(this.#transport);
|
|
12
|
-
};
|
|
13
|
-
getAddress = async (params) => {
|
|
14
|
-
const handler = await this.#getAssetHandler(params.assetName);
|
|
15
|
-
return handler.getAddress(params);
|
|
16
|
-
};
|
|
17
|
-
getXPub = async (params) => {
|
|
18
|
-
const handler = await this.#getAssetHandler(params.assetName);
|
|
19
|
-
return handler.getXPub(params);
|
|
20
|
-
};
|
|
21
|
-
getPublicKey = async (params) => {
|
|
22
|
-
const handler = await this.#getAssetHandler(params.assetName);
|
|
23
|
-
return handler.getPublicKey(params);
|
|
24
|
-
};
|
|
25
|
-
signTransaction = async (params) => {
|
|
26
|
-
const handler = await this.#getAssetHandler(params.assetName);
|
|
27
|
-
return handler.signTransaction(params);
|
|
28
|
-
};
|
|
29
|
-
signMessage = async (params) => {
|
|
30
|
-
const handler = await this.#getAssetHandler(params.assetName);
|
|
31
|
-
return handler.signMessage(params);
|
|
32
|
-
};
|
|
33
|
-
}
|
|
File without changes
|
|
File without changes
|