@sidhujag/sysweb3-keyring 1.0.547 → 1.0.548

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 (210) hide show
  1. package/package.json +2 -27
  2. package/coverage/clover.xml +0 -2875
  3. package/coverage/coverage-final.json +0 -29468
  4. package/coverage/lcov-report/base.css +0 -354
  5. package/coverage/lcov-report/block-navigation.js +0 -85
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +0 -320
  8. package/coverage/lcov-report/prettify.css +0 -101
  9. package/coverage/lcov-report/prettify.js +0 -1008
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +0 -191
  12. package/coverage/lcov-report/src/index.html +0 -276
  13. package/coverage/lcov-report/src/index.ts.html +0 -114
  14. package/coverage/lcov-report/src/initial-state.ts.html +0 -558
  15. package/coverage/lcov-report/src/keyring-manager.ts.html +0 -6279
  16. package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +0 -178
  17. package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +0 -144
  18. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +0 -1560
  19. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +0 -276
  20. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +0 -495
  21. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +0 -1138
  22. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +0 -363
  23. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +0 -289
  24. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +0 -486
  25. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +0 -240
  26. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +0 -342
  27. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +0 -2388
  28. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +0 -453
  29. package/coverage/lcov-report/src/ledger/consts.ts.html +0 -177
  30. package/coverage/lcov-report/src/ledger/index.html +0 -216
  31. package/coverage/lcov-report/src/ledger/index.ts.html +0 -1371
  32. package/coverage/lcov-report/src/ledger/utils.ts.html +0 -102
  33. package/coverage/lcov-report/src/signers.ts.html +0 -591
  34. package/coverage/lcov-report/src/storage.ts.html +0 -198
  35. package/coverage/lcov-report/src/transactions/ethereum.ts.html +0 -5826
  36. package/coverage/lcov-report/src/transactions/index.html +0 -216
  37. package/coverage/lcov-report/src/transactions/index.ts.html +0 -93
  38. package/coverage/lcov-report/src/transactions/syscoin.ts.html +0 -1521
  39. package/coverage/lcov-report/src/trezor/index.html +0 -176
  40. package/coverage/lcov-report/src/trezor/index.ts.html +0 -2655
  41. package/coverage/lcov-report/src/types.ts.html +0 -1443
  42. package/coverage/lcov-report/src/utils/derivation-paths.ts.html +0 -486
  43. package/coverage/lcov-report/src/utils/index.html +0 -196
  44. package/coverage/lcov-report/src/utils/psbt.ts.html +0 -159
  45. package/coverage/lcov-report/test/helpers/constants.ts.html +0 -627
  46. package/coverage/lcov-report/test/helpers/index.html +0 -176
  47. package/coverage/lcov.info +0 -4832
  48. package/dist/package.json +0 -50
  49. package/examples/basic-usage.js +0 -140
  50. package/jest.config.js +0 -32
  51. package/readme.md +0 -201
  52. package/src/declare.d.ts +0 -7
  53. package/src/errorUtils.ts +0 -83
  54. package/src/hardware-wallet-manager.ts +0 -655
  55. package/src/index.ts +0 -12
  56. package/src/initial-state.ts +0 -108
  57. package/src/keyring-manager.ts +0 -2698
  58. package/src/ledger/bitcoin_client/index.ts +0 -19
  59. package/src/ledger/bitcoin_client/lib/appClient.ts +0 -405
  60. package/src/ledger/bitcoin_client/lib/bip32.ts +0 -61
  61. package/src/ledger/bitcoin_client/lib/buffertools.ts +0 -134
  62. package/src/ledger/bitcoin_client/lib/clientCommands.ts +0 -356
  63. package/src/ledger/bitcoin_client/lib/constants.ts +0 -12
  64. package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +0 -65
  65. package/src/ledger/bitcoin_client/lib/merkle.ts +0 -136
  66. package/src/ledger/bitcoin_client/lib/merkleMap.ts +0 -49
  67. package/src/ledger/bitcoin_client/lib/policy.ts +0 -91
  68. package/src/ledger/bitcoin_client/lib/psbtv2.ts +0 -768
  69. package/src/ledger/bitcoin_client/lib/varint.ts +0 -120
  70. package/src/ledger/consts.ts +0 -3
  71. package/src/ledger/index.ts +0 -685
  72. package/src/ledger/types.ts +0 -74
  73. package/src/network-utils.ts +0 -99
  74. package/src/providers.ts +0 -345
  75. package/src/signers.ts +0 -158
  76. package/src/storage.ts +0 -63
  77. package/src/transactions/__tests__/integration.test.ts +0 -303
  78. package/src/transactions/__tests__/syscoin.test.ts +0 -409
  79. package/src/transactions/ethereum.ts +0 -2503
  80. package/src/transactions/index.ts +0 -2
  81. package/src/transactions/syscoin.ts +0 -542
  82. package/src/trezor/index.ts +0 -1050
  83. package/src/types.ts +0 -366
  84. package/src/utils/derivation-paths.ts +0 -133
  85. package/src/utils/psbt.ts +0 -24
  86. package/src/utils.ts +0 -191
  87. package/test/README.md +0 -158
  88. package/test/__mocks__/ledger-mock.js +0 -20
  89. package/test/__mocks__/trezor-mock.js +0 -75
  90. package/test/cleanup-summary.md +0 -167
  91. package/test/helpers/README.md +0 -78
  92. package/test/helpers/constants.ts +0 -79
  93. package/test/helpers/setup.ts +0 -714
  94. package/test/integration/import-validation.spec.ts +0 -588
  95. package/test/unit/hardware/ledger.spec.ts +0 -869
  96. package/test/unit/hardware/trezor.spec.ts +0 -828
  97. package/test/unit/keyring-manager/account-management.spec.ts +0 -970
  98. package/test/unit/keyring-manager/import-watchonly.spec.ts +0 -181
  99. package/test/unit/keyring-manager/import-wif.spec.ts +0 -126
  100. package/test/unit/keyring-manager/initialization.spec.ts +0 -782
  101. package/test/unit/keyring-manager/key-derivation.spec.ts +0 -996
  102. package/test/unit/keyring-manager/security.spec.ts +0 -505
  103. package/test/unit/keyring-manager/state-management.spec.ts +0 -375
  104. package/test/unit/network/network-management.spec.ts +0 -372
  105. package/test/unit/transactions/ethereum-transactions.spec.ts +0 -382
  106. package/test/unit/transactions/syscoin-transactions.spec.ts +0 -615
  107. package/tsconfig.json +0 -14
  108. /package/{dist/README.md → README.md} +0 -0
  109. /package/{dist/cjs → cjs}/errorUtils.js +0 -0
  110. /package/{dist/cjs → cjs}/errorUtils.js.map +0 -0
  111. /package/{dist/cjs → cjs}/hardware-wallet-manager.js +0 -0
  112. /package/{dist/cjs → cjs}/hardware-wallet-manager.js.map +0 -0
  113. /package/{dist/cjs → cjs}/index.js +0 -0
  114. /package/{dist/cjs → cjs}/index.js.map +0 -0
  115. /package/{dist/cjs → cjs}/initial-state.js +0 -0
  116. /package/{dist/cjs → cjs}/initial-state.js.map +0 -0
  117. /package/{dist/cjs → cjs}/keyring-manager.js +0 -0
  118. /package/{dist/cjs → cjs}/keyring-manager.js.map +0 -0
  119. /package/{dist/cjs → cjs}/ledger/bitcoin_client/index.js +0 -0
  120. /package/{dist/cjs → cjs}/ledger/bitcoin_client/index.js.map +0 -0
  121. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/appClient.js +0 -0
  122. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/appClient.js.map +0 -0
  123. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
  124. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
  125. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
  126. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
  127. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
  128. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
  129. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
  130. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
  131. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
  132. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
  133. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
  134. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
  135. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
  136. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
  137. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
  138. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
  139. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
  140. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
  141. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
  142. /package/{dist/cjs → cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
  143. /package/{dist/cjs → cjs}/ledger/consts.js +0 -0
  144. /package/{dist/cjs → cjs}/ledger/consts.js.map +0 -0
  145. /package/{dist/cjs → cjs}/ledger/index.js +0 -0
  146. /package/{dist/cjs → cjs}/ledger/index.js.map +0 -0
  147. /package/{dist/cjs → cjs}/ledger/types.js +0 -0
  148. /package/{dist/cjs → cjs}/ledger/types.js.map +0 -0
  149. /package/{dist/cjs → cjs}/network-utils.js +0 -0
  150. /package/{dist/cjs → cjs}/network-utils.js.map +0 -0
  151. /package/{dist/cjs → cjs}/providers.js +0 -0
  152. /package/{dist/cjs → cjs}/providers.js.map +0 -0
  153. /package/{dist/cjs → cjs}/signers.js +0 -0
  154. /package/{dist/cjs → cjs}/signers.js.map +0 -0
  155. /package/{dist/cjs → cjs}/storage.js +0 -0
  156. /package/{dist/cjs → cjs}/storage.js.map +0 -0
  157. /package/{dist/cjs → cjs}/transactions/__tests__/integration.test.js +0 -0
  158. /package/{dist/cjs → cjs}/transactions/__tests__/integration.test.js.map +0 -0
  159. /package/{dist/cjs → cjs}/transactions/__tests__/syscoin.test.js +0 -0
  160. /package/{dist/cjs → cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
  161. /package/{dist/cjs → cjs}/transactions/ethereum.js +0 -0
  162. /package/{dist/cjs → cjs}/transactions/ethereum.js.map +0 -0
  163. /package/{dist/cjs → cjs}/transactions/index.js +0 -0
  164. /package/{dist/cjs → cjs}/transactions/index.js.map +0 -0
  165. /package/{dist/cjs → cjs}/transactions/syscoin.js +0 -0
  166. /package/{dist/cjs → cjs}/transactions/syscoin.js.map +0 -0
  167. /package/{dist/cjs → cjs}/trezor/index.js +0 -0
  168. /package/{dist/cjs → cjs}/trezor/index.js.map +0 -0
  169. /package/{dist/cjs → cjs}/types.js +0 -0
  170. /package/{dist/cjs → cjs}/types.js.map +0 -0
  171. /package/{dist/cjs → cjs}/utils/derivation-paths.js +0 -0
  172. /package/{dist/cjs → cjs}/utils/derivation-paths.js.map +0 -0
  173. /package/{dist/cjs → cjs}/utils/psbt.js +0 -0
  174. /package/{dist/cjs → cjs}/utils/psbt.js.map +0 -0
  175. /package/{dist/cjs → cjs}/utils.js +0 -0
  176. /package/{dist/cjs → cjs}/utils.js.map +0 -0
  177. /package/{dist/types → types}/errorUtils.d.ts +0 -0
  178. /package/{dist/types → types}/hardware-wallet-manager.d.ts +0 -0
  179. /package/{dist/types → types}/index.d.ts +0 -0
  180. /package/{dist/types → types}/initial-state.d.ts +0 -0
  181. /package/{dist/types → types}/keyring-manager.d.ts +0 -0
  182. /package/{dist/types → types}/ledger/bitcoin_client/index.d.ts +0 -0
  183. /package/{dist/types → types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -0
  184. /package/{dist/types → types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
  185. /package/{dist/types → types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
  186. /package/{dist/types → types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
  187. /package/{dist/types → types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
  188. /package/{dist/types → types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
  189. /package/{dist/types → types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
  190. /package/{dist/types → types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
  191. /package/{dist/types → types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
  192. /package/{dist/types → types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
  193. /package/{dist/types → types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
  194. /package/{dist/types → types}/ledger/consts.d.ts +0 -0
  195. /package/{dist/types → types}/ledger/index.d.ts +0 -0
  196. /package/{dist/types → types}/ledger/types.d.ts +0 -0
  197. /package/{dist/types → types}/network-utils.d.ts +0 -0
  198. /package/{dist/types → types}/providers.d.ts +0 -0
  199. /package/{dist/types → types}/signers.d.ts +0 -0
  200. /package/{dist/types → types}/storage.d.ts +0 -0
  201. /package/{dist/types → types}/transactions/__tests__/integration.test.d.ts +0 -0
  202. /package/{dist/types → types}/transactions/__tests__/syscoin.test.d.ts +0 -0
  203. /package/{dist/types → types}/transactions/ethereum.d.ts +0 -0
  204. /package/{dist/types → types}/transactions/index.d.ts +0 -0
  205. /package/{dist/types → types}/transactions/syscoin.d.ts +0 -0
  206. /package/{dist/types → types}/trezor/index.d.ts +0 -0
  207. /package/{dist/types → types}/types.d.ts +0 -0
  208. /package/{dist/types → types}/utils/derivation-paths.d.ts +0 -0
  209. /package/{dist/types → types}/utils/psbt.d.ts +0 -0
  210. /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
- }