@dynamic-labs-wallet/node-btc 0.0.0 → 0.0.255

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/index.esm.js ADDED
@@ -0,0 +1,608 @@
1
+ import { DynamicWalletClient, getMPCChainConfig, getExternalServerKeyShareBackupInfo, WalletOperation } from '@dynamic-labs-wallet/node';
2
+ import { handleAxiosError, BitcoinNetwork, BitcoinAddressType } from '@dynamic-labs-wallet/core';
3
+ import { initEccLib, normalizePublicKey, publicKeyToBitcoinAddress, getAddressTypeFromDerivationPath, extractPublicKeyHex, calculateBip322Hash, calculateTaprootTweak, encodeBip322Signature, doesInputBelongToAddress, collectPSBTInputData, convertSignatureToTaprootBuffer, getBitcoinNetwork, convertSignatureToDER, privateKeyToWIF, wifToPrivateKey, getPublicKeyFromPrivateKey } from '@dynamic-labs-wallet/btc-utils';
4
+ import * as bitcoin from 'bitcoinjs-lib';
5
+ import * as ecc from 'tiny-secp256k1';
6
+ import { Logger } from '@dynamic-labs/logger';
7
+ import { AxiosError } from 'axios';
8
+
9
+ function _extends() {
10
+ _extends = Object.assign || function assign(target) {
11
+ for(var i = 1; i < arguments.length; i++){
12
+ var source = arguments[i];
13
+ for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
14
+ }
15
+ return target;
16
+ };
17
+ return _extends.apply(this, arguments);
18
+ }
19
+
20
+ const logger = new Logger('DynamicWaasWalletClient');
21
+ const logError = ({ message, error, context })=>{
22
+ if (error instanceof AxiosError) {
23
+ handleAxiosError(error, message, context, logger);
24
+ }
25
+ logger.error('[DynamicWaasWalletClient] Error in node-btc client', {
26
+ error: error instanceof Error ? error.message : String(error),
27
+ context
28
+ });
29
+ };
30
+
31
+ const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating wallet account';
32
+ const ERROR_ACCOUNT_ADDRESS_REQUIRED = 'Account address is required';
33
+ const ERROR_SIGN_MESSAGE = 'Error signing message';
34
+ const ERROR_SIGN_TRANSACTION = 'Error signing transaction';
35
+ const ERROR_IMPORT_PRIVATE_KEY = 'Error importing private key';
36
+ const ERROR_KEYGEN_FAILED = 'Error with keygen';
37
+
38
+ // Initialize ECC library with tiny-secp256k1 for Node.js
39
+ initEccLib(ecc);
40
+ class DynamicBtcWalletClient extends DynamicWalletClient {
41
+ /**
42
+ * Derives the Bitcoin account address
43
+ * - BIP340 keys (32 bytes x-only): Only for Taproot addresses
44
+ * - ECDSA keys (33/65 bytes): For all other address types (Legacy, SegWit, Native SegWit)
45
+ * - Algorithm selection is automatic based on addressType
46
+ * @param rawPublicKey - The raw public key to derive the account address from
47
+ * @param addressType - The address type to derive the account address for
48
+ * @param network - The network to derive the account address for
49
+ * @returns The account address
50
+ */ deriveAccountAddress({ rawPublicKey, addressType, network }) {
51
+ // Derive address based on the chosen address type and network
52
+ const normalizedKey = normalizePublicKey(rawPublicKey, addressType);
53
+ const accountAddress = publicKeyToBitcoinAddress(normalizedKey, addressType, network);
54
+ return {
55
+ accountAddress
56
+ };
57
+ }
58
+ /**
59
+ * Gets wallet properties and derivation info for a given account address
60
+ * @param accountAddress - The account address
61
+ * @returns The wallet properties, derivation path, and address type
62
+ */ getWalletDerivationInfo(accountAddress) {
63
+ const walletProperties = this.walletMap[accountAddress];
64
+ if (!walletProperties) {
65
+ throw new Error('Wallet not found in walletMap');
66
+ }
67
+ const derivationPath = walletProperties.derivationPath;
68
+ if (!derivationPath) {
69
+ throw new Error('Derivation path missing in walletMap');
70
+ }
71
+ return {
72
+ walletProperties,
73
+ derivationPath,
74
+ addressType: getAddressTypeFromDerivationPath(derivationPath)
75
+ };
76
+ }
77
+ /**
78
+ * Verifies that the derived address matches the expected address
79
+ * @param rawPublicKey - The raw public key
80
+ * @param addressType - The address type
81
+ * @param network - The network
82
+ * @param expectedAddress - The expected address
83
+ */ verifyWalletAddress(rawPublicKey, addressType, network, expectedAddress) {
84
+ const normalizedKey = normalizePublicKey(rawPublicKey, addressType);
85
+ const derivedAddress = publicKeyToBitcoinAddress(normalizedKey, addressType, network);
86
+ if (derivedAddress !== expectedAddress) {
87
+ throw new Error(`Address verification failed: expected ${expectedAddress}, got ${derivedAddress}`);
88
+ }
89
+ }
90
+ /**
91
+ * Creates a new wallet account and stores the key shares in the wallet map.
92
+ * @param thresholdSignatureScheme - The threshold signature scheme to use for the wallet.
93
+ * @param password - The password to use for the wallet.
94
+ * @param onError - The function to call if an error occurs.
95
+ * @param backUpToClientShareService - Whether to back up the external server key shares to the client share service. By default, it is false.
96
+ * @param bitcoinConfig - Bitcoin configuration (addressType, network)
97
+ * @returns The account address, public key, raw public key, external server key shares, and wallet id.
98
+ */ async createWalletAccount({ thresholdSignatureScheme, password = undefined, onError, backUpToClientShareService = false, bitcoinConfig }) {
99
+ try {
100
+ const { addressType, network = BitcoinNetwork.MAINNET } = bitcoinConfig;
101
+ if (!addressType) {
102
+ throw new Error('addressType is required for BTC');
103
+ }
104
+ let ceremonyCeremonyCompleteResolver;
105
+ let ceremonyAccountAddress;
106
+ const ceremonyCompletePromise = new Promise((resolve)=>{
107
+ ceremonyCeremonyCompleteResolver = resolve;
108
+ });
109
+ // Generate key shares for given threshold signature scheme (TSS)
110
+ const { rawPublicKey, externalServerKeyShares } = await this.keyGen({
111
+ chainName: this.chainName,
112
+ thresholdSignatureScheme,
113
+ skipLock: true,
114
+ bitcoinConfig,
115
+ onError,
116
+ onCeremonyComplete: (accountAddress, walletId)=>{
117
+ // Store the ceremony account address to ensure consistency
118
+ ceremonyAccountAddress = accountAddress;
119
+ // update wallet map
120
+ const chainConfig = getMPCChainConfig(this.chainName, bitcoinConfig);
121
+ this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
122
+ accountAddress,
123
+ walletId,
124
+ chainName: this.chainName,
125
+ thresholdSignatureScheme,
126
+ derivationPath: JSON.stringify(Object.fromEntries(chainConfig.derivationPath.map((value, index)=>[
127
+ index,
128
+ value
129
+ ]))),
130
+ externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
131
+ });
132
+ this.logger.debug('walletMap updated for wallet', {
133
+ context: {
134
+ accountAddress,
135
+ walletId,
136
+ walletMap: this.walletMap
137
+ }
138
+ });
139
+ ceremonyCeremonyCompleteResolver(undefined);
140
+ }
141
+ });
142
+ await ceremonyCompletePromise;
143
+ if (!rawPublicKey || !externalServerKeyShares) {
144
+ throw new Error(ERROR_KEYGEN_FAILED);
145
+ }
146
+ const accountAddress = ceremonyAccountAddress || this.deriveAccountAddress({
147
+ rawPublicKey,
148
+ addressType,
149
+ network
150
+ }).accountAddress;
151
+ await this.storeEncryptedBackupByWalletWithRetry({
152
+ accountAddress,
153
+ externalServerKeyShares,
154
+ password,
155
+ backUpToClientShareService
156
+ });
157
+ const publicKeyHex = extractPublicKeyHex(rawPublicKey);
158
+ return {
159
+ accountAddress,
160
+ rawPublicKey,
161
+ publicKeyHex,
162
+ externalServerKeyShares
163
+ };
164
+ } catch (error) {
165
+ logError({
166
+ message: ERROR_CREATE_WALLET_ACCOUNT,
167
+ error: error,
168
+ context: {}
169
+ });
170
+ throw new Error(ERROR_CREATE_WALLET_ACCOUNT);
171
+ }
172
+ }
173
+ /**
174
+ * Signs a message for BTC using BIP-322 format
175
+ *
176
+ * The address type is automatically derived from the wallet's derivation path.
177
+ * Supports both TAPROOT (BIP-86) and NATIVE_SEGWIT (BIP-84) address types.
178
+ *
179
+ * @param message - The message to sign
180
+ * @param accountAddress - The account address
181
+ * @param network - The Bitcoin network (MAINNET or TESTNET)
182
+ * @param password - Optional password for encrypted backup shares
183
+ * @param externalServerKeyShares - Optional external server key shares
184
+ * @param context - Optional. Sign message context for API tracking
185
+ * @param onError - Optional. Error callback function
186
+ * @returns Promise resolving to the BIP-322 signature as a base64 string
187
+ * @throws Error if required parameters are missing, derivation path is invalid, or signing fails
188
+ */ async signMessage({ message, accountAddress, network, password = undefined, externalServerKeyShares, context, onError }) {
189
+ try {
190
+ var _this_walletMap_accountAddress;
191
+ if (!accountAddress) {
192
+ throw new Error(ERROR_ACCOUNT_ADDRESS_REQUIRED);
193
+ }
194
+ const { derivationPath, addressType } = this.getWalletDerivationInfo(accountAddress);
195
+ // Attempt to recover key shares from backup if not provided
196
+ await this.ensureKeySharesRecovered({
197
+ accountAddress,
198
+ password,
199
+ walletOperation: WalletOperation.SIGN_MESSAGE,
200
+ externalServerKeyShares,
201
+ errorMessage: 'External server key shares are required to sign a message. No backup shares available for recovery.'
202
+ });
203
+ const resolvedExternalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : (_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.externalServerKeyShares;
204
+ if (!resolvedExternalServerKeyShares || resolvedExternalServerKeyShares.length === 0) {
205
+ throw new Error('External server key shares are required to sign a message');
206
+ }
207
+ const bitcoinConfig = {
208
+ addressType,
209
+ network
210
+ };
211
+ const derivedPublicKey = await this.derivePublicKey({
212
+ chainName: this.chainName,
213
+ keyShare: resolvedExternalServerKeyShares[0],
214
+ derivationPath: new Uint32Array(Object.values(JSON.parse(derivationPath))),
215
+ bitcoinConfig
216
+ });
217
+ if (!derivedPublicKey) {
218
+ throw new Error('Failed to derive public key');
219
+ }
220
+ const pubKey = normalizePublicKey(derivedPublicKey, addressType);
221
+ this.verifyWalletAddress(derivedPublicKey, addressType, network, accountAddress);
222
+ // Prepare BIP-322 Transactions and calculate hash
223
+ const { formattedMessage, toSignPsbt } = calculateBip322Hash(message, pubKey, addressType, network, ecc);
224
+ // Prepare tweak for Taproot in case of BIP340
225
+ let tweak;
226
+ if (addressType === BitcoinAddressType.TAPROOT) {
227
+ tweak = calculateTaprootTweak(pubKey);
228
+ }
229
+ // Build complete bitcoinConfig with addressType and tweak
230
+ const completeBitcoinConfig = _extends({}, bitcoinConfig, {
231
+ addressType,
232
+ tweak
233
+ });
234
+ // Sign the message using MPC
235
+ const signature = await this.sign({
236
+ message: formattedMessage,
237
+ accountAddress,
238
+ chainName: this.chainName,
239
+ password,
240
+ externalServerKeyShares: resolvedExternalServerKeyShares,
241
+ isFormatted: true,
242
+ context: context != null ? context : {
243
+ btcMessage: message
244
+ },
245
+ bitcoinConfig: completeBitcoinConfig,
246
+ onError
247
+ });
248
+ const bip322Signature = encodeBip322Signature(toSignPsbt, pubKey, signature, addressType);
249
+ return bip322Signature;
250
+ } catch (error) {
251
+ logError({
252
+ message: ERROR_SIGN_MESSAGE,
253
+ error: error,
254
+ context: {
255
+ accountAddress
256
+ }
257
+ });
258
+ if (onError) {
259
+ onError(error);
260
+ }
261
+ throw new Error(ERROR_SIGN_MESSAGE);
262
+ }
263
+ }
264
+ /**
265
+ * Signs a Bitcoin transaction (PSBT)
266
+ *
267
+ * The address type is automatically derived from the wallet's derivation path.
268
+ * Only inputs belonging to the sender address are signed. Supports both TAPROOT
269
+ * (BIP-341) and Native SegWit (BIP-143) signing methods.
270
+ *
271
+ * @param transaction - The PSBT to sign as a base64 string
272
+ * @param senderAddress - The sender address (must match inputs to be signed)
273
+ * @param network - The Bitcoin network (MAINNET or TESTNET)
274
+ * @param password - Optional password for encrypted backup shares
275
+ * @param externalServerKeyShares - Optional external server key shares
276
+ * @param context - Optional. Sign message context for API tracking
277
+ * @param onError - Optional. Error callback function
278
+ * @returns Promise resolving to the signed PSBT as a base64 string
279
+ * @throws Error if required parameters are missing, no inputs belong to sender address, or signing fails
280
+ */ async signTransaction({ transaction, senderAddress, network, password = undefined, externalServerKeyShares, context, onError }) {
281
+ try {
282
+ await this.verifyPassword({
283
+ accountAddress: senderAddress,
284
+ password,
285
+ walletOperation: WalletOperation.SIGN_TRANSACTION
286
+ });
287
+ if (!senderAddress) {
288
+ throw new Error(ERROR_ACCOUNT_ADDRESS_REQUIRED);
289
+ }
290
+ const { derivationPath, addressType } = this.getWalletDerivationInfo(senderAddress);
291
+ // Attempt to recover key shares from backup if not provided
292
+ await this.ensureKeySharesRecovered({
293
+ accountAddress: senderAddress,
294
+ password,
295
+ walletOperation: WalletOperation.SIGN_TRANSACTION,
296
+ externalServerKeyShares,
297
+ errorMessage: 'External server key shares are required to sign transaction. No backup shares available for recovery.'
298
+ });
299
+ const updatedWalletProperties = this.walletMap[senderAddress];
300
+ if (!updatedWalletProperties) {
301
+ throw new Error('Wallet not found in walletMap after key share recovery');
302
+ }
303
+ const resolvedExternalServerKeyShares = externalServerKeyShares || updatedWalletProperties.externalServerKeyShares;
304
+ if (!resolvedExternalServerKeyShares || resolvedExternalServerKeyShares.length === 0) {
305
+ throw new Error('External server key shares are required to sign transaction. No backup shares available for recovery.');
306
+ }
307
+ const bitcoinConfig = {
308
+ addressType,
309
+ network
310
+ };
311
+ const psbt = bitcoin.Psbt.fromBase64(transaction);
312
+ const derivedPublicKey = await this.derivePublicKey({
313
+ chainName: this.chainName,
314
+ keyShare: resolvedExternalServerKeyShares[0],
315
+ derivationPath: new Uint32Array(Object.values(JSON.parse(derivationPath))),
316
+ bitcoinConfig
317
+ });
318
+ if (!derivedPublicKey) {
319
+ throw new Error('Failed to derive public key');
320
+ }
321
+ const pubKey = normalizePublicKey(derivedPublicKey, addressType);
322
+ const tx = psbt.__CACHE.__TX;
323
+ // Filter inputs to only sign those that belong to the current address
324
+ const inputsToSign = psbt.data.inputs.map((input, i)=>({
325
+ input,
326
+ index: i
327
+ })).filter(({ input })=>doesInputBelongToAddress(input, senderAddress, network));
328
+ if (inputsToSign.length === 0) {
329
+ throw new Error('No inputs found that belong to the sender address');
330
+ }
331
+ if (addressType === BitcoinAddressType.TAPROOT) {
332
+ const tweak = calculateTaprootTweak(pubKey);
333
+ const completeBitcoinConfig = _extends({}, bitcoinConfig, {
334
+ addressType,
335
+ tweak
336
+ });
337
+ const { prevOutScripts, values } = collectPSBTInputData(psbt);
338
+ await Promise.all(inputsToSign.map(async ({ input, index: i })=>{
339
+ if (!input.witnessUtxo) {
340
+ throw new Error(`Input ${i} missing witnessUtxo`);
341
+ }
342
+ const hash = Buffer.from(tx.hashForWitnessV1(i, prevOutScripts, values, bitcoin.Transaction.SIGHASH_DEFAULT));
343
+ const signature = await this.sign({
344
+ message: new Uint8Array(hash),
345
+ accountAddress: senderAddress,
346
+ chainName: this.chainName,
347
+ password,
348
+ externalServerKeyShares: resolvedExternalServerKeyShares,
349
+ isFormatted: true,
350
+ context,
351
+ bitcoinConfig: completeBitcoinConfig,
352
+ onError
353
+ });
354
+ const sigBuffer = convertSignatureToTaprootBuffer(signature);
355
+ psbt.updateInput(i, {
356
+ tapKeySig: sigBuffer
357
+ });
358
+ }));
359
+ } else {
360
+ // Native SegWit (P2WPKH) or other ECDSA-based signing
361
+ const completeBitcoinConfig = _extends({}, bitcoinConfig, {
362
+ addressType
363
+ });
364
+ await Promise.all(inputsToSign.map(async ({ input, index: i })=>{
365
+ if (!input.witnessUtxo) {
366
+ throw new Error(`Input ${i} missing witnessUtxo`);
367
+ }
368
+ const { script, value } = input.witnessUtxo;
369
+ const p2pkh = bitcoin.payments.p2pkh({
370
+ hash: script.slice(2),
371
+ network: getBitcoinNetwork(network)
372
+ });
373
+ const scriptCode = p2pkh.output;
374
+ if (!scriptCode) {
375
+ throw new Error('Failed to generate scriptCode');
376
+ }
377
+ const hash = tx.hashForWitnessV0(i, scriptCode, value, bitcoin.Transaction.SIGHASH_ALL);
378
+ const signature = await this.sign({
379
+ message: new Uint8Array(hash),
380
+ accountAddress: senderAddress,
381
+ chainName: this.chainName,
382
+ password,
383
+ externalServerKeyShares: resolvedExternalServerKeyShares,
384
+ isFormatted: true,
385
+ context,
386
+ bitcoinConfig: completeBitcoinConfig,
387
+ onError
388
+ });
389
+ const derSignature = convertSignatureToDER(signature);
390
+ psbt.updateInput(i, {
391
+ partialSig: [
392
+ {
393
+ pubkey: pubKey,
394
+ signature: new Uint8Array(derSignature)
395
+ }
396
+ ]
397
+ });
398
+ }));
399
+ }
400
+ // Return signed PSBT in base64 format (not finalized)
401
+ return psbt.toBase64();
402
+ } catch (error) {
403
+ logError({
404
+ message: ERROR_SIGN_TRANSACTION,
405
+ error: error,
406
+ context: {
407
+ senderAddress
408
+ }
409
+ });
410
+ if (onError) {
411
+ onError(error);
412
+ }
413
+ throw error;
414
+ }
415
+ }
416
+ /**
417
+ * Exports the private key for a wallet
418
+ * @param accountAddress - The account address to export the private key for
419
+ * @param password - The password for encrypted backup shares
420
+ * @param externalServerKeyShares - Optional external server key shares
421
+ * @returns The private key in WIF format
422
+ */ async exportPrivateKey({ accountAddress, password = undefined, externalServerKeyShares }) {
423
+ await this.verifyPassword({
424
+ accountAddress,
425
+ password,
426
+ walletOperation: WalletOperation.EXPORT_PRIVATE_KEY
427
+ });
428
+ // Attempt to recover key shares from backup if not provided
429
+ await this.ensureKeySharesRecovered({
430
+ accountAddress,
431
+ password,
432
+ walletOperation: WalletOperation.EXPORT_PRIVATE_KEY,
433
+ externalServerKeyShares,
434
+ errorMessage: 'External server key shares are required to export private key. No backup shares available for recovery.'
435
+ });
436
+ // Re-read wallet from map after recovery (it may have been updated with recovered shares)
437
+ const updatedWalletProperties = this.walletMap[accountAddress];
438
+ if (!updatedWalletProperties) {
439
+ throw new Error('Wallet not found in walletMap after recovery');
440
+ }
441
+ const derivationPath = updatedWalletProperties.derivationPath;
442
+ if (!derivationPath) {
443
+ throw new Error('Derivation path missing in walletMap');
444
+ }
445
+ // Derive address type from derivation path for bitcoinConfig
446
+ const addressType = getAddressTypeFromDerivationPath(derivationPath);
447
+ const network = BitcoinNetwork.MAINNET; // Default to mainnet, could be derived from wallet
448
+ const bitcoinConfig = {
449
+ addressType,
450
+ network
451
+ };
452
+ // Get key shares from wallet map if not provided (recovered from backup)
453
+ const resolvedExternalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : updatedWalletProperties.externalServerKeyShares;
454
+ const { derivedPrivateKey } = await this.exportKey({
455
+ accountAddress,
456
+ chainName: this.chainName,
457
+ password,
458
+ externalServerKeyShares: resolvedExternalServerKeyShares,
459
+ bitcoinConfig
460
+ });
461
+ if (!derivedPrivateKey) {
462
+ throw new Error('Derived private key is undefined');
463
+ }
464
+ // Convert private key to WIF format
465
+ const wifPrivateKey = privateKeyToWIF(derivedPrivateKey, network);
466
+ return wifPrivateKey;
467
+ }
468
+ /**
469
+ * Exports the private key for a given account address offline using key shares
470
+ * @param keyShares - The key shares to export the private key for
471
+ * @param derivationPath - Optional derivation path
472
+ * @param bitcoinConfig - Bitcoin configuration (address type, network)
473
+ * @returns The private key in WIF format
474
+ */ async offlineExportPrivateKey({ keyShares, derivationPath, bitcoinConfig }) {
475
+ const { derivedPrivateKey } = await this.offlineExportKey({
476
+ chainName: this.chainName,
477
+ keyShares,
478
+ derivationPath
479
+ });
480
+ if (!derivedPrivateKey) {
481
+ throw new Error('Derived private key is undefined');
482
+ }
483
+ const network = (bitcoinConfig == null ? void 0 : bitcoinConfig.network) || BitcoinNetwork.MAINNET;
484
+ // Convert private key to WIF format
485
+ const wifPrivateKey = privateKeyToWIF(derivedPrivateKey, network);
486
+ return wifPrivateKey;
487
+ }
488
+ /**
489
+ * Imports a private key and stores the key shares in the wallet map.
490
+ * @param privateKey - The private key to import (WIF format)
491
+ * @param chainName - The chain name to use for the wallet
492
+ * @param thresholdSignatureScheme - The threshold signature scheme to use for the wallet
493
+ * @param password - The password to use for the wallet
494
+ * @param onError - The function to call if an error occurs
495
+ * @param backUpToClientShareService - Whether to back up the external server key shares to the client share service. By default, it is false.
496
+ * @param bitcoinConfig - Bitcoin configuration (addressType, network)
497
+ * @returns The account address, public key, raw public key, external server key shares
498
+ */ async importPrivateKey({ privateKey, chainName, thresholdSignatureScheme, password = undefined, backUpToClientShareService = false, onError, bitcoinConfig }) {
499
+ try {
500
+ const { addressType, network = BitcoinNetwork.MAINNET } = bitcoinConfig;
501
+ if (!addressType) {
502
+ throw new Error('addressType is required for BTC importPrivateKey');
503
+ }
504
+ if (addressType !== BitcoinAddressType.NATIVE_SEGWIT && addressType !== BitcoinAddressType.TAPROOT) {
505
+ throw new Error(`Invalid addressType: ${addressType}. Must be one of: ${BitcoinAddressType.NATIVE_SEGWIT}, ${BitcoinAddressType.TAPROOT}`);
506
+ }
507
+ let ceremonyCeremonyCompleteResolver;
508
+ let ceremonyAccountAddress;
509
+ const ceremonyCompletePromise = new Promise((resolve)=>{
510
+ ceremonyCeremonyCompleteResolver = resolve;
511
+ });
512
+ // Convert WIF to private key hex
513
+ const formattedPrivateKey = wifToPrivateKey(privateKey, network);
514
+ // Get public key from private key to verify address
515
+ const derivedPublicKey = await getPublicKeyFromPrivateKey(privateKey, addressType, ecc, network);
516
+ const { accountAddress: expectedAddress } = this.deriveAccountAddress({
517
+ rawPublicKey: derivedPublicKey,
518
+ addressType,
519
+ network
520
+ });
521
+ const { rawPublicKey, externalServerKeyShares } = await this.importRawPrivateKey({
522
+ chainName,
523
+ privateKey: formattedPrivateKey,
524
+ thresholdSignatureScheme,
525
+ bitcoinConfig,
526
+ onError,
527
+ onCeremonyComplete: (accountAddress, walletId)=>{
528
+ // Store the ceremony account address to ensure consistency
529
+ ceremonyAccountAddress = accountAddress;
530
+ // Verify address matches
531
+ if (accountAddress !== expectedAddress) {
532
+ throw new Error(`Public address mismatch: derived address ${accountAddress} !== expected address ${expectedAddress}`);
533
+ }
534
+ // update wallet map
535
+ const chainConfig = getMPCChainConfig(this.chainName, bitcoinConfig);
536
+ this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
537
+ accountAddress,
538
+ walletId,
539
+ chainName: this.chainName,
540
+ thresholdSignatureScheme,
541
+ derivationPath: JSON.stringify(Object.fromEntries(chainConfig.derivationPath.map((value, index)=>[
542
+ index,
543
+ value
544
+ ]))),
545
+ externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
546
+ });
547
+ ceremonyCeremonyCompleteResolver(undefined);
548
+ }
549
+ });
550
+ // Wait for the ceremony to complete before proceeding
551
+ await ceremonyCompletePromise;
552
+ if (!rawPublicKey || !externalServerKeyShares) {
553
+ throw new Error('Error creating wallet account');
554
+ }
555
+ // Use the ceremony account address if available, otherwise derive it from raw public key
556
+ const accountAddress = ceremonyAccountAddress || this.deriveAccountAddress({
557
+ rawPublicKey,
558
+ addressType,
559
+ network
560
+ }).accountAddress;
561
+ if (accountAddress !== expectedAddress) {
562
+ throw new Error(`Public address mismatch: derived address ${accountAddress} !== expected address ${expectedAddress}`);
563
+ }
564
+ await this.storeEncryptedBackupByWalletWithRetry({
565
+ accountAddress,
566
+ externalServerKeyShares,
567
+ password,
568
+ backUpToClientShareService
569
+ });
570
+ const publicKeyHex = extractPublicKeyHex(rawPublicKey);
571
+ return {
572
+ accountAddress,
573
+ rawPublicKey,
574
+ publicKeyHex,
575
+ externalServerKeyShares
576
+ };
577
+ } catch (error) {
578
+ logError({
579
+ message: ERROR_IMPORT_PRIVATE_KEY,
580
+ error: error,
581
+ context: {}
582
+ });
583
+ throw new Error(ERROR_IMPORT_PRIVATE_KEY);
584
+ }
585
+ }
586
+ /**
587
+ * Gets the Bitcoin wallets
588
+ * @returns The Bitcoin wallets
589
+ */ async getBitcoinWallets() {
590
+ const wallets = await this.getWallets(); // NOSONAR
591
+ const btcWallets = wallets.filter((wallet)=>{
592
+ var _wallet_chainName;
593
+ const chainName = (_wallet_chainName = wallet.chainName) == null ? void 0 : _wallet_chainName.toLowerCase();
594
+ return chainName === 'btc' || chainName === 'bip122';
595
+ });
596
+ return btcWallets;
597
+ }
598
+ constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, enableMPCAccelerator }){
599
+ super({
600
+ environmentId,
601
+ baseApiUrl,
602
+ baseMPCRelayApiUrl,
603
+ enableMPCAccelerator
604
+ }), this.chainName = 'BTC';
605
+ }
606
+ }
607
+
608
+ export { DynamicBtcWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TRANSACTION };
package/package.json CHANGED
@@ -1 +1,43 @@
1
- { "name": "@dynamic-labs-wallet/node-btc", "version": "0.0.0" }
1
+ {
2
+ "name": "@dynamic-labs-wallet/node-btc",
3
+ "version": "0.0.255",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@dynamic-labs-wallet/core": "0.0.255",
8
+ "@dynamic-labs-wallet/node": "0.0.255",
9
+ "@dynamic-labs-wallet/btc-utils": "0.0.255",
10
+ "@dynamic-labs/logger": "^4.25.3",
11
+ "@dynamic-labs/sdk-api-core": "^0.0.801",
12
+ "axios": "1.13.2",
13
+ "bitcoinjs-lib": "^7.0.0",
14
+ "tiny-secp256k1": "^2.2.3"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "nx": {
20
+ "sourceRoot": "packages/node-btc/src",
21
+ "projectType": "library",
22
+ "name": "node-btc",
23
+ "targets": {
24
+ "build": {}
25
+ }
26
+ },
27
+ "main": "./index.cjs.js",
28
+ "module": "./index.esm.js",
29
+ "types": "./index.esm.d.ts",
30
+ "exports": {
31
+ "./package.json": "./package.json",
32
+ ".": {
33
+ "types": "./index.esm.d.ts",
34
+ "import": "./index.esm.js",
35
+ "require": "./index.cjs.js"
36
+ }
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.0.0",
40
+ "@types/node": "^20.0.0",
41
+ "@types/http-errors": "^2.0.0"
42
+ }
43
+ }