@sidhujag/sysweb3-keyring 1.0.547 → 1.0.549

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/{dist/cjs → cjs}/providers.js +1 -64
  2. package/cjs/providers.js.map +1 -0
  3. package/{dist/cjs → cjs}/transactions/ethereum.js +1 -16
  4. package/cjs/transactions/ethereum.js.map +1 -0
  5. package/package.json +2 -30
  6. package/{dist/types → types}/providers.d.ts +0 -8
  7. package/{dist/types → types}/transactions/ethereum.d.ts +2 -2
  8. package/{dist/types → types}/types.d.ts +2 -2
  9. package/coverage/clover.xml +0 -2875
  10. package/coverage/coverage-final.json +0 -29468
  11. package/coverage/lcov-report/base.css +0 -354
  12. package/coverage/lcov-report/block-navigation.js +0 -85
  13. package/coverage/lcov-report/favicon.png +0 -0
  14. package/coverage/lcov-report/index.html +0 -320
  15. package/coverage/lcov-report/prettify.css +0 -101
  16. package/coverage/lcov-report/prettify.js +0 -1008
  17. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  18. package/coverage/lcov-report/sorter.js +0 -191
  19. package/coverage/lcov-report/src/index.html +0 -276
  20. package/coverage/lcov-report/src/index.ts.html +0 -114
  21. package/coverage/lcov-report/src/initial-state.ts.html +0 -558
  22. package/coverage/lcov-report/src/keyring-manager.ts.html +0 -6279
  23. package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +0 -178
  24. package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +0 -144
  25. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +0 -1560
  26. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +0 -276
  27. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +0 -495
  28. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +0 -1138
  29. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +0 -363
  30. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +0 -289
  31. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +0 -486
  32. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +0 -240
  33. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +0 -342
  34. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +0 -2388
  35. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +0 -453
  36. package/coverage/lcov-report/src/ledger/consts.ts.html +0 -177
  37. package/coverage/lcov-report/src/ledger/index.html +0 -216
  38. package/coverage/lcov-report/src/ledger/index.ts.html +0 -1371
  39. package/coverage/lcov-report/src/ledger/utils.ts.html +0 -102
  40. package/coverage/lcov-report/src/signers.ts.html +0 -591
  41. package/coverage/lcov-report/src/storage.ts.html +0 -198
  42. package/coverage/lcov-report/src/transactions/ethereum.ts.html +0 -5826
  43. package/coverage/lcov-report/src/transactions/index.html +0 -216
  44. package/coverage/lcov-report/src/transactions/index.ts.html +0 -93
  45. package/coverage/lcov-report/src/transactions/syscoin.ts.html +0 -1521
  46. package/coverage/lcov-report/src/trezor/index.html +0 -176
  47. package/coverage/lcov-report/src/trezor/index.ts.html +0 -2655
  48. package/coverage/lcov-report/src/types.ts.html +0 -1443
  49. package/coverage/lcov-report/src/utils/derivation-paths.ts.html +0 -486
  50. package/coverage/lcov-report/src/utils/index.html +0 -196
  51. package/coverage/lcov-report/src/utils/psbt.ts.html +0 -159
  52. package/coverage/lcov-report/test/helpers/constants.ts.html +0 -627
  53. package/coverage/lcov-report/test/helpers/index.html +0 -176
  54. package/coverage/lcov.info +0 -4832
  55. package/dist/cjs/providers.js.map +0 -1
  56. package/dist/cjs/transactions/ethereum.js.map +0 -1
  57. package/dist/package.json +0 -50
  58. package/examples/basic-usage.js +0 -140
  59. package/jest.config.js +0 -32
  60. package/readme.md +0 -201
  61. package/src/declare.d.ts +0 -7
  62. package/src/errorUtils.ts +0 -83
  63. package/src/hardware-wallet-manager.ts +0 -655
  64. package/src/index.ts +0 -12
  65. package/src/initial-state.ts +0 -108
  66. package/src/keyring-manager.ts +0 -2698
  67. package/src/ledger/bitcoin_client/index.ts +0 -19
  68. package/src/ledger/bitcoin_client/lib/appClient.ts +0 -405
  69. package/src/ledger/bitcoin_client/lib/bip32.ts +0 -61
  70. package/src/ledger/bitcoin_client/lib/buffertools.ts +0 -134
  71. package/src/ledger/bitcoin_client/lib/clientCommands.ts +0 -356
  72. package/src/ledger/bitcoin_client/lib/constants.ts +0 -12
  73. package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +0 -65
  74. package/src/ledger/bitcoin_client/lib/merkle.ts +0 -136
  75. package/src/ledger/bitcoin_client/lib/merkleMap.ts +0 -49
  76. package/src/ledger/bitcoin_client/lib/policy.ts +0 -91
  77. package/src/ledger/bitcoin_client/lib/psbtv2.ts +0 -768
  78. package/src/ledger/bitcoin_client/lib/varint.ts +0 -120
  79. package/src/ledger/consts.ts +0 -3
  80. package/src/ledger/index.ts +0 -685
  81. package/src/ledger/types.ts +0 -74
  82. package/src/network-utils.ts +0 -99
  83. package/src/providers.ts +0 -345
  84. package/src/signers.ts +0 -158
  85. package/src/storage.ts +0 -63
  86. package/src/transactions/__tests__/integration.test.ts +0 -303
  87. package/src/transactions/__tests__/syscoin.test.ts +0 -409
  88. package/src/transactions/ethereum.ts +0 -2503
  89. package/src/transactions/index.ts +0 -2
  90. package/src/transactions/syscoin.ts +0 -542
  91. package/src/trezor/index.ts +0 -1050
  92. package/src/types.ts +0 -366
  93. package/src/utils/derivation-paths.ts +0 -133
  94. package/src/utils/psbt.ts +0 -24
  95. package/src/utils.ts +0 -191
  96. package/test/README.md +0 -158
  97. package/test/__mocks__/ledger-mock.js +0 -20
  98. package/test/__mocks__/trezor-mock.js +0 -75
  99. package/test/cleanup-summary.md +0 -167
  100. package/test/helpers/README.md +0 -78
  101. package/test/helpers/constants.ts +0 -79
  102. package/test/helpers/setup.ts +0 -714
  103. package/test/integration/import-validation.spec.ts +0 -588
  104. package/test/unit/hardware/ledger.spec.ts +0 -869
  105. package/test/unit/hardware/trezor.spec.ts +0 -828
  106. package/test/unit/keyring-manager/account-management.spec.ts +0 -970
  107. package/test/unit/keyring-manager/import-watchonly.spec.ts +0 -181
  108. package/test/unit/keyring-manager/import-wif.spec.ts +0 -126
  109. package/test/unit/keyring-manager/initialization.spec.ts +0 -782
  110. package/test/unit/keyring-manager/key-derivation.spec.ts +0 -996
  111. package/test/unit/keyring-manager/security.spec.ts +0 -505
  112. package/test/unit/keyring-manager/state-management.spec.ts +0 -375
  113. package/test/unit/network/network-management.spec.ts +0 -372
  114. package/test/unit/transactions/ethereum-transactions.spec.ts +0 -382
  115. package/test/unit/transactions/syscoin-transactions.spec.ts +0 -615
  116. package/tsconfig.json +0 -14
  117. /package/{dist/README.md → README.md} +0 -0
  118. /package/{dist/cjs → cjs}/errorUtils.js +0 -0
  119. /package/{dist/cjs → cjs}/errorUtils.js.map +0 -0
  120. /package/{dist/cjs → cjs}/hardware-wallet-manager.js +0 -0
  121. /package/{dist/cjs → cjs}/hardware-wallet-manager.js.map +0 -0
  122. /package/{dist/cjs → cjs}/index.js +0 -0
  123. /package/{dist/cjs → cjs}/index.js.map +0 -0
  124. /package/{dist/cjs → cjs}/initial-state.js +0 -0
  125. /package/{dist/cjs → cjs}/initial-state.js.map +0 -0
  126. /package/{dist/cjs → cjs}/keyring-manager.js +0 -0
  127. /package/{dist/cjs → cjs}/keyring-manager.js.map +0 -0
  128. /package/{dist/cjs → cjs}/ledger/bitcoin_client/index.js +0 -0
  129. /package/{dist/cjs → cjs}/ledger/bitcoin_client/index.js.map +0 -0
  130. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/appClient.js +0 -0
  131. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/appClient.js.map +0 -0
  132. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
  133. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
  134. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
  135. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
  136. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
  137. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
  138. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
  139. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
  140. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
  141. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
  142. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
  143. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
  144. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
  145. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
  146. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
  147. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
  148. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
  149. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
  150. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
  151. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
  152. /package/{dist/cjs → cjs}/ledger/consts.js +0 -0
  153. /package/{dist/cjs → cjs}/ledger/consts.js.map +0 -0
  154. /package/{dist/cjs → cjs}/ledger/index.js +0 -0
  155. /package/{dist/cjs → cjs}/ledger/index.js.map +0 -0
  156. /package/{dist/cjs → cjs}/ledger/types.js +0 -0
  157. /package/{dist/cjs → cjs}/ledger/types.js.map +0 -0
  158. /package/{dist/cjs → cjs}/network-utils.js +0 -0
  159. /package/{dist/cjs → cjs}/network-utils.js.map +0 -0
  160. /package/{dist/cjs → cjs}/signers.js +0 -0
  161. /package/{dist/cjs → cjs}/signers.js.map +0 -0
  162. /package/{dist/cjs → cjs}/storage.js +0 -0
  163. /package/{dist/cjs → cjs}/storage.js.map +0 -0
  164. /package/{dist/cjs → cjs}/transactions/__tests__/integration.test.js +0 -0
  165. /package/{dist/cjs → cjs}/transactions/__tests__/integration.test.js.map +0 -0
  166. /package/{dist/cjs → cjs}/transactions/__tests__/syscoin.test.js +0 -0
  167. /package/{dist/cjs → cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
  168. /package/{dist/cjs → cjs}/transactions/index.js +0 -0
  169. /package/{dist/cjs → cjs}/transactions/index.js.map +0 -0
  170. /package/{dist/cjs → cjs}/transactions/syscoin.js +0 -0
  171. /package/{dist/cjs → cjs}/transactions/syscoin.js.map +0 -0
  172. /package/{dist/cjs → cjs}/trezor/index.js +0 -0
  173. /package/{dist/cjs → cjs}/trezor/index.js.map +0 -0
  174. /package/{dist/cjs → cjs}/types.js +0 -0
  175. /package/{dist/cjs → cjs}/types.js.map +0 -0
  176. /package/{dist/cjs → cjs}/utils/derivation-paths.js +0 -0
  177. /package/{dist/cjs → cjs}/utils/derivation-paths.js.map +0 -0
  178. /package/{dist/cjs → cjs}/utils/psbt.js +0 -0
  179. /package/{dist/cjs → cjs}/utils/psbt.js.map +0 -0
  180. /package/{dist/cjs → cjs}/utils.js +0 -0
  181. /package/{dist/cjs → cjs}/utils.js.map +0 -0
  182. /package/{dist/types → types}/errorUtils.d.ts +0 -0
  183. /package/{dist/types → types}/hardware-wallet-manager.d.ts +0 -0
  184. /package/{dist/types → types}/index.d.ts +0 -0
  185. /package/{dist/types → types}/initial-state.d.ts +0 -0
  186. /package/{dist/types → types}/keyring-manager.d.ts +0 -0
  187. /package/{dist/types → types}/ledger/bitcoin_client/index.d.ts +0 -0
  188. /package/{dist/types → types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -0
  189. /package/{dist/types → types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
  190. /package/{dist/types → types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
  191. /package/{dist/types → types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
  192. /package/{dist/types → types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
  193. /package/{dist/types → types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
  194. /package/{dist/types → types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
  195. /package/{dist/types → types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
  196. /package/{dist/types → types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
  197. /package/{dist/types → types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
  198. /package/{dist/types → types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
  199. /package/{dist/types → types}/ledger/consts.d.ts +0 -0
  200. /package/{dist/types → types}/ledger/index.d.ts +0 -0
  201. /package/{dist/types → types}/ledger/types.d.ts +0 -0
  202. /package/{dist/types → types}/network-utils.d.ts +0 -0
  203. /package/{dist/types → types}/signers.d.ts +0 -0
  204. /package/{dist/types → types}/storage.d.ts +0 -0
  205. /package/{dist/types → types}/transactions/__tests__/integration.test.d.ts +0 -0
  206. /package/{dist/types → types}/transactions/__tests__/syscoin.test.d.ts +0 -0
  207. /package/{dist/types → types}/transactions/index.d.ts +0 -0
  208. /package/{dist/types → types}/transactions/syscoin.d.ts +0 -0
  209. /package/{dist/types → types}/trezor/index.d.ts +0 -0
  210. /package/{dist/types → types}/utils/derivation-paths.d.ts +0 -0
  211. /package/{dist/types → types}/utils/psbt.d.ts +0 -0
  212. /package/{dist/types → types}/utils.d.ts +0 -0
@@ -1,685 +0,0 @@
1
- /* eslint-disable camelcase */
2
- /* eslint-disable import/no-named-as-default */
3
- /* eslint-disable import/order */
4
- import Transport from '@ledgerhq/hw-transport';
5
- import SysUtxoClient, { DefaultWalletPolicy } from './bitcoin_client';
6
- import { RECEIVING_ADDRESS_INDEX, WILL_NOT_DISPLAY } from './consts';
7
- import { getNetworkConfig } from '@sidhujag/sysweb3-network';
8
- import BIP32Factory from 'bip32';
9
- import ecc from '@bitcoinerlab/secp256k1';
10
- import { IEvmMethods, IUTXOMethods, MessageTypes } from './types';
11
- import LedgerEthClient, { ledgerService } from '@ledgerhq/hw-app-eth';
12
- import { Transaction } from 'syscoinjs-lib';
13
- import {
14
- TypedDataUtils,
15
- TypedMessage,
16
- SignTypedDataVersion,
17
- TypedDataV1,
18
- } from '@metamask/eth-sig-util';
19
- import {
20
- getAccountDerivationPath,
21
- getAddressDerivationPath,
22
- isEvmCoin,
23
- convertExtendedKeyVersion,
24
- } from '../utils/derivation-paths';
25
-
26
- import {
27
- HardwareWalletManager,
28
- HardwareWalletType,
29
- } from '../hardware-wallet-manager';
30
- //
31
-
32
- export class LedgerKeyring {
33
- public ledgerEVMClient!: LedgerEthClient;
34
- public ledgerUtxoClient!: SysUtxoClient;
35
- private hdPath = "m/44'/57'/0'/0/0";
36
- public evm: IEvmMethods;
37
- public utxo: IUTXOMethods;
38
- public transport: Transport | null = null;
39
- private hardwareWalletManager: HardwareWalletManager;
40
- // In-memory cache of registered wallet policy HMACs
41
- private walletHmacCache: Map<string, Buffer> = new Map();
42
-
43
- constructor() {
44
- this.hardwareWalletManager = new HardwareWalletManager();
45
-
46
- // Set up event listeners
47
- this.hardwareWalletManager.on('connected', ({ type }) => {
48
- if (type === HardwareWalletType.LEDGER) {
49
- console.log('Ledger connected');
50
- }
51
- });
52
-
53
- this.hardwareWalletManager.on('disconnected', ({ type }) => {
54
- if (type === HardwareWalletType.LEDGER) {
55
- console.log('Ledger disconnected');
56
- this.transport = null;
57
- // Clear clients on disconnect
58
- this.ledgerEVMClient = null as any;
59
- this.ledgerUtxoClient = null as any;
60
- this.walletHmacCache.clear();
61
- }
62
- });
63
-
64
- this.hardwareWalletManager.on('connectionFailed', ({ type, error }) => {
65
- if (type === HardwareWalletType.LEDGER) {
66
- console.error('Ledger connection failed:', error);
67
- }
68
- });
69
-
70
- this.evm = {
71
- getEvmAddressAndPubKey: this.getEvmAddressAndPubKey,
72
- signEVMTransaction: this.signEVMTransaction,
73
- signPersonalMessage: this.signPersonalMessage,
74
- signTypedData: this.signTypedData,
75
- };
76
-
77
- this.utxo = {
78
- getUtxoAddress: this.getUtxoAddress,
79
- getXpub: this.getXpub,
80
- verifyUtxoAddress: this.verifyUtxoAddress,
81
- };
82
- }
83
-
84
- /**
85
- * Ensure Ledger is connected with automatic retry
86
- * Note: This is automatically called by all operations through executeWithRetry
87
- * External callers don't need to call this directly
88
- */
89
- public async ensureConnection(): Promise<void> {
90
- await this.hardwareWalletManager.ensureConnection(
91
- HardwareWalletType.LEDGER
92
- );
93
- this.transport = await this.hardwareWalletManager.getLedgerConnection();
94
-
95
- // Create clients if transport is available
96
- if (this.transport && (!this.ledgerEVMClient || !this.ledgerUtxoClient)) {
97
- this.ledgerEVMClient = new LedgerEthClient(this.transport);
98
- this.ledgerUtxoClient = new SysUtxoClient(this.transport);
99
- }
100
- }
101
-
102
- private getUtxoAddress = async ({
103
- coin,
104
- index, // account index
105
- slip44,
106
- showInLedger,
107
- }: {
108
- coin: string;
109
- index: number;
110
- showInLedger?: boolean;
111
- slip44: number;
112
- }) => {
113
- return this.executeWithRetry(async () => {
114
- const fingerprint = await this.ledgerUtxoClient.getMasterFingerprint();
115
- const xpub = await this.getXpub({ index, coin, slip44 });
116
- this.setHdPath(coin, index, slip44);
117
-
118
- // Convert stored/display zpub/vpub to device-friendly xpub/tpub for policy registration using network macros
119
- const { types: deviceTypes } = getNetworkConfig(slip44, coin);
120
- const devicePubMagicDec =
121
- slip44 === 1
122
- ? (deviceTypes.xPubType as any).testnet.vpub
123
- : deviceTypes.xPubType.mainnet.zpub;
124
- const devicePubMagicHex = Number(devicePubMagicDec)
125
- .toString(16)
126
- .padStart(8, '0');
127
- const deviceXpub = convertExtendedKeyVersion(xpub, devicePubMagicHex);
128
- const xpubWithDescriptor = `[${this.hdPath}]${deviceXpub}`.replace(
129
- 'm',
130
- fingerprint
131
- );
132
- const walletPolicy = new DefaultWalletPolicy(
133
- // Ensure policy template matches BIP84 single-sig wpkh
134
- 'wpkh(@0/**)',
135
- xpubWithDescriptor
136
- );
137
-
138
- const hmac = await this.getOrRegisterHmac(walletPolicy, fingerprint);
139
-
140
- const address = await this.ledgerUtxoClient.getWalletAddress(
141
- walletPolicy,
142
- hmac,
143
- RECEIVING_ADDRESS_INDEX,
144
- index,
145
- !!showInLedger
146
- );
147
-
148
- return address;
149
- }, 'getUtxoAddress');
150
- };
151
-
152
- public verifyUtxoAddress = async (
153
- accountIndex: number,
154
- currency: string,
155
- slip44: number
156
- ) =>
157
- await this.getUtxoAddress({
158
- coin: currency,
159
- index: accountIndex,
160
- slip44: slip44,
161
- showInLedger: true,
162
- });
163
-
164
- private getXpub = async ({
165
- index,
166
- coin,
167
- slip44,
168
- }: {
169
- coin: string;
170
- index: number;
171
- slip44: number;
172
- }): Promise<string> => {
173
- return this.executeWithRetry(async () => {
174
- this.setHdPath(coin, index, slip44);
175
-
176
- {
177
- // Syscoin and general UTXO flow: try silent first, then fall back to on-device display for unusual paths
178
- try {
179
- const xpub = await this.ledgerUtxoClient.getExtendedPubkey(
180
- this.hdPath,
181
- WILL_NOT_DISPLAY
182
- );
183
- // Normalize to BIP84 prefix for Blockbook using network-config magic bytes
184
- const { types } = getNetworkConfig(slip44, coin);
185
- const target =
186
- slip44 === 1
187
- ? (types.zPubType as any).testnet.vpub
188
- : types.zPubType.mainnet.zpub;
189
- return convertExtendedKeyVersion(xpub, target);
190
- } catch (err) {
191
- // Retry with display=true to allow unusual paths with user approval
192
- const xpubWithDisplay = await this.ledgerUtxoClient.getExtendedPubkey(
193
- this.hdPath,
194
- true
195
- );
196
- const { types } = getNetworkConfig(slip44, coin);
197
- const target =
198
- slip44 === 1
199
- ? (types.zPubType as any).testnet.vpub
200
- : types.zPubType.mainnet.zpub;
201
- return convertExtendedKeyVersion(xpubWithDisplay, target);
202
- }
203
- }
204
- }, 'getXpub');
205
- };
206
-
207
- /**
208
- * Sign a UTXO message - public method used by transaction classes
209
- */
210
- public signUtxoMessage = async (path: string, message: string) => {
211
- return this.executeWithRetry(async () => {
212
- const bufferMessage = Buffer.from(message);
213
- const signature = await this.ledgerUtxoClient.signMessage(
214
- bufferMessage,
215
- path
216
- );
217
- return signature;
218
- }, 'signUtxoMessage');
219
- };
220
-
221
- private signEVMTransaction = async ({
222
- rawTx,
223
- accountIndex,
224
- }: {
225
- accountIndex: number;
226
- rawTx: string;
227
- }) => {
228
- return this.executeWithRetry(async () => {
229
- this.setHdPath('eth', accountIndex, 60);
230
- const resolution = await ledgerService.resolveTransaction(rawTx, {}, {});
231
-
232
- const signature = await this.ledgerEVMClient.signTransaction(
233
- this.hdPath.replace(/^m\//, ''), // Remove 'm/' prefix for EVM
234
- rawTx,
235
- resolution
236
- );
237
-
238
- return signature;
239
- }, 'signEVMTransaction');
240
- };
241
-
242
- private signPersonalMessage = async ({
243
- message,
244
- accountIndex,
245
- }: {
246
- accountIndex: number;
247
- message: string;
248
- }) => {
249
- return this.executeWithRetry(async () => {
250
- this.setHdPath('eth', accountIndex, 60);
251
-
252
- const signature = await this.ledgerEVMClient.signPersonalMessage(
253
- this.hdPath.replace(/^m\//, ''), // Remove 'm/' prefix for EVM
254
- message
255
- );
256
-
257
- return `0x${signature.r}${signature.s}${signature.v.toString(16)}`;
258
- }, 'signPersonalMessage');
259
- };
260
-
261
- private sanitizeData(data: any): any {
262
- switch (Object.prototype.toString.call(data)) {
263
- case '[object Object]': {
264
- const entries = Object.keys(data).map((k) => [
265
- k,
266
- this.sanitizeData(data[k]),
267
- ]);
268
- return Object.fromEntries(entries);
269
- }
270
-
271
- case '[object Array]':
272
- return data.map((v: any[]) => this.sanitizeData(v));
273
-
274
- case '[object BigInt]':
275
- return data.toString();
276
-
277
- default:
278
- return data;
279
- }
280
- }
281
-
282
- private transformTypedData = <T extends MessageTypes>(
283
- data: TypedMessage<T>,
284
- version: SignTypedDataVersion
285
- ) => {
286
- const { types, primaryType, domain, message } = this.sanitizeData(data);
287
-
288
- const domainSeparatorHash = TypedDataUtils.hashStruct(
289
- 'EIP712Domain',
290
- this.sanitizeData(domain),
291
- types,
292
- version as SignTypedDataVersion.V3 | SignTypedDataVersion.V4
293
- ).toString('hex');
294
-
295
- let messageHash: string | null = null;
296
-
297
- if (primaryType !== 'EIP712Domain') {
298
- messageHash = TypedDataUtils.hashStruct(
299
- primaryType as string,
300
- this.sanitizeData(message),
301
- types,
302
- version as SignTypedDataVersion.V3 | SignTypedDataVersion.V4
303
- ).toString('hex');
304
- }
305
-
306
- return {
307
- domain_separator_hash: domainSeparatorHash,
308
- message_hash: messageHash,
309
- ...data,
310
- };
311
- };
312
-
313
- private getEvmAddressAndPubKey = async ({
314
- accountIndex,
315
- }: {
316
- accountIndex: number;
317
- }): Promise<{ address: string; publicKey: string }> => {
318
- return this.executeWithRetry(async () => {
319
- this.setHdPath('eth', accountIndex, 60);
320
- const { address, publicKey } = await this.ledgerEVMClient.getAddress(
321
- this.hdPath.replace(/^m\//, '') // Remove 'm/' prefix for EVM
322
- );
323
- return { address, publicKey };
324
- }, 'getEvmAddressAndPubKey');
325
- };
326
-
327
- private signTypedData = async ({
328
- version,
329
- data,
330
- accountIndex,
331
- }: {
332
- accountIndex: number;
333
- data: TypedMessage<any> | TypedDataV1;
334
- version: SignTypedDataVersion;
335
- }) => {
336
- return this.executeWithRetry(async () => {
337
- this.setHdPath('eth', accountIndex, 60);
338
-
339
- // V1 typed data is not supported by hardware wallets
340
- if (version === SignTypedDataVersion.V1) {
341
- throw new Error(
342
- 'Ledger: V1 typed data signing is not supported. Please use V3 or V4.'
343
- );
344
- }
345
-
346
- const dataWithHashes = this.transformTypedData(
347
- data as TypedMessage<any>,
348
- version
349
- );
350
-
351
- const { domain_separator_hash, message_hash } = dataWithHashes;
352
-
353
- const signature = await this.ledgerEVMClient.signEIP712HashedMessage(
354
- this.hdPath.replace(/^m\//, ''), // Remove 'm/' prefix for EVM
355
- domain_separator_hash,
356
- message_hash ? message_hash : ''
357
- );
358
-
359
- return `0x${signature.r}${signature.s}${signature.v.toString(16)}`;
360
- }, 'signTypedData');
361
- };
362
-
363
- private getMasterFingerprint = async () => {
364
- try {
365
- const masterFingerprint =
366
- await this.ledgerUtxoClient.getMasterFingerprint();
367
- return masterFingerprint;
368
- } catch (error) {
369
- console.log('Fingerprint error: ', error);
370
- throw error;
371
- }
372
- };
373
-
374
- // Build a stable cache key for a policy bound to the device and derivation path
375
- private buildWalletCacheKey(
376
- fingerprint: string,
377
- descriptorTemplate: string,
378
- hdPath: string
379
- ): string {
380
- return `${fingerprint}|${descriptorTemplate}|${hdPath}`;
381
- }
382
-
383
- // Lazily register the wallet policy and cache HMAC in memory only
384
- private async getOrRegisterHmac(
385
- walletPolicy: any,
386
- fingerprint: string
387
- ): Promise<Buffer | null> {
388
- const cacheKey = this.buildWalletCacheKey(
389
- fingerprint,
390
- walletPolicy.descriptorTemplate,
391
- this.hdPath
392
- );
393
-
394
- const cached = this.walletHmacCache.get(cacheKey);
395
- if (cached) return cached;
396
-
397
- // If registerWallet is unavailable (tests/mocks), fall back to null HMAC
398
- const registerWallet: any = (this.ledgerUtxoClient as any)?.registerWallet;
399
- if (typeof registerWallet !== 'function') {
400
- return null;
401
- }
402
-
403
- try {
404
- // Register once (device approval). If user cancels, error will propagate via retryOperation
405
- const result = await registerWallet.call(
406
- this.ledgerUtxoClient,
407
- walletPolicy
408
- );
409
- const walletHMAC = Array.isArray(result) ? result[1] : null;
410
- if (walletHMAC && Buffer.isBuffer(walletHMAC)) {
411
- this.walletHmacCache.set(cacheKey, walletHMAC);
412
- return walletHMAC;
413
- }
414
- return null;
415
- } catch (e) {
416
- // On failure, proceed without HMAC (device may prompt)
417
- return null;
418
- }
419
- }
420
-
421
- private setHdPath(coin: string, accountIndex: number, slip44: number) {
422
- if (isEvmCoin(coin, slip44)) {
423
- // For EVM, the "accountIndex" parameter is actually used as the address index
424
- // EVM typically uses account 0, and different addresses are at different address indices
425
- this.hdPath = getAddressDerivationPath(
426
- coin,
427
- slip44,
428
- 0, // account is always 0 for EVM
429
- false, // not a change address
430
- accountIndex // this is actually the address index for EVM
431
- );
432
- } else {
433
- // For UTXO, use account-level derivation path
434
- this.hdPath = getAccountDerivationPath(coin, slip44, accountIndex);
435
- }
436
- }
437
- /**
438
- * Convert PSBT to Ledger format with retry logic
439
- */
440
- public async convertToLedgerFormat(
441
- psbt: any,
442
- accountXpub: string,
443
- accountId: number,
444
- currency: string,
445
- slip44: number
446
- ): Promise<any> {
447
- return this.executeWithRetry(async () => {
448
- // Ensure Ledger is connected before attempting operations
449
- // This is now handled by executeWithRetry
450
-
451
- // Build a bitcoinjs-bip32 network from slip44/currency so zpub/vpub parse directly
452
- const { networks, types } = getNetworkConfig(slip44, currency);
453
- const isTestnet = slip44 === 1;
454
- const pubTypes = isTestnet
455
- ? (types.zPubType as any).testnet
456
- : types.zPubType.mainnet;
457
- const baseNetwork = isTestnet ? networks.testnet : networks.mainnet;
458
- const network = {
459
- ...baseNetwork,
460
- bip32: {
461
- public: parseInt(pubTypes.vpub || pubTypes.zpub, 16),
462
- private: parseInt(pubTypes.vprv || pubTypes.zprv, 16),
463
- },
464
- } as any;
465
-
466
- const bip32 = BIP32Factory(ecc as any);
467
- const accountNode = bip32.fromBase58(accountXpub, network);
468
-
469
- // Get master fingerprint
470
- const fingerprint = await this.getMasterFingerprint();
471
-
472
- // Enhance each input with bip32Derivation
473
- const missingInputDerivations: number[] = [];
474
- for (let i = 0; i < psbt.inputCount; i++) {
475
- const dataInput = psbt.data.inputs[i];
476
-
477
- // Skip if already has bip32Derivation
478
- if (dataInput.bip32Derivation && dataInput.bip32Derivation.length > 0) {
479
- continue;
480
- }
481
-
482
- // Ensure witnessUtxo is present if nonWitnessUtxo exists
483
- if (!dataInput.witnessUtxo && dataInput.nonWitnessUtxo) {
484
- const txBuffer = dataInput.nonWitnessUtxo;
485
- const tx = Transaction.fromBuffer(txBuffer);
486
- const vout = psbt.txInputs[i].index;
487
-
488
- if (tx.outs[vout]) {
489
- dataInput.witnessUtxo = {
490
- script: tx.outs[vout].script,
491
- value: tx.outs[vout].value,
492
- };
493
- }
494
- }
495
-
496
- // Extract path from unknownKeyVals by searching for the key, not using hardcoded index
497
- let pathFromInput: string | null = null;
498
- if (dataInput.unknownKeyVals && dataInput.unknownKeyVals.length > 0) {
499
- for (const kv of dataInput.unknownKeyVals) {
500
- if (kv.key.equals(Buffer.from('path'))) {
501
- pathFromInput = kv.value.toString();
502
- break;
503
- }
504
- }
505
- }
506
-
507
- if (pathFromInput) {
508
- const fullPath = pathFromInput;
509
- const accountPath = getAccountDerivationPath(
510
- currency,
511
- slip44,
512
- accountId
513
- );
514
- const relativePath = fullPath
515
- .replace(accountPath, '')
516
- .replace(/^\//, '');
517
- const derivationTokens = relativePath.split('/').filter((t) => t);
518
-
519
- const derivedAccount = derivationTokens.reduce(
520
- (acc: any, token: string) => {
521
- const index = parseInt(token);
522
- if (isNaN(index)) {
523
- return acc;
524
- }
525
- return acc.derive(index);
526
- },
527
- accountNode
528
- );
529
-
530
- const pubkey = derivedAccount.publicKey;
531
-
532
- if (pubkey && Buffer.isBuffer(pubkey)) {
533
- // Add the bip32Derivation that Ledger needs
534
- const bip32Derivation = {
535
- masterFingerprint: Buffer.from(fingerprint, 'hex'),
536
- path: fullPath,
537
- pubkey: pubkey,
538
- };
539
-
540
- psbt.updateInput(i, {
541
- bip32Derivation: [bip32Derivation],
542
- });
543
- }
544
- }
545
-
546
- if (
547
- !dataInput.bip32Derivation ||
548
- dataInput.bip32Derivation.length === 0
549
- ) {
550
- missingInputDerivations.push(i);
551
- }
552
- }
553
-
554
- // Enhance each output with bip32Derivation when it's a change/output owned by the wallet
555
- const missingOutputDerivations: number[] = [];
556
- for (let i = 0; i < psbt.data.outputs.length; i++) {
557
- const dataOutput = psbt.data.outputs[i];
558
-
559
- // Skip if derivation already present
560
- if (
561
- dataOutput.bip32Derivation &&
562
- dataOutput.bip32Derivation.length > 0
563
- ) {
564
- continue;
565
- }
566
-
567
- // Extract path from unknownKeyVals by searching for the key 'path'
568
- let pathFromOutput: string | null = null;
569
- if (dataOutput.unknownKeyVals && dataOutput.unknownKeyVals.length > 0) {
570
- for (const kv of dataOutput.unknownKeyVals) {
571
- if (kv.key.equals(Buffer.from('path'))) {
572
- pathFromOutput = kv.value.toString();
573
- break;
574
- }
575
- }
576
- }
577
-
578
- if (pathFromOutput) {
579
- const fullPath = pathFromOutput;
580
- const accountPath = getAccountDerivationPath(
581
- currency,
582
- slip44,
583
- accountId
584
- );
585
- const relativePath = fullPath
586
- .replace(accountPath, '')
587
- .replace(/^\//, '');
588
- const derivationTokens = relativePath.split('/').filter((t) => t);
589
-
590
- const derivedAccount = derivationTokens.reduce(
591
- (acc: any, token: string) => {
592
- const index = parseInt(token);
593
- if (isNaN(index)) {
594
- return acc;
595
- }
596
- return acc.derive(index);
597
- },
598
- accountNode
599
- );
600
-
601
- const pubkey = derivedAccount.publicKey;
602
-
603
- if (pubkey && Buffer.isBuffer(pubkey)) {
604
- const bip32Derivation = {
605
- masterFingerprint: Buffer.from(fingerprint, 'hex'),
606
- path: fullPath,
607
- pubkey: pubkey,
608
- };
609
-
610
- psbt.updateOutput(i, {
611
- bip32Derivation: [bip32Derivation],
612
- });
613
- }
614
- }
615
-
616
- // Track outputs that declared a path but still lack derivation info
617
- if (pathFromOutput) {
618
- if (
619
- !dataOutput.bip32Derivation ||
620
- dataOutput.bip32Derivation.length === 0
621
- ) {
622
- missingOutputDerivations.push(i);
623
- }
624
- }
625
- }
626
-
627
- // If any wallet-owned inputs/outputs are missing bip32Derivation, fail early with a clear error
628
- if (
629
- missingInputDerivations.length > 0 ||
630
- missingOutputDerivations.length > 0
631
- ) {
632
- const parts: string[] = [];
633
- if (missingInputDerivations.length > 0) {
634
- parts.push(`inputs [${missingInputDerivations.join(', ')}]`);
635
- }
636
- if (missingOutputDerivations.length > 0) {
637
- parts.push(`outputs [${missingOutputDerivations.join(', ')}]`);
638
- }
639
- throw new Error(
640
- `convertToLedgerFormat: Missing bip32Derivation for ${parts.join(
641
- ' and '
642
- )}. Ensure PSBT includes a 'path' unknownKeyVal or BIP32_DERIVATION for wallet-owned entries.`
643
- );
644
- }
645
-
646
- return psbt;
647
- }, 'convertToLedgerFormat');
648
- }
649
-
650
- /**
651
- * Execute operation with automatic retry
652
- */
653
- private async executeWithRetry<T>(
654
- operation: () => Promise<T>,
655
- operationName: string
656
- ): Promise<T> {
657
- // Ensure connection first
658
- await this.ensureConnection();
659
-
660
- // Use hardware wallet manager's retry mechanism
661
- return this.hardwareWalletManager.retryOperation(operation, operationName, {
662
- maxRetries: 3,
663
- baseDelay: 1000,
664
- maxDelay: 5000,
665
- backoffMultiplier: 2,
666
- });
667
- }
668
-
669
- /**
670
- * Get hardware wallet status
671
- */
672
- public getStatus() {
673
- return this.hardwareWalletManager
674
- .getStatus()
675
- .find((s) => s.type === HardwareWalletType.LEDGER);
676
- }
677
-
678
- /**
679
- * Clean up resources
680
- */
681
- public async destroy() {
682
- await this.hardwareWalletManager.destroy();
683
- this.transport = null;
684
- }
685
- }