@dynamic-labs/bitcoin 4.91.2 → 4.91.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
1
 
2
+ ### [4.91.4](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.3...v4.91.4) (2026-07-01)
3
+
4
+
5
+ ### Bug Fixes
6
+
7
+ * conditionally use /op/ route for AA transactions based on block explorer support ([#11628](https://github.com/dynamic-labs/dynamic-auth/issues/11628)) ([95822dc](https://github.com/dynamic-labs/dynamic-auth/commit/95822dc0edbcd739afe870f0b3881555fdb17eb6))
8
+ * require error codes in all SDK error classes ([#11761](https://github.com/dynamic-labs/dynamic-auth/issues/11761)) ([5744811](https://github.com/dynamic-labs/dynamic-auth/commit/5744811efdd1e415aaf51eec5b3300b323ec1347))
9
+
10
+ ### [4.91.3](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.2...v4.91.3) (2026-06-30)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **bitcoin:** detect late-registering wallet-standard wallets (DYNT-1210) ([#11747](https://github.com/dynamic-labs/dynamic-auth/issues/11747)) ([17809f6](https://github.com/dynamic-labs/dynamic-auth/commit/17809f6e811fe101367cc31a2d899a8d0b0d107e))
16
+ * fetch onramp providers lazily instead of eagerly on mount ([#11767](https://github.com/dynamic-labs/dynamic-auth/issues/11767)) ([3ee6708](https://github.com/dynamic-labs/dynamic-auth/commit/3ee67087142ae1942f865e0ab37b7108700b3327))
17
+ * **iframe-setup:** sync parent URL to iframe on pushState/replaceState ([#11751](https://github.com/dynamic-labs/dynamic-auth/issues/11751)) ([c8be3af](https://github.com/dynamic-labs/dynamic-auth/commit/c8be3afc301f77b349f23398bed940ce40f93b73))
18
+ * **nonce:** fetch nonce on demand when missing before wallet verification ([#11753](https://github.com/dynamic-labs/dynamic-auth/issues/11753)) ([4f01a21](https://github.com/dynamic-labs/dynamic-auth/commit/4f01a21b6081c852f32868c8a5e04d70aaf280a2)), closes [#1](https://github.com/dynamic-labs/dynamic-auth/issues/1)
19
+ * **react-native-extension:** arm loading timer before setUrl to cover pre-onLoadStart gap ([#11764](https://github.com/dynamic-labs/dynamic-auth/issues/11764)) ([31f2015](https://github.com/dynamic-labs/dynamic-auth/commit/31f20151a4b59279f9c133b723e50de771b06436))
20
+ * **webview-controller:** unwrap ZeroDev smart wallet for native gasless ([#11758](https://github.com/dynamic-labs/dynamic-auth/issues/11758)) ([989714e](https://github.com/dynamic-labs/dynamic-auth/commit/989714e414556868c8e0c54401aac1ed7354331b))
21
+
2
22
  ### [4.91.2](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.1...v4.91.2) (2026-06-26)
3
23
 
4
24
 
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.91.2";
6
+ var version = "4.91.4";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "4.91.2";
2
+ var version = "4.91.4";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/bitcoin",
3
- "version": "4.91.2",
3
+ "version": "4.91.4",
4
4
  "description": "A React SDK for implementing wallet web3 authentication and authorization to your website.",
5
5
  "author": "Dynamic Labs, Inc.",
6
6
  "license": "MIT",
@@ -29,13 +29,13 @@
29
29
  "ecpair": "2.1.0",
30
30
  "sats-connect": "4.2.1",
31
31
  "jsontokens": "4.0.1",
32
- "@dynamic-labs/assert-package-version": "4.91.2",
33
- "@dynamic-labs/logger": "4.91.2",
34
- "@dynamic-labs/types": "4.91.2",
35
- "@dynamic-labs/utils": "4.91.2",
36
- "@dynamic-labs/waas": "4.91.2",
37
- "@dynamic-labs/wallet-book": "4.91.2",
38
- "@dynamic-labs/wallet-connector-core": "4.91.2",
32
+ "@dynamic-labs/assert-package-version": "4.91.4",
33
+ "@dynamic-labs/logger": "4.91.4",
34
+ "@dynamic-labs/types": "4.91.4",
35
+ "@dynamic-labs/utils": "4.91.4",
36
+ "@dynamic-labs/waas": "4.91.4",
37
+ "@dynamic-labs/wallet-book": "4.91.4",
38
+ "@dynamic-labs/wallet-connector-core": "4.91.4",
39
39
  "eventemitter3": "5.0.1"
40
40
  },
41
41
  "peerDependencies": {}
@@ -54,8 +54,19 @@ class BitcoinWalletConnector extends walletConnectorCore.WalletConnectorBase {
54
54
  return Boolean((_a = this.metadata.supportedHardwareWallets) === null || _a === void 0 ? void 0 : _a.includes('ledger'));
55
55
  }
56
56
  isInstalledOnBrowser() {
57
- var _a;
58
- return (Boolean(this.wallet) || Boolean((_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.getProvider()));
57
+ var _a, _b, _c;
58
+ // Re-run the wallet-standard lookup on each call so that a wallet which
59
+ // registered after the constructor ran (late injection via wallet-standard)
60
+ // is picked up by the existing useReconcileInstalledWallets poll.
61
+ if (!this.wallet) {
62
+ const found = (_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.findWallet();
63
+ if (found) {
64
+ this.wallet = found;
65
+ this.walletMethods =
66
+ (_b = this.bitcoinProviderHelper) === null || _b === void 0 ? void 0 : _b.getWalletMethods(found);
67
+ }
68
+ }
69
+ return (Boolean(this.wallet) || Boolean((_c = this.bitcoinProviderHelper) === null || _c === void 0 ? void 0 : _c.getProvider()));
59
70
  }
60
71
  getDeepLink() {
61
72
  return undefined;
@@ -167,11 +178,11 @@ class BitcoinWalletConnector extends walletConnectorCore.WalletConnectorBase {
167
178
  sendRawTransaction(rawTransaction) {
168
179
  return _tslib.__awaiter(this, void 0, void 0, function* () {
169
180
  if (!rawTransaction) {
170
- throw new utils.DynamicError('No transaction specified!');
181
+ throw new utils.DynamicError('No transaction specified!', 'no_transaction_specified');
171
182
  }
172
183
  const [connectedAddress] = yield this.getConnectedAccounts();
173
184
  if (!connectedAddress) {
174
- throw new utils.DynamicError('No connected address found!');
185
+ throw new utils.DynamicError('No connected address found!', 'no_connected_address_found');
175
186
  }
176
187
  const API_URL = getMempoolApiUrl.getMempoolApiUrl(connectedAddress);
177
188
  const response = yield fetch(`${API_URL}/tx`, {
@@ -183,11 +194,11 @@ class BitcoinWalletConnector extends walletConnectorCore.WalletConnectorBase {
183
194
  });
184
195
  if (!response.ok) {
185
196
  if (response.status === _const.HTTP_STATUS_TOO_MANY_REQUESTS) {
186
- throw new utils.DynamicError('sendRawTransaction - mempool api rate limit exceeded');
197
+ throw new utils.DynamicError('sendRawTransaction - mempool api rate limit exceeded', 'sendrawtransaction_mempool_api_rate_limit_exceeded');
187
198
  }
188
199
  const error = yield response.text();
189
200
  walletConnectorCore.logger.debug(`sendRawTransaction - response not ok: ${JSON.stringify(error)}`);
190
- throw new utils.DynamicError('sendRawTransaction - failed to send transaction');
201
+ throw new utils.DynamicError('sendRawTransaction - failed to send transaction', 'sendrawtransaction_failed_to_send_transaction');
191
202
  }
192
203
  // this endpoint returns the transaction ID
193
204
  return response.text();
@@ -50,8 +50,19 @@ class BitcoinWalletConnector extends WalletConnectorBase {
50
50
  return Boolean((_a = this.metadata.supportedHardwareWallets) === null || _a === void 0 ? void 0 : _a.includes('ledger'));
51
51
  }
52
52
  isInstalledOnBrowser() {
53
- var _a;
54
- return (Boolean(this.wallet) || Boolean((_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.getProvider()));
53
+ var _a, _b, _c;
54
+ // Re-run the wallet-standard lookup on each call so that a wallet which
55
+ // registered after the constructor ran (late injection via wallet-standard)
56
+ // is picked up by the existing useReconcileInstalledWallets poll.
57
+ if (!this.wallet) {
58
+ const found = (_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.findWallet();
59
+ if (found) {
60
+ this.wallet = found;
61
+ this.walletMethods =
62
+ (_b = this.bitcoinProviderHelper) === null || _b === void 0 ? void 0 : _b.getWalletMethods(found);
63
+ }
64
+ }
65
+ return (Boolean(this.wallet) || Boolean((_c = this.bitcoinProviderHelper) === null || _c === void 0 ? void 0 : _c.getProvider()));
55
66
  }
56
67
  getDeepLink() {
57
68
  return undefined;
@@ -163,11 +174,11 @@ class BitcoinWalletConnector extends WalletConnectorBase {
163
174
  sendRawTransaction(rawTransaction) {
164
175
  return __awaiter(this, void 0, void 0, function* () {
165
176
  if (!rawTransaction) {
166
- throw new DynamicError('No transaction specified!');
177
+ throw new DynamicError('No transaction specified!', 'no_transaction_specified');
167
178
  }
168
179
  const [connectedAddress] = yield this.getConnectedAccounts();
169
180
  if (!connectedAddress) {
170
- throw new DynamicError('No connected address found!');
181
+ throw new DynamicError('No connected address found!', 'no_connected_address_found');
171
182
  }
172
183
  const API_URL = getMempoolApiUrl(connectedAddress);
173
184
  const response = yield fetch(`${API_URL}/tx`, {
@@ -179,11 +190,11 @@ class BitcoinWalletConnector extends WalletConnectorBase {
179
190
  });
180
191
  if (!response.ok) {
181
192
  if (response.status === HTTP_STATUS_TOO_MANY_REQUESTS) {
182
- throw new DynamicError('sendRawTransaction - mempool api rate limit exceeded');
193
+ throw new DynamicError('sendRawTransaction - mempool api rate limit exceeded', 'sendrawtransaction_mempool_api_rate_limit_exceeded');
183
194
  }
184
195
  const error = yield response.text();
185
196
  logger.debug(`sendRawTransaction - response not ok: ${JSON.stringify(error)}`);
186
- throw new DynamicError('sendRawTransaction - failed to send transaction');
197
+ throw new DynamicError('sendRawTransaction - failed to send transaction', 'sendrawtransaction_failed_to_send_transaction');
187
198
  }
188
199
  // this endpoint returns the transaction ID
189
200
  return response.text();
@@ -14,6 +14,7 @@ var MempoolApiService = require('../../services/MempoolApiService.cjs');
14
14
  var PsbtBuilderService = require('../../services/PsbtBuilderService.cjs');
15
15
  require('sats-connect');
16
16
  var BitcoinWalletConnector = require('../BitcoinWalletConnector.cjs');
17
+ require('@wallet-standard/app');
17
18
  require('jsontokens');
18
19
  var btcToSatoshis = require('../../utils/btcToSatoshis/btcToSatoshis.cjs');
19
20
  var PsbtParser = require('../../utils/psbtParser/PsbtParser.cjs');
@@ -89,7 +90,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
89
90
  this.setActiveAccount(address);
90
91
  return address;
91
92
  }
92
- throw new utils.DynamicError('No active wallet address found');
93
+ throw new utils.DynamicError('No active wallet address found', 'no_active_wallet_address_found');
93
94
  }
94
95
  return activeAccount.address;
95
96
  });
@@ -179,11 +180,11 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
179
180
  var _a, _b, _c, _d;
180
181
  const walletClient = yield this.getWaasWalletClient();
181
182
  if (!this.activeAccountAddress) {
182
- throw new utils.DynamicError('Active account address is required');
183
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
183
184
  }
184
185
  const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
185
186
  if (!signedSessionId) {
186
- throw new utils.DynamicError('Signed session ID is required');
187
+ throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
187
188
  }
188
189
  const mfaToken = yield ((_b = this.getMfaToken) === null || _b === void 0 ? void 0 : _b.call(this, {
189
190
  mfaAction: sdkApiCore.MFAAction.WalletWaasSign,
@@ -217,7 +218,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
217
218
  sendRawTransaction(rawTransaction) {
218
219
  return _tslib.__awaiter(this, void 0, void 0, function* () {
219
220
  if (!this.activeAccountAddress) {
220
- throw new utils.DynamicError('Active account address is required');
221
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
221
222
  }
222
223
  return this.mempoolApiService.sendRawTransaction(this.activeAccountAddress, rawTransaction);
223
224
  });
@@ -232,7 +233,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
232
233
  sendBitcoin(transaction) {
233
234
  return _tslib.__awaiter(this, void 0, void 0, function* () {
234
235
  if (!this.activeAccountAddress) {
235
- throw new utils.DynamicError('Active account address is required');
236
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
236
237
  }
237
238
  // Step 1: Build the PSBT
238
239
  const unsignedPsbt = yield this.buildPsbt(transaction);
@@ -241,7 +242,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
241
242
  unsignedPsbtBase64: unsignedPsbt,
242
243
  });
243
244
  if (!signedPsbtResponse) {
244
- throw new utils.DynamicError('Failed to sign PSBT');
245
+ throw new utils.DynamicError('Failed to sign PSBT', 'sign_psbt_failed');
245
246
  }
246
247
  // Step 3: Finalize the signed PSBT and extract the raw transaction
247
248
  const network = this.activeAccountAddress.startsWith('t')
@@ -296,11 +297,11 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
296
297
  });
297
298
  return _tslib.__awaiter(this, arguments, void 0, function* ({ privateKey, thresholdSignatureScheme = 'TWO_OF_TWO', publicAddressCheck, addressType, legacyWalletId, }) {
298
299
  if (!addressType) {
299
- throw new utils.DynamicError('addressType is required for BTC importPrivateKey');
300
+ throw new utils.DynamicError('addressType is required for BTC importPrivateKey', 'addresstype_is_required_for_btc_importprivatekey');
300
301
  }
301
302
  const validAddressTypes = ['native_segwit', 'taproot'];
302
303
  if (!validAddressTypes.includes(addressType)) {
303
- throw new utils.DynamicError(`Invalid addressType: ${addressType}. Must be one of: ${validAddressTypes.join(', ')}`);
304
+ throw new utils.DynamicError(`Invalid addressType: ${addressType}. Must be one of: ${validAddressTypes.join(', ')}`, 'invalid_addresstype_must_be_one_of');
304
305
  }
305
306
  return _super.importPrivateKey.call(this, {
306
307
  addressType,
@@ -346,7 +347,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
346
347
  signedSessionId: '',
347
348
  });
348
349
  if (!targetWallet) {
349
- throw new utils.DynamicError('Account not found');
350
+ throw new utils.DynamicError('Account not found', 'account_not_found');
350
351
  }
351
352
  const isWalletActive = walletConnectorCore.isSameAddress(targetWallet.accountAddress, this.activeAccountAddress || '', this.connectedChain);
352
353
  if (!isWalletActive) {
@@ -367,11 +368,11 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
367
368
  var _b, _c, _d, _e;
368
369
  const walletClient = yield this.getWaasWalletClient();
369
370
  if (!this.activeAccountAddress) {
370
- throw new utils.DynamicError('Active account address is required');
371
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
371
372
  }
372
373
  const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
373
374
  if (!signedSessionId) {
374
- throw new utils.DynamicError('Signed session ID is required');
375
+ throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
375
376
  }
376
377
  const mfaToken = yield ((_c = this.getMfaToken) === null || _c === void 0 ? void 0 : _c.call(this, {
377
378
  mfaAction: sdkApiCore.MFAAction.WalletWaasSign,
@@ -404,7 +405,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
404
405
  buildPsbt(transaction) {
405
406
  return _tslib.__awaiter(this, void 0, void 0, function* () {
406
407
  if (!this.activeAccountAddress) {
407
- throw new utils.DynamicError('Active account address is required');
408
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
408
409
  }
409
410
  const publicKeyHex = yield this.getPublicKey();
410
411
  const buildOptions = PsbtBuilderService.PsbtBuilderService.createBuildOptions(this.activeAccountAddress, transaction, publicKeyHex, transaction.feePriority || 'medium');
@@ -436,7 +437,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
436
437
  }
437
438
  if (!this.activeAccountAddress) {
438
439
  logger.error('[DynamicWaasBitcoinConnector] Active account address is required but not set');
439
- throw new utils.DynamicError('Active account address is required');
440
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
440
441
  }
441
442
  logger.debug('[DynamicWaasBitcoinConnector] Creating BitcoinUiTransaction', {
442
443
  from: this.activeAccountAddress,
@@ -456,7 +457,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
456
457
  hasAmount: Boolean(transaction.amount),
457
458
  hasRecipientAddress: Boolean(transaction.recipientAddress),
458
459
  });
459
- throw new utils.DynamicError('Recipient address and amount are required');
460
+ throw new utils.DynamicError('Recipient address and amount are required', 'recipient_address_and_amount_are_required');
460
461
  }
461
462
  try {
462
463
  logger.debug('[DynamicWaasBitcoinConnector] Calling sendBitcoin', {
@@ -474,7 +475,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
474
475
  });
475
476
  if (!txHash) {
476
477
  logger.error('[DynamicWaasBitcoinConnector] sendBitcoin returned undefined');
477
- throw new utils.DynamicError('Failed to send Bitcoin transaction');
478
+ throw new utils.DynamicError('Failed to send Bitcoin transaction', 'send_bitcoin_transaction');
478
479
  }
479
480
  logger.info('[DynamicWaasBitcoinConnector] Transaction submitted successfully', { txHash });
480
481
  return txHash;
@@ -529,11 +530,11 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
529
530
  return _tslib.__awaiter(this, void 0, void 0, function* () {
530
531
  var _a;
531
532
  if (!this.activeAccountAddress) {
532
- throw new utils.DynamicError('No active account address');
533
+ throw new utils.DynamicError('No active account address', 'no_active_account_address');
533
534
  }
534
535
  const allCredentials = (_a = this.verifiedCredentials) !== null && _a !== void 0 ? _a : [];
535
536
  if (allCredentials.length === 0) {
536
- throw new utils.DynamicError(`No verified credentials found. The address ${this.activeAccountAddress} may not be properly registered.`);
537
+ throw new utils.DynamicError(`No verified credentials found. The address ${this.activeAccountAddress} may not be properly registered.`, 'no_verified_credentials_found_the_address');
537
538
  }
538
539
  for (const credential of allCredentials) {
539
540
  const { walletAdditionalAddresses } = credential !== null && credential !== void 0 ? credential : {};
@@ -549,7 +550,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
549
550
  .flatMap((cred) => { var _a; return (_a = cred.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []; })
550
551
  .map((addr) => addr.address)
551
552
  .join(', ');
552
- throw new utils.DynamicError(`No additional address found for ${this.activeAccountAddress}. Available addresses: ${allAvailableAddresses || 'none'}`);
553
+ throw new utils.DynamicError(`No additional address found for ${this.activeAccountAddress}. Available addresses: ${allAvailableAddresses || 'none'}`, 'no_additional_address_found_for_available');
553
554
  });
554
555
  }
555
556
  /**
@@ -580,11 +581,11 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
580
581
  var _a, _b, _c, _d;
581
582
  const walletClient = yield this.getWaasWalletClient();
582
583
  if (!this.activeAccountAddress) {
583
- throw new utils.DynamicError('Active account address is required');
584
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
584
585
  }
585
586
  const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
586
587
  if (!signedSessionId) {
587
- throw new utils.DynamicError('Signed session ID is required');
588
+ throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
588
589
  }
589
590
  const mfaToken = yield ((_b = this.getMfaToken) === null || _b === void 0 ? void 0 : _b.call(this, {
590
591
  mfaAction: sdkApiCore.MFAAction.WalletWaasSign,
@@ -120,11 +120,7 @@ declare const DynamicWaasBitcoinConnector_base: (abstract new (...args: any[]) =
120
120
  hideICloudSignIn(): Promise<void>;
121
121
  isICloudAuthenticated(): Promise<boolean>;
122
122
  refreshWalletAccountShares({ accountAddress, password, }: {
123
- accountAddress: string; /**
124
- * Gets block explorer URLs for the current network
125
- * Returns mempool.space for Bitcoin transactions
126
- * @returns Array containing mempool.space URL
127
- */
123
+ accountAddress: string;
128
124
  password?: string | undefined;
129
125
  }): Promise<void>;
130
126
  reshareWalletAccountShares({ accountAddress, thresholdSignatureScheme, password, }: {
@@ -10,6 +10,7 @@ import { MempoolApiService } from '../../services/MempoolApiService.js';
10
10
  import { PsbtBuilderService } from '../../services/PsbtBuilderService.js';
11
11
  import 'sats-connect';
12
12
  import { BitcoinWalletConnector } from '../BitcoinWalletConnector.js';
13
+ import '@wallet-standard/app';
13
14
  import 'jsontokens';
14
15
  import { btcToSatoshis } from '../../utils/btcToSatoshis/btcToSatoshis.js';
15
16
  import { PsbtParser } from '../../utils/psbtParser/PsbtParser.js';
@@ -85,7 +86,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
85
86
  this.setActiveAccount(address);
86
87
  return address;
87
88
  }
88
- throw new DynamicError('No active wallet address found');
89
+ throw new DynamicError('No active wallet address found', 'no_active_wallet_address_found');
89
90
  }
90
91
  return activeAccount.address;
91
92
  });
@@ -175,11 +176,11 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
175
176
  var _a, _b, _c, _d;
176
177
  const walletClient = yield this.getWaasWalletClient();
177
178
  if (!this.activeAccountAddress) {
178
- throw new DynamicError('Active account address is required');
179
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
179
180
  }
180
181
  const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
181
182
  if (!signedSessionId) {
182
- throw new DynamicError('Signed session ID is required');
183
+ throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
183
184
  }
184
185
  const mfaToken = yield ((_b = this.getMfaToken) === null || _b === void 0 ? void 0 : _b.call(this, {
185
186
  mfaAction: MFAAction.WalletWaasSign,
@@ -213,7 +214,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
213
214
  sendRawTransaction(rawTransaction) {
214
215
  return __awaiter(this, void 0, void 0, function* () {
215
216
  if (!this.activeAccountAddress) {
216
- throw new DynamicError('Active account address is required');
217
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
217
218
  }
218
219
  return this.mempoolApiService.sendRawTransaction(this.activeAccountAddress, rawTransaction);
219
220
  });
@@ -228,7 +229,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
228
229
  sendBitcoin(transaction) {
229
230
  return __awaiter(this, void 0, void 0, function* () {
230
231
  if (!this.activeAccountAddress) {
231
- throw new DynamicError('Active account address is required');
232
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
232
233
  }
233
234
  // Step 1: Build the PSBT
234
235
  const unsignedPsbt = yield this.buildPsbt(transaction);
@@ -237,7 +238,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
237
238
  unsignedPsbtBase64: unsignedPsbt,
238
239
  });
239
240
  if (!signedPsbtResponse) {
240
- throw new DynamicError('Failed to sign PSBT');
241
+ throw new DynamicError('Failed to sign PSBT', 'sign_psbt_failed');
241
242
  }
242
243
  // Step 3: Finalize the signed PSBT and extract the raw transaction
243
244
  const network = this.activeAccountAddress.startsWith('t')
@@ -292,11 +293,11 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
292
293
  });
293
294
  return __awaiter(this, arguments, void 0, function* ({ privateKey, thresholdSignatureScheme = 'TWO_OF_TWO', publicAddressCheck, addressType, legacyWalletId, }) {
294
295
  if (!addressType) {
295
- throw new DynamicError('addressType is required for BTC importPrivateKey');
296
+ throw new DynamicError('addressType is required for BTC importPrivateKey', 'addresstype_is_required_for_btc_importprivatekey');
296
297
  }
297
298
  const validAddressTypes = ['native_segwit', 'taproot'];
298
299
  if (!validAddressTypes.includes(addressType)) {
299
- throw new DynamicError(`Invalid addressType: ${addressType}. Must be one of: ${validAddressTypes.join(', ')}`);
300
+ throw new DynamicError(`Invalid addressType: ${addressType}. Must be one of: ${validAddressTypes.join(', ')}`, 'invalid_addresstype_must_be_one_of');
300
301
  }
301
302
  return _super.importPrivateKey.call(this, {
302
303
  addressType,
@@ -342,7 +343,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
342
343
  signedSessionId: '',
343
344
  });
344
345
  if (!targetWallet) {
345
- throw new DynamicError('Account not found');
346
+ throw new DynamicError('Account not found', 'account_not_found');
346
347
  }
347
348
  const isWalletActive = isSameAddress(targetWallet.accountAddress, this.activeAccountAddress || '', this.connectedChain);
348
349
  if (!isWalletActive) {
@@ -363,11 +364,11 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
363
364
  var _b, _c, _d, _e;
364
365
  const walletClient = yield this.getWaasWalletClient();
365
366
  if (!this.activeAccountAddress) {
366
- throw new DynamicError('Active account address is required');
367
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
367
368
  }
368
369
  const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
369
370
  if (!signedSessionId) {
370
- throw new DynamicError('Signed session ID is required');
371
+ throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
371
372
  }
372
373
  const mfaToken = yield ((_c = this.getMfaToken) === null || _c === void 0 ? void 0 : _c.call(this, {
373
374
  mfaAction: MFAAction.WalletWaasSign,
@@ -400,7 +401,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
400
401
  buildPsbt(transaction) {
401
402
  return __awaiter(this, void 0, void 0, function* () {
402
403
  if (!this.activeAccountAddress) {
403
- throw new DynamicError('Active account address is required');
404
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
404
405
  }
405
406
  const publicKeyHex = yield this.getPublicKey();
406
407
  const buildOptions = PsbtBuilderService.createBuildOptions(this.activeAccountAddress, transaction, publicKeyHex, transaction.feePriority || 'medium');
@@ -432,7 +433,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
432
433
  }
433
434
  if (!this.activeAccountAddress) {
434
435
  logger.error('[DynamicWaasBitcoinConnector] Active account address is required but not set');
435
- throw new DynamicError('Active account address is required');
436
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
436
437
  }
437
438
  logger.debug('[DynamicWaasBitcoinConnector] Creating BitcoinUiTransaction', {
438
439
  from: this.activeAccountAddress,
@@ -452,7 +453,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
452
453
  hasAmount: Boolean(transaction.amount),
453
454
  hasRecipientAddress: Boolean(transaction.recipientAddress),
454
455
  });
455
- throw new DynamicError('Recipient address and amount are required');
456
+ throw new DynamicError('Recipient address and amount are required', 'recipient_address_and_amount_are_required');
456
457
  }
457
458
  try {
458
459
  logger.debug('[DynamicWaasBitcoinConnector] Calling sendBitcoin', {
@@ -470,7 +471,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
470
471
  });
471
472
  if (!txHash) {
472
473
  logger.error('[DynamicWaasBitcoinConnector] sendBitcoin returned undefined');
473
- throw new DynamicError('Failed to send Bitcoin transaction');
474
+ throw new DynamicError('Failed to send Bitcoin transaction', 'send_bitcoin_transaction');
474
475
  }
475
476
  logger.info('[DynamicWaasBitcoinConnector] Transaction submitted successfully', { txHash });
476
477
  return txHash;
@@ -525,11 +526,11 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
525
526
  return __awaiter(this, void 0, void 0, function* () {
526
527
  var _a;
527
528
  if (!this.activeAccountAddress) {
528
- throw new DynamicError('No active account address');
529
+ throw new DynamicError('No active account address', 'no_active_account_address');
529
530
  }
530
531
  const allCredentials = (_a = this.verifiedCredentials) !== null && _a !== void 0 ? _a : [];
531
532
  if (allCredentials.length === 0) {
532
- throw new DynamicError(`No verified credentials found. The address ${this.activeAccountAddress} may not be properly registered.`);
533
+ throw new DynamicError(`No verified credentials found. The address ${this.activeAccountAddress} may not be properly registered.`, 'no_verified_credentials_found_the_address');
533
534
  }
534
535
  for (const credential of allCredentials) {
535
536
  const { walletAdditionalAddresses } = credential !== null && credential !== void 0 ? credential : {};
@@ -545,7 +546,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
545
546
  .flatMap((cred) => { var _a; return (_a = cred.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []; })
546
547
  .map((addr) => addr.address)
547
548
  .join(', ');
548
- throw new DynamicError(`No additional address found for ${this.activeAccountAddress}. Available addresses: ${allAvailableAddresses || 'none'}`);
549
+ throw new DynamicError(`No additional address found for ${this.activeAccountAddress}. Available addresses: ${allAvailableAddresses || 'none'}`, 'no_additional_address_found_for_available');
549
550
  });
550
551
  }
551
552
  /**
@@ -576,11 +577,11 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
576
577
  var _a, _b, _c, _d;
577
578
  const walletClient = yield this.getWaasWalletClient();
578
579
  if (!this.activeAccountAddress) {
579
- throw new DynamicError('Active account address is required');
580
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
580
581
  }
581
582
  const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
582
583
  if (!signedSessionId) {
583
- throw new DynamicError('Signed session ID is required');
584
+ throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
584
585
  }
585
586
  const mfaToken = yield ((_b = this.getMfaToken) === null || _b === void 0 ? void 0 : _b.call(this, {
586
587
  mfaAction: MFAAction.WalletWaasSign,
@@ -174,7 +174,7 @@ class MagicEdenConnector extends BitcoinSatsConnectConnector.BitcoinSatsConnectC
174
174
  // to ensure we use it rather than the general Sats Connect API for this method
175
175
  const provider = this.getProvider();
176
176
  if (!provider)
177
- throw new utils.DynamicError('signTransactions failed: Magic Eden provider not found');
177
+ throw new utils.DynamicError('signTransactions failed: Magic Eden provider not found', 'signtransactions_failed_magic_eden_provider_not');
178
178
  return provider.signMultipleTransactions(request);
179
179
  });
180
180
  }
@@ -170,7 +170,7 @@ class MagicEdenConnector extends BitcoinSatsConnectConnector {
170
170
  // to ensure we use it rather than the general Sats Connect API for this method
171
171
  const provider = this.getProvider();
172
172
  if (!provider)
173
- throw new DynamicError('signTransactions failed: Magic Eden provider not found');
173
+ throw new DynamicError('signTransactions failed: Magic Eden provider not found', 'signtransactions_failed_magic_eden_provider_not');
174
174
  return provider.signMultipleTransactions(request);
175
175
  });
176
176
  }
package/src/index.cjs CHANGED
@@ -58,6 +58,7 @@ exports.UnisatConnector = UnisatConnector.UnisatConnector;
58
58
  exports.DynamicWaasBitcoinConnector = DynamicWaasBitcoinConnector.DynamicWaasBitcoinConnector;
59
59
  exports.fetchBtcKitConnectors = fetchBtcKitConnectors.fetchBtcKitConnectors;
60
60
  exports.fetchSatsConnectConnectors = fetchSatsConnectConnectors.fetchSatsConnectConnectors;
61
+ exports.stopBitcoinSatsConnectDiscovery = fetchSatsConnectConnectors.stopBitcoinSatsConnectDiscovery;
61
62
  exports.hasSatsConnectFeature = hasSatsConnectFeature.hasSatsConnectFeature;
62
63
  exports.getMempoolApiUrl = getMempoolApiUrl.getMempoolApiUrl;
63
64
  exports.createPsbtOptions = createSignPsbtOptions.createPsbtOptions;
package/src/index.js CHANGED
@@ -23,7 +23,7 @@ import { BitgetConnector } from './connectors/BitgetConnector/BitgetConnector.js
23
23
  import { fetchBtcKitConnectors } from './utils/fetchBtcKitConnectors/fetchBtcKitConnectors.js';
24
24
  export { fetchBtcKitConnectors } from './utils/fetchBtcKitConnectors/fetchBtcKitConnectors.js';
25
25
  import { fetchSatsConnectConnectors } from './utils/fetchSatsConnectConnectors/fetchSatsConnectConnectors.js';
26
- export { fetchSatsConnectConnectors } from './utils/fetchSatsConnectConnectors/fetchSatsConnectConnectors.js';
26
+ export { fetchSatsConnectConnectors, stopBitcoinSatsConnectDiscovery } from './utils/fetchSatsConnectConnectors/fetchSatsConnectConnectors.js';
27
27
  export { hasSatsConnectFeature } from './utils/hasSatsConnectFeature.js';
28
28
  export { getMempoolApiUrl } from './utils/getMempoolApiUrl.js';
29
29
  export { createPsbtOptions } from './utils/psbt/createSignPsbtOptions.js';
@@ -42,13 +42,13 @@ class MempoolApiService {
42
42
  const baseUrl = this.getBaseUrl(address);
43
43
  const response = yield fetch(`${baseUrl}/address/${address}/utxo`);
44
44
  if (!response.ok) {
45
- throw new utils.DynamicError(`Failed to fetch UTXOs: ${response.statusText}`);
45
+ throw new utils.DynamicError(`Failed to fetch UTXOs: ${response.statusText}`, 'fetch_utxos_failed');
46
46
  }
47
47
  return response.json();
48
48
  }
49
49
  catch (error) {
50
50
  logger.error('Error fetching UTXOs:', error);
51
- throw new utils.DynamicError('Failed to fetch UTXOs');
51
+ throw new utils.DynamicError('Failed to fetch UTXOs', 'fetch_utxos_failed');
52
52
  }
53
53
  });
54
54
  }
@@ -127,7 +127,7 @@ class MempoolApiService {
127
127
  sendRawTransaction(address, rawTransaction) {
128
128
  return _tslib.__awaiter(this, void 0, void 0, function* () {
129
129
  if (!rawTransaction) {
130
- throw new utils.DynamicError('No transaction specified!');
130
+ throw new utils.DynamicError('No transaction specified!', 'no_transaction_specified');
131
131
  }
132
132
  const baseUrl = this.getBaseUrl(address);
133
133
  const response = yield fetch(`${baseUrl}/tx`, {
@@ -140,7 +140,7 @@ class MempoolApiService {
140
140
  if (!response.ok) {
141
141
  const error = yield response.text();
142
142
  logger.debug(`sendRawTransaction - response not ok: ${JSON.stringify(error)}`);
143
- throw new utils.DynamicError('sendRawTransaction - failed to send transaction');
143
+ throw new utils.DynamicError('sendRawTransaction - failed to send transaction', 'sendrawtransaction_failed_to_send_transaction');
144
144
  }
145
145
  return response.text();
146
146
  });
@@ -38,13 +38,13 @@ class MempoolApiService {
38
38
  const baseUrl = this.getBaseUrl(address);
39
39
  const response = yield fetch(`${baseUrl}/address/${address}/utxo`);
40
40
  if (!response.ok) {
41
- throw new DynamicError(`Failed to fetch UTXOs: ${response.statusText}`);
41
+ throw new DynamicError(`Failed to fetch UTXOs: ${response.statusText}`, 'fetch_utxos_failed');
42
42
  }
43
43
  return response.json();
44
44
  }
45
45
  catch (error) {
46
46
  logger.error('Error fetching UTXOs:', error);
47
- throw new DynamicError('Failed to fetch UTXOs');
47
+ throw new DynamicError('Failed to fetch UTXOs', 'fetch_utxos_failed');
48
48
  }
49
49
  });
50
50
  }
@@ -123,7 +123,7 @@ class MempoolApiService {
123
123
  sendRawTransaction(address, rawTransaction) {
124
124
  return __awaiter(this, void 0, void 0, function* () {
125
125
  if (!rawTransaction) {
126
- throw new DynamicError('No transaction specified!');
126
+ throw new DynamicError('No transaction specified!', 'no_transaction_specified');
127
127
  }
128
128
  const baseUrl = this.getBaseUrl(address);
129
129
  const response = yield fetch(`${baseUrl}/tx`, {
@@ -136,7 +136,7 @@ class MempoolApiService {
136
136
  if (!response.ok) {
137
137
  const error = yield response.text();
138
138
  logger.debug(`sendRawTransaction - response not ok: ${JSON.stringify(error)}`);
139
- throw new DynamicError('sendRawTransaction - failed to send transaction');
139
+ throw new DynamicError('sendRawTransaction - failed to send transaction', 'sendrawtransaction_failed_to_send_transaction');
140
140
  }
141
141
  return response.text();
142
142
  });
@@ -36,7 +36,7 @@ class PsbtBuilderService {
36
36
  if (accountAddress.toLowerCase().startsWith('bc1p') ||
37
37
  accountAddress.toLowerCase().startsWith('tb1p')) {
38
38
  logger.warn(`Account address ${accountAddress} appears to be Taproot. Only Native SegWit (P2WPKH) is supported.`);
39
- throw new utils.DynamicError('Taproot addresses are not supported. Only Native SegWit (P2WPKH) addresses are allowed.');
39
+ throw new utils.DynamicError('Taproot addresses are not supported. Only Native SegWit (P2WPKH) addresses are allowed.', 'taproot_addresses_are_not_supported_only');
40
40
  }
41
41
  return utxos;
42
42
  }
@@ -90,11 +90,11 @@ class PsbtBuilderService {
90
90
  if (selectedUTXOs.length < allUTXOs.length) {
91
91
  const allTotal = this.calculateUTXOTotal(allUTXOs);
92
92
  if (allTotal < requiredAmount) {
93
- throw new utils.DynamicError(`Insufficient funds. Available: ${allTotal / _const.SATOSHIS_PER_BTC} BTC (${allTotal} satoshis), Required: ${amountInSatoshisNumber / _const.SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`);
93
+ throw new utils.DynamicError(`Insufficient funds. Available: ${allTotal / _const.SATOSHIS_PER_BTC} BTC (${allTotal} satoshis), Required: ${amountInSatoshisNumber / _const.SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`, 'insufficient_funds_available_btc_satoshis_required');
94
94
  }
95
95
  return allUTXOs;
96
96
  }
97
- throw new utils.DynamicError(`Insufficient funds. Available: ${selectedTotal / _const.SATOSHIS_PER_BTC} BTC (${selectedTotal} satoshis), Required: ${amountInSatoshisNumber / _const.SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`);
97
+ throw new utils.DynamicError(`Insufficient funds. Available: ${selectedTotal / _const.SATOSHIS_PER_BTC} BTC (${selectedTotal} satoshis), Required: ${amountInSatoshisNumber / _const.SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`, 'insufficient_funds_available_btc_satoshis_required');
98
98
  }
99
99
  /**
100
100
  * Calculates fee estimate and change amount, handling dust limit
@@ -148,7 +148,7 @@ class PsbtBuilderService {
148
148
  pubkey: publicKeyPair.publicKey,
149
149
  }).output;
150
150
  if (!outputScript) {
151
- throw new utils.DynamicError('Failed to create segwit output script');
151
+ throw new utils.DynamicError('Failed to create segwit output script', 'create_segwit_output_script');
152
152
  }
153
153
  // Convert txid from hex string to Buffer and reverse it (Bitcoin uses little-endian)
154
154
  // The txid from the API is in big-endian format, but bitcoinjs-lib expects little-endian
@@ -176,7 +176,7 @@ class PsbtBuilderService {
176
176
  */
177
177
  addOutputsToPsbt(psbt, recipientAddress, accountAddress, amountInSatoshisNumber, changeAmountNumber, hasChangeOutput, network) {
178
178
  if (amountInSatoshisNumber < _const.DUST_LIMIT) {
179
- throw new utils.DynamicError(`Amount is below dust limit of ${_const.DUST_LIMIT} satoshis (${_const.DUST_LIMIT / _const.SATOSHIS_PER_BTC} BTC)`);
179
+ throw new utils.DynamicError(`Amount is below dust limit of ${_const.DUST_LIMIT} satoshis (${_const.DUST_LIMIT / _const.SATOSHIS_PER_BTC} BTC)`, 'amount_is_below_dust_limit_of');
180
180
  }
181
181
  psbt.addOutput({
182
182
  script: bitcoinjsLib.address.toOutputScript(recipientAddress, network),
@@ -201,11 +201,11 @@ class PsbtBuilderService {
201
201
  const { accountAddress, recipientAddress, amountInSatoshis, publicKeyHex, network, feePriority = 'medium', } = options;
202
202
  logger.debug(`buildPsbt called with feePriority: ${feePriority}, amount: ${amountInSatoshis} satoshis`);
203
203
  if (amountInSatoshis <= BigInt(0)) {
204
- throw new utils.DynamicError('Amount must be greater than 0');
204
+ throw new utils.DynamicError('Amount must be greater than 0', 'amount_must_be_greater_than_0');
205
205
  }
206
206
  const allUTXOs = yield this.mempoolApiService.getUTXOs(accountAddress);
207
207
  if (allUTXOs.length === 0) {
208
- throw new utils.DynamicError('No UTXOs found for this address');
208
+ throw new utils.DynamicError('No UTXOs found for this address', 'no_utxos_found_for_this_address');
209
209
  }
210
210
  const utxos = this.filterTaprootUTXOs(accountAddress, allUTXOs);
211
211
  const publicKeyBuffer = Buffer.from(publicKeyHex, 'hex');
@@ -229,7 +229,7 @@ class PsbtBuilderService {
229
229
  // Final check: ensure we have enough after final fee calculation
230
230
  const maxToSpend = selectedTotalValue - feeEstimate;
231
231
  if (maxToSpend < amountInSatoshisNumber) {
232
- throw new utils.DynamicError(`Insufficient funds. Available: ${maxToSpend / _const.SATOSHIS_PER_BTC} BTC (${maxToSpend} satoshis), Required: ${amountInSatoshisNumber / _const.SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis)`);
232
+ throw new utils.DynamicError(`Insufficient funds. Available: ${maxToSpend / _const.SATOSHIS_PER_BTC} BTC (${maxToSpend} satoshis), Required: ${amountInSatoshisNumber / _const.SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis)`, 'insufficient_funds_available_btc_satoshis_required');
233
233
  }
234
234
  // Create PSBT and add inputs/outputs
235
235
  const psbt = new bitcoinjsLib.Psbt({ network });
@@ -28,7 +28,7 @@ class PsbtBuilderService {
28
28
  if (accountAddress.toLowerCase().startsWith('bc1p') ||
29
29
  accountAddress.toLowerCase().startsWith('tb1p')) {
30
30
  logger.warn(`Account address ${accountAddress} appears to be Taproot. Only Native SegWit (P2WPKH) is supported.`);
31
- throw new DynamicError('Taproot addresses are not supported. Only Native SegWit (P2WPKH) addresses are allowed.');
31
+ throw new DynamicError('Taproot addresses are not supported. Only Native SegWit (P2WPKH) addresses are allowed.', 'taproot_addresses_are_not_supported_only');
32
32
  }
33
33
  return utxos;
34
34
  }
@@ -82,11 +82,11 @@ class PsbtBuilderService {
82
82
  if (selectedUTXOs.length < allUTXOs.length) {
83
83
  const allTotal = this.calculateUTXOTotal(allUTXOs);
84
84
  if (allTotal < requiredAmount) {
85
- throw new DynamicError(`Insufficient funds. Available: ${allTotal / SATOSHIS_PER_BTC} BTC (${allTotal} satoshis), Required: ${amountInSatoshisNumber / SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`);
85
+ throw new DynamicError(`Insufficient funds. Available: ${allTotal / SATOSHIS_PER_BTC} BTC (${allTotal} satoshis), Required: ${amountInSatoshisNumber / SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`, 'insufficient_funds_available_btc_satoshis_required');
86
86
  }
87
87
  return allUTXOs;
88
88
  }
89
- throw new DynamicError(`Insufficient funds. Available: ${selectedTotal / SATOSHIS_PER_BTC} BTC (${selectedTotal} satoshis), Required: ${amountInSatoshisNumber / SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`);
89
+ throw new DynamicError(`Insufficient funds. Available: ${selectedTotal / SATOSHIS_PER_BTC} BTC (${selectedTotal} satoshis), Required: ${amountInSatoshisNumber / SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis) + fees`, 'insufficient_funds_available_btc_satoshis_required');
90
90
  }
91
91
  /**
92
92
  * Calculates fee estimate and change amount, handling dust limit
@@ -140,7 +140,7 @@ class PsbtBuilderService {
140
140
  pubkey: publicKeyPair.publicKey,
141
141
  }).output;
142
142
  if (!outputScript) {
143
- throw new DynamicError('Failed to create segwit output script');
143
+ throw new DynamicError('Failed to create segwit output script', 'create_segwit_output_script');
144
144
  }
145
145
  // Convert txid from hex string to Buffer and reverse it (Bitcoin uses little-endian)
146
146
  // The txid from the API is in big-endian format, but bitcoinjs-lib expects little-endian
@@ -168,7 +168,7 @@ class PsbtBuilderService {
168
168
  */
169
169
  addOutputsToPsbt(psbt, recipientAddress, accountAddress, amountInSatoshisNumber, changeAmountNumber, hasChangeOutput, network) {
170
170
  if (amountInSatoshisNumber < DUST_LIMIT) {
171
- throw new DynamicError(`Amount is below dust limit of ${DUST_LIMIT} satoshis (${DUST_LIMIT / SATOSHIS_PER_BTC} BTC)`);
171
+ throw new DynamicError(`Amount is below dust limit of ${DUST_LIMIT} satoshis (${DUST_LIMIT / SATOSHIS_PER_BTC} BTC)`, 'amount_is_below_dust_limit_of');
172
172
  }
173
173
  psbt.addOutput({
174
174
  script: address.toOutputScript(recipientAddress, network),
@@ -193,11 +193,11 @@ class PsbtBuilderService {
193
193
  const { accountAddress, recipientAddress, amountInSatoshis, publicKeyHex, network, feePriority = 'medium', } = options;
194
194
  logger.debug(`buildPsbt called with feePriority: ${feePriority}, amount: ${amountInSatoshis} satoshis`);
195
195
  if (amountInSatoshis <= BigInt(0)) {
196
- throw new DynamicError('Amount must be greater than 0');
196
+ throw new DynamicError('Amount must be greater than 0', 'amount_must_be_greater_than_0');
197
197
  }
198
198
  const allUTXOs = yield this.mempoolApiService.getUTXOs(accountAddress);
199
199
  if (allUTXOs.length === 0) {
200
- throw new DynamicError('No UTXOs found for this address');
200
+ throw new DynamicError('No UTXOs found for this address', 'no_utxos_found_for_this_address');
201
201
  }
202
202
  const utxos = this.filterTaprootUTXOs(accountAddress, allUTXOs);
203
203
  const publicKeyBuffer = Buffer.from(publicKeyHex, 'hex');
@@ -221,7 +221,7 @@ class PsbtBuilderService {
221
221
  // Final check: ensure we have enough after final fee calculation
222
222
  const maxToSpend = selectedTotalValue - feeEstimate;
223
223
  if (maxToSpend < amountInSatoshisNumber) {
224
- throw new DynamicError(`Insufficient funds. Available: ${maxToSpend / SATOSHIS_PER_BTC} BTC (${maxToSpend} satoshis), Required: ${amountInSatoshisNumber / SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis)`);
224
+ throw new DynamicError(`Insufficient funds. Available: ${maxToSpend / SATOSHIS_PER_BTC} BTC (${maxToSpend} satoshis), Required: ${amountInSatoshisNumber / SATOSHIS_PER_BTC} BTC (${amountInSatoshisNumber} satoshis)`, 'insufficient_funds_available_btc_satoshis_required');
225
225
  }
226
226
  // Create PSBT and add inputs/outputs
227
227
  const psbt = new Psbt({ network });
@@ -14,7 +14,7 @@ var _const = require('../../const.cjs');
14
14
  */
15
15
  const btcToSatoshis = (btcAmount) => {
16
16
  if (btcAmount < 0) {
17
- throw new utils.DynamicError('BTC amount cannot be negative');
17
+ throw new utils.DynamicError('BTC amount cannot be negative', 'btc_amount_cannot_be_negative');
18
18
  }
19
19
  // Convert to satoshis and return as BigInt
20
20
  const satoshis = Math.floor(btcAmount * _const.SATOSHIS_PER_BTC);
@@ -10,7 +10,7 @@ import { SATOSHIS_PER_BTC } from '../../const.js';
10
10
  */
11
11
  const btcToSatoshis = (btcAmount) => {
12
12
  if (btcAmount < 0) {
13
- throw new DynamicError('BTC amount cannot be negative');
13
+ throw new DynamicError('BTC amount cannot be negative', 'btc_amount_cannot_be_negative');
14
14
  }
15
15
  // Convert to satoshis and return as BigInt
16
16
  const satoshis = Math.floor(btcAmount * SATOSHIS_PER_BTC);
@@ -3,22 +3,92 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
+ var app = require('@wallet-standard/app');
7
+ var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
6
8
  require('../../../_virtual/_tslib.cjs');
7
9
  require('sats-connect');
8
- require('@dynamic-labs/wallet-connector-core');
9
10
  require('@dynamic-labs/wallet-book');
10
11
  require('@dynamic-labs/utils');
11
12
  require('@dynamic-labs/sdk-api-core');
12
- require('@wallet-standard/app');
13
13
  var _const = require('../../const.cjs');
14
14
  require('bitcoinjs-lib');
15
15
  var BitcoinSatsConnectConnector = require('../../connectors/BitcoinSatsConnectConnector/BitcoinSatsConnectConnector.cjs');
16
16
  require('jsontokens');
17
17
  require('../../connectors/DynamicWaasBitcoinConnector/DynamicWaasBitcoinConnector.cjs');
18
+ var hasSatsConnectFeature = require('../hasSatsConnectFeature.cjs');
18
19
 
20
+ const BITCOIN_SATSCONNECT_POLL_INTERVAL_MS = 300;
21
+ const BITCOIN_SATSCONNECT_POLL_DURATION_MS = 5000;
22
+ let teardownBitcoinSatsConnectDiscovery = null;
23
+ /**
24
+ * Stops the active SatsConnect wallet-standard discovery (register listener + poll).
25
+ * @internal - Exposed for test cleanup.
26
+ */
27
+ const stopBitcoinSatsConnectDiscovery = () => {
28
+ teardownBitcoinSatsConnectDiscovery === null || teardownBitcoinSatsConnectDiscovery === void 0 ? void 0 : teardownBitcoinSatsConnectDiscovery();
29
+ teardownBitcoinSatsConnectDiscovery = null;
30
+ };
31
+ const findSatsConnectWalletBookEntry = (walletName, walletBook) => {
32
+ var _a;
33
+ return Object.entries((_a = walletBook === null || walletBook === void 0 ? void 0 : walletBook.wallets) !== null && _a !== void 0 ? _a : {}).find(([, walletData]) => {
34
+ var _a;
35
+ return (_a = walletData.injectedConfig) === null || _a === void 0 ? void 0 : _a.some((config) => {
36
+ var _a, _b;
37
+ return config.chain === 'btc' &&
38
+ ((_a = config.walletStandard) === null || _a === void 0 ? void 0 : _a.features.includes(_const.SATSCONNECT_FEATURE)) &&
39
+ ((_b = config.walletStandard) === null || _b === void 0 ? void 0 : _b.providerId) &&
40
+ walletData.name === walletName;
41
+ });
42
+ });
43
+ };
44
+ const setupBitcoinSatsConnectDiscovery = (walletBook) => {
45
+ if (typeof window === 'undefined')
46
+ return;
47
+ teardownBitcoinSatsConnectDiscovery === null || teardownBitcoinSatsConnectDiscovery === void 0 ? void 0 : teardownBitcoinSatsConnectDiscovery();
48
+ const discoveredWallets = new Set();
49
+ const { get, on } = app.getWallets();
50
+ const initialWallets = get();
51
+ // Seed already-present wallets so the listener and poll skip them.
52
+ initialWallets.forEach((wallet) => {
53
+ if (hasSatsConnectFeature.hasSatsConnectFeature(wallet.features) &&
54
+ findSatsConnectWalletBookEntry(wallet.name, walletBook)) {
55
+ discoveredWallets.add(wallet.name);
56
+ }
57
+ });
58
+ const handleWallet = (wallet) => {
59
+ if (discoveredWallets.has(wallet.name))
60
+ return;
61
+ if (!hasSatsConnectFeature.hasSatsConnectFeature(wallet.features))
62
+ return;
63
+ const entry = findSatsConnectWalletBookEntry(wallet.name, walletBook);
64
+ if (!entry)
65
+ return;
66
+ discoveredWallets.add(wallet.name);
67
+ const [key, walletData] = entry;
68
+ const connectorName = walletData.shortName || walletData.name;
69
+ const injectedConnectorConstructor = class extends BitcoinSatsConnectConnector.BitcoinSatsConnectConnector {
70
+ constructor(props) {
71
+ super(Object.assign(Object.assign({}, props), { overrideKey: key, walletData }));
72
+ this.name = connectorName;
73
+ }
74
+ };
75
+ walletConnectorCore.walletConnectorEvents.emit('providerInjected', {
76
+ injectedConnectorConstructor,
77
+ });
78
+ };
79
+ const removeRegisterListener = on('register', handleWallet);
80
+ const pollIntervalId = setInterval(() => get().forEach(handleWallet), BITCOIN_SATSCONNECT_POLL_INTERVAL_MS);
81
+ const pollTimeoutId = setTimeout(() => clearInterval(pollIntervalId), BITCOIN_SATSCONNECT_POLL_DURATION_MS);
82
+ teardownBitcoinSatsConnectDiscovery = () => {
83
+ removeRegisterListener === null || removeRegisterListener === void 0 ? void 0 : removeRegisterListener();
84
+ clearInterval(pollIntervalId);
85
+ clearTimeout(pollTimeoutId);
86
+ };
87
+ };
19
88
  const fetchSatsConnectConnectors = ({ walletBook, }) => {
20
89
  var _a;
21
- return Object.entries((_a = walletBook === null || walletBook === void 0 ? void 0 : walletBook.wallets) !== null && _a !== void 0 ? _a : {})
90
+ setupBitcoinSatsConnectDiscovery(walletBook);
91
+ return (Object.entries((_a = walletBook === null || walletBook === void 0 ? void 0 : walletBook.wallets) !== null && _a !== void 0 ? _a : {})
22
92
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
93
  .filter(([_, wallet]) => {
24
94
  var _a;
@@ -38,7 +108,8 @@ const fetchSatsConnectConnectors = ({ walletBook, }) => {
38
108
  this.name = name;
39
109
  }
40
110
  };
41
- });
111
+ }));
42
112
  };
43
113
 
44
114
  exports.fetchSatsConnectConnectors = fetchSatsConnectConnectors;
115
+ exports.stopBitcoinSatsConnectDiscovery = stopBitcoinSatsConnectDiscovery;
@@ -1,5 +1,10 @@
1
1
  import { WalletBookSchema } from '@dynamic-labs/wallet-book';
2
2
  import { WalletConnectorConstructor } from '@dynamic-labs/wallet-connector-core';
3
+ /**
4
+ * Stops the active SatsConnect wallet-standard discovery (register listener + poll).
5
+ * @internal - Exposed for test cleanup.
6
+ */
7
+ export declare const stopBitcoinSatsConnectDiscovery: () => void;
3
8
  export declare const fetchSatsConnectConnectors: ({ walletBook, }: {
4
9
  walletBook: WalletBookSchema;
5
10
  }) => WalletConnectorConstructor[];
@@ -1,20 +1,90 @@
1
1
  'use client'
2
+ import { getWallets } from '@wallet-standard/app';
3
+ import { walletConnectorEvents } from '@dynamic-labs/wallet-connector-core';
2
4
  import '../../../_virtual/_tslib.js';
3
5
  import 'sats-connect';
4
- import '@dynamic-labs/wallet-connector-core';
5
6
  import '@dynamic-labs/wallet-book';
6
7
  import '@dynamic-labs/utils';
7
8
  import '@dynamic-labs/sdk-api-core';
8
- import '@wallet-standard/app';
9
9
  import { SATSCONNECT_FEATURE } from '../../const.js';
10
10
  import 'bitcoinjs-lib';
11
11
  import { BitcoinSatsConnectConnector } from '../../connectors/BitcoinSatsConnectConnector/BitcoinSatsConnectConnector.js';
12
12
  import 'jsontokens';
13
13
  import '../../connectors/DynamicWaasBitcoinConnector/DynamicWaasBitcoinConnector.js';
14
+ import { hasSatsConnectFeature } from '../hasSatsConnectFeature.js';
14
15
 
16
+ const BITCOIN_SATSCONNECT_POLL_INTERVAL_MS = 300;
17
+ const BITCOIN_SATSCONNECT_POLL_DURATION_MS = 5000;
18
+ let teardownBitcoinSatsConnectDiscovery = null;
19
+ /**
20
+ * Stops the active SatsConnect wallet-standard discovery (register listener + poll).
21
+ * @internal - Exposed for test cleanup.
22
+ */
23
+ const stopBitcoinSatsConnectDiscovery = () => {
24
+ teardownBitcoinSatsConnectDiscovery === null || teardownBitcoinSatsConnectDiscovery === void 0 ? void 0 : teardownBitcoinSatsConnectDiscovery();
25
+ teardownBitcoinSatsConnectDiscovery = null;
26
+ };
27
+ const findSatsConnectWalletBookEntry = (walletName, walletBook) => {
28
+ var _a;
29
+ return Object.entries((_a = walletBook === null || walletBook === void 0 ? void 0 : walletBook.wallets) !== null && _a !== void 0 ? _a : {}).find(([, walletData]) => {
30
+ var _a;
31
+ return (_a = walletData.injectedConfig) === null || _a === void 0 ? void 0 : _a.some((config) => {
32
+ var _a, _b;
33
+ return config.chain === 'btc' &&
34
+ ((_a = config.walletStandard) === null || _a === void 0 ? void 0 : _a.features.includes(SATSCONNECT_FEATURE)) &&
35
+ ((_b = config.walletStandard) === null || _b === void 0 ? void 0 : _b.providerId) &&
36
+ walletData.name === walletName;
37
+ });
38
+ });
39
+ };
40
+ const setupBitcoinSatsConnectDiscovery = (walletBook) => {
41
+ if (typeof window === 'undefined')
42
+ return;
43
+ teardownBitcoinSatsConnectDiscovery === null || teardownBitcoinSatsConnectDiscovery === void 0 ? void 0 : teardownBitcoinSatsConnectDiscovery();
44
+ const discoveredWallets = new Set();
45
+ const { get, on } = getWallets();
46
+ const initialWallets = get();
47
+ // Seed already-present wallets so the listener and poll skip them.
48
+ initialWallets.forEach((wallet) => {
49
+ if (hasSatsConnectFeature(wallet.features) &&
50
+ findSatsConnectWalletBookEntry(wallet.name, walletBook)) {
51
+ discoveredWallets.add(wallet.name);
52
+ }
53
+ });
54
+ const handleWallet = (wallet) => {
55
+ if (discoveredWallets.has(wallet.name))
56
+ return;
57
+ if (!hasSatsConnectFeature(wallet.features))
58
+ return;
59
+ const entry = findSatsConnectWalletBookEntry(wallet.name, walletBook);
60
+ if (!entry)
61
+ return;
62
+ discoveredWallets.add(wallet.name);
63
+ const [key, walletData] = entry;
64
+ const connectorName = walletData.shortName || walletData.name;
65
+ const injectedConnectorConstructor = class extends BitcoinSatsConnectConnector {
66
+ constructor(props) {
67
+ super(Object.assign(Object.assign({}, props), { overrideKey: key, walletData }));
68
+ this.name = connectorName;
69
+ }
70
+ };
71
+ walletConnectorEvents.emit('providerInjected', {
72
+ injectedConnectorConstructor,
73
+ });
74
+ };
75
+ const removeRegisterListener = on('register', handleWallet);
76
+ const pollIntervalId = setInterval(() => get().forEach(handleWallet), BITCOIN_SATSCONNECT_POLL_INTERVAL_MS);
77
+ const pollTimeoutId = setTimeout(() => clearInterval(pollIntervalId), BITCOIN_SATSCONNECT_POLL_DURATION_MS);
78
+ teardownBitcoinSatsConnectDiscovery = () => {
79
+ removeRegisterListener === null || removeRegisterListener === void 0 ? void 0 : removeRegisterListener();
80
+ clearInterval(pollIntervalId);
81
+ clearTimeout(pollTimeoutId);
82
+ };
83
+ };
15
84
  const fetchSatsConnectConnectors = ({ walletBook, }) => {
16
85
  var _a;
17
- return Object.entries((_a = walletBook === null || walletBook === void 0 ? void 0 : walletBook.wallets) !== null && _a !== void 0 ? _a : {})
86
+ setupBitcoinSatsConnectDiscovery(walletBook);
87
+ return (Object.entries((_a = walletBook === null || walletBook === void 0 ? void 0 : walletBook.wallets) !== null && _a !== void 0 ? _a : {})
18
88
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
89
  .filter(([_, wallet]) => {
20
90
  var _a;
@@ -34,7 +104,7 @@ const fetchSatsConnectConnectors = ({ walletBook, }) => {
34
104
  this.name = name;
35
105
  }
36
106
  };
37
- });
107
+ }));
38
108
  };
39
109
 
40
- export { fetchSatsConnectConnectors };
110
+ export { fetchSatsConnectConnectors, stopBitcoinSatsConnectDiscovery };
@@ -11,9 +11,9 @@ const validateAddress = (psbt, signingAddress, index, inputAtIndex) => {
11
11
  // to be able to compare it to the address provided in the request for the current index
12
12
  const extractedAddress = extractAddressFromInput.extractAddressFromInput(psbt, index, inputAtIndex);
13
13
  if (!extractedAddress)
14
- throw new utils.DynamicError(`validateAddress - input not found for the given address: ${signingAddress}`);
14
+ throw new utils.DynamicError(`validateAddress - input not found for the given address: ${signingAddress}`, 'validateaddress_input_not_found_for_the');
15
15
  if (extractedAddress !== signingAddress)
16
- throw new utils.DynamicError('validateAddress - address mismatch with index');
16
+ throw new utils.DynamicError('validateAddress - address mismatch with index', 'validateaddress_address_mismatch_with_index');
17
17
  };
18
18
 
19
19
  exports.validateAddress = validateAddress;
@@ -7,9 +7,9 @@ const validateAddress = (psbt, signingAddress, index, inputAtIndex) => {
7
7
  // to be able to compare it to the address provided in the request for the current index
8
8
  const extractedAddress = extractAddressFromInput(psbt, index, inputAtIndex);
9
9
  if (!extractedAddress)
10
- throw new DynamicError(`validateAddress - input not found for the given address: ${signingAddress}`);
10
+ throw new DynamicError(`validateAddress - input not found for the given address: ${signingAddress}`, 'validateaddress_input_not_found_for_the');
11
11
  if (extractedAddress !== signingAddress)
12
- throw new DynamicError('validateAddress - address mismatch with index');
12
+ throw new DynamicError('validateAddress - address mismatch with index', 'validateaddress_address_mismatch_with_index');
13
13
  };
14
14
 
15
15
  export { validateAddress };