@dynamic-labs/bitcoin 5.0.0-rc.4 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,98 @@
1
1
 
2
+ ## [5.1.0](https://github.com/dynamic-labs/dynamic-auth/compare/v5.0.0...v5.1.0) (2026-08-04)
3
+
4
+
5
+ ### Features
6
+
7
+ * **midnight:** expose contract wallet provider ([#12120](https://github.com/dynamic-labs/dynamic-auth/issues/12120)) ([2cecf23](https://github.com/dynamic-labs/dynamic-auth/commit/2cecf23f1f51819b99dfde4afd20264ea1abebf8))
8
+ * **webview-messages,webview-controller:** add waas_backupKeySharesToICloud webview message ([#12147](https://github.com/dynamic-labs/dynamic-auth/issues/12147)) ([ca4edda](https://github.com/dynamic-labs/dynamic-auth/commit/ca4edda54adf0673a9fe41c17d861a739f1b8f2e))
9
+ * **webview:** add business accounts message contract and controller ([#12144](https://github.com/dynamic-labs/dynamic-auth/issues/12144)) ([d9f80c1](https://github.com/dynamic-labs/dynamic-auth/commit/d9f80c10bb3c332c2f3e3dc401b95025b930fa4e))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * bump brace-expansion to latest patched maintenance releases ([#12130](https://github.com/dynamic-labs/dynamic-auth/issues/12130)) ([1b26671](https://github.com/dynamic-labs/dynamic-auth/commit/1b26671ad8e62025904e04f9dfed8b3faafb9c19))
15
+ * **ethereum-aa:** resolve chainId from network for ZeroDev v2 multichain projects ([#12105](https://github.com/dynamic-labs/dynamic-auth/issues/12105)) ([fe2c5d2](https://github.com/dynamic-labs/dynamic-auth/commit/fe2c5d239758ea79e8647006d9d77cc27c2ab647))
16
+ * **midnight:** name the missing RPCs when the wallet client is too old ([#12121](https://github.com/dynamic-labs/dynamic-auth/issues/12121)) ([3a8a77f](https://github.com/dynamic-labs/dynamic-auth/commit/3a8a77f89d368d8634497a5b49ff345b004cdffa))
17
+ * **midnight:** return the submission identifier from the contract provider ([#12122](https://github.com/dynamic-labs/dynamic-auth/issues/12122)) ([8aa5289](https://github.com/dynamic-labs/dynamic-auth/commit/8aa52895fbdf09a3cf481049009a95f03cddda54))
18
+ * remediate high-severity dependency vulnerabilities ([#12149](https://github.com/dynamic-labs/dynamic-auth/issues/12149)) ([14058ee](https://github.com/dynamic-labs/dynamic-auth/commit/14058eeed34351791ad7ab7ac1fa720eb2cf9948))
19
+ * **starknet:** restore Braavos L2 session after lock and unlock ([#12124](https://github.com/dynamic-labs/dynamic-auth/issues/12124)) ([ab3b11b](https://github.com/dynamic-labs/dynamic-auth/commit/ab3b11bd71fbca8d32d4f2badfa49e3b9641eecf))
20
+ * **wallet-book:** add App Store id for Robinhood Wallet iOS install link ([#11877](https://github.com/dynamic-labs/dynamic-auth/issues/11877)) ([ef28619](https://github.com/dynamic-labs/dynamic-auth/commit/ef28619ba4396e01046717ea7ad560870a56c78f))
21
+ * **wallet-book:** add universal deep link for Exodus Solana WalletConnect ([#12123](https://github.com/dynamic-labs/dynamic-auth/issues/12123)) ([cd7223e](https://github.com/dynamic-labs/dynamic-auth/commit/cd7223eb2dc21314f351b96d9a06d795d282c35d))
22
+
23
+ ## [5.0.0](https://github.com/dynamic-labs/dynamic-auth/compare/v5.0.0-rc.4...v5.0.0) (2026-07-29)
24
+
25
+ ## 🚀 Dynamic SDK v5 is live
26
+
27
+ This release has been a long time coming. We've cleaned up the architecture, cut support for v1 and v2 embedded wallets from core, shipped a migration path for legacy wallets, and made the SDK story a lot cleaner for both web and mobile devs.
28
+
29
+ ### 🔥 Embedded Wallet clean up
30
+
31
+ V1/V2 embedded wallets support has been removed from `@dynamic-labs/sdk-react-core`. Dynamic WaaS (V3) is now the one and only embedded wallet type, all new embedded wallets are V3.
32
+
33
+ ### 📦 New: `@dynamic-labs/legacy-embedded-wallet-migration`
34
+
35
+ For users still on V1/V2 embedded wallets, we've got you covered with a dedicated migration package. It owns all the logic for upgrading to V3. This includes recovery flow, session keys, private key export, and the migration UI.
36
+
37
+ Two paths available:
38
+
39
+ - **UI-based** (recommended): Mount `LegacyWalletMigrationProvider` and the upgrade modal opens automatically when a legacy wallet is detected at login. It handles the full flow end-to-end.
40
+ - **Headless**: Call `upgradeToDynamicWaas()` directly if you want a custom migration UX.
41
+
42
+ ### 📱 JS SDK for web and React Native are the new default
43
+
44
+ Going forward, we're encouraging all developers building for web and React Native to use the JS SDK. It's the direction we're investing in. Ideally v5 is our final version of the old React SDK. To be clear: **no new features will be added to the old SDKs, including v5. All new feature development is happening in the JS SDK.**
45
+
46
+ ### ✏️ Legacy package renames
47
+
48
+ The old packages have been renamed to make their status explicit. Update your `package.json` accordingly:
49
+
50
+ ```
51
+ @dynamic-labs/client → @dynamic-labs/legacy-client
52
+ @dynamic-labs/react-hooks → @dynamic-labs/legacy-react-hooks
53
+ @dynamic-labs/react-native-extension → @dynamic-labs/legacy-react-native-extension
54
+ @dynamic-labs/web-extension → @dynamic-labs/legacy-web-extension
55
+ @dynamic-labs/solana-extension → @dynamic-labs/legacy-solana-extension
56
+ @dynamic-labs/sui-extension → @dynamic-labs/legacy-sui-extension
57
+ @dynamic-labs/viem-extension → @dynamic-labs/legacy-viem-extension
58
+ @dynamic-labs/zerodev-extension → @dynamic-labs/legacy-zerodev-extension
59
+ ```
60
+
61
+ ### :book: Full migration guide
62
+
63
+ Everything you need to upgrade from v4 → v5: [Upgrade SDK from V4 to V5 - Dynamic](https://www.dynamic.xyz/docs/react/reference/upgrade/v5)
64
+
65
+ Huge props to everyone who contributed to this.  :tada:
66
+
67
+ ------------------------------------------
68
+
69
+ ### ⚠ BREAKING CHANGES
70
+
71
+ * **waas:** move the client key-share store onto the core storage API (#12096)
72
+ * **react-native-extension:** store sensitive information with react-native-keychain (#12092)
73
+
74
+ ### Features
75
+
76
+ * **bitcoin:** add sendBitcoinWithConfirmation to show confirm modal on BTC sends ([#12025](https://github.com/dynamic-labs/dynamic-auth/issues/12025)) ([5f992f5](https://github.com/dynamic-labs/dynamic-auth/commit/5f992f5fdaf805f07870ab9248ea2fe06a2fd3bf))
77
+ * **waas:** delegate key shares with initial signer rules [DYNT-1675] ([#12028](https://github.com/dynamic-labs/dynamic-auth/issues/12028)) ([6b1ffc5](https://github.com/dynamic-labs/dynamic-auth/commit/6b1ffc5ee7070d639916a65b6a0aa6ada0167cb8)), closes [dynamic-labs/dynamic-waas-sdk#1500](https://github.com/dynamic-labs/dynamic-waas-sdk/issues/1500) [#10754](https://github.com/dynamic-labs/dynamic-auth/issues/10754) [#10772](https://github.com/dynamic-labs/dynamic-auth/issues/10772) [#646](https://github.com/dynamic-labs/dynamic-auth/issues/646) [#649](https://github.com/dynamic-labs/dynamic-auth/issues/649) [#1500](https://github.com/dynamic-labs/dynamic-auth/issues/1500) [dynamic-labs/dynamic-waas-sdk#1500](https://github.com/dynamic-labs/dynamic-waas-sdk/issues/1500)
78
+ * **waas:** expose rawSign sha256/blake2b in SignMessageContext ([#12075](https://github.com/dynamic-labs/dynamic-auth/issues/12075)) ([20e3d2f](https://github.com/dynamic-labs/dynamic-auth/commit/20e3d2f40d18aa0bdb77e9b00fa4a5dc5dd6e9de))
79
+
80
+
81
+ ### Bug Fixes
82
+
83
+ * **bitcoin:** resolve the public key of the active wallet, not the first ([#12091](https://github.com/dynamic-labs/dynamic-auth/issues/12091)) ([ebad70c](https://github.com/dynamic-labs/dynamic-auth/commit/ebad70c37ab9a771ad7051ca8f3c68d52c1d32fa)), closes [#11970](https://github.com/dynamic-labs/dynamic-auth/issues/11970) [#11970](https://github.com/dynamic-labs/dynamic-auth/issues/11970) [#11970](https://github.com/dynamic-labs/dynamic-auth/issues/11970)
84
+ * fallback to local wallet balance when token balances API returns no tokens ([#12059](https://github.com/dynamic-labs/dynamic-auth/issues/12059)) ([26c9022](https://github.com/dynamic-labs/dynamic-auth/commit/26c9022da3371b0da0498ec641157aa9fed356aa))
85
+ * security updates ([#12114](https://github.com/dynamic-labs/dynamic-auth/issues/12114)) ([a4f9bc1](https://github.com/dynamic-labs/dynamic-auth/commit/a4f9bc19870156903b76996c55ecd46a3417f004)) ([#12050](https://github.com/dynamic-labs/dynamic-auth/issues/12050)) ([8eeec92](https://github.com/dynamic-labs/dynamic-auth/commit/8eeec923d0f2f688ade60440e1f92bb1fd646201)) ([#12088](https://github.com/dynamic-labs/dynamic-auth/issues/12088)) ([f11f1f3](https://github.com/dynamic-labs/dynamic-auth/commit/f11f1f384201fa31f58753c0854eaf17f420fb09))
86
+ * replace deprecated polygon-rpc.com RPC with polygon.drpc.org ([#12032](https://github.com/dynamic-labs/dynamic-auth/issues/12032)) ([e1ece53](https://github.com/dynamic-labs/dynamic-auth/commit/e1ece538aedf6127048ca64daf9f9f3cf3a7026e))
87
+ * **sui:** stop using Sui Foundation fullnode URLs in WaaS and extension connectors ([#12108](https://github.com/dynamic-labs/dynamic-auth/issues/12108)) ([b23c85b](https://github.com/dynamic-labs/dynamic-auth/commit/b23c85b7c06f3147705a026ba23f5e8ae1f03b68))
88
+ * **wallet-book:** use Backpack universal link for iOS in-app browser ([#12053](https://github.com/dynamic-labs/dynamic-auth/issues/12053)) ([afba6b1](https://github.com/dynamic-labs/dynamic-auth/commit/afba6b192e4be50aaeca9f5fad88b44808a034a0))
89
+ * **wallet-connect:** recover WalletConnect IndexedDB store when object store is missing ([#12048](https://github.com/dynamic-labs/dynamic-auth/issues/12048)) ([96ff80e](https://github.com/dynamic-labs/dynamic-auth/commit/96ff80e40b28740de15b1702b85f954100fd5f25))
90
+
91
+
92
+ * **react-native-extension:** store sensitive information with react-native-keychain ([#12092](https://github.com/dynamic-labs/dynamic-auth/issues/12092)) ([10c0f2f](https://github.com/dynamic-labs/dynamic-auth/commit/10c0f2f6c281e300e4a0455aaaa0d452808dcb87))
93
+ * **waas:** move the client key-share store onto the core storage API ([#12096](https://github.com/dynamic-labs/dynamic-auth/issues/12096)) ([778f993](https://github.com/dynamic-labs/dynamic-auth/commit/778f993c5528ec03a7f1446dcb81a041a22db89b)), closes [#12094](https://github.com/dynamic-labs/dynamic-auth/issues/12094) [#12094](https://github.com/dynamic-labs/dynamic-auth/issues/12094) [#12092](https://github.com/dynamic-labs/dynamic-auth/issues/12092) [#12094](https://github.com/dynamic-labs/dynamic-auth/issues/12094)
94
+
95
+
2
96
  ## [5.0.0-rc.4](https://github.com/dynamic-labs/dynamic-auth/compare/v5.0.0-rc.3...v5.0.0-rc.4) (2026-07-20)
3
97
 
4
98
 
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "5.0.0-rc.4";
6
+ var version = "5.1.0";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "5.0.0-rc.4";
2
+ var version = "5.1.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/bitcoin",
3
- "version": "5.0.0-rc.4",
3
+ "version": "5.1.0",
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",
@@ -18,24 +18,24 @@
18
18
  },
19
19
  "homepage": "https://www.dynamic.xyz/",
20
20
  "dependencies": {
21
- "@dynamic-labs-wallet/browser-wallet-client": "1.0.74",
21
+ "@dynamic-labs-wallet/browser-wallet-client": "1.0.92",
22
22
  "@dynamic-labs-wallet/forward-mpc-client": "1.0.1",
23
23
  "@bitcoinerlab/secp256k1": "1.1.1",
24
24
  "@btckit/types": "0.0.19",
25
- "@dynamic-labs/sdk-api-core": "0.0.1082",
25
+ "@dynamic-labs/sdk-api-core": "0.0.1093",
26
26
  "@wallet-standard/app": "1.0.1",
27
27
  "@wallet-standard/base": "1.0.1",
28
28
  "bitcoinjs-lib": "6.1.5",
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": "5.0.0-rc.4",
33
- "@dynamic-labs/logger": "5.0.0-rc.4",
34
- "@dynamic-labs/types": "5.0.0-rc.4",
35
- "@dynamic-labs/utils": "5.0.0-rc.4",
36
- "@dynamic-labs/waas": "5.0.0-rc.4",
37
- "@dynamic-labs/wallet-book": "5.0.0-rc.4",
38
- "@dynamic-labs/wallet-connector-core": "5.0.0-rc.4",
32
+ "@dynamic-labs/assert-package-version": "5.1.0",
33
+ "@dynamic-labs/logger": "5.1.0",
34
+ "@dynamic-labs/types": "5.1.0",
35
+ "@dynamic-labs/utils": "5.1.0",
36
+ "@dynamic-labs/waas": "5.1.0",
37
+ "@dynamic-labs/wallet-book": "5.1.0",
38
+ "@dynamic-labs/wallet-connector-core": "5.1.0",
39
39
  "eventemitter3": "5.0.1"
40
40
  },
41
41
  "peerDependencies": {}
@@ -41,6 +41,7 @@ export declare abstract class BitcoinWalletConnector extends WalletConnectorBase
41
41
  setAdditionalAddresses(mainAddress: string, additionalAddresses: WalletAdditionalAddress[]): Promise<void>;
42
42
  sendRawTransaction(rawTransaction: string): Promise<string>;
43
43
  sendBitcoin(transaction: BitcoinTransaction): Promise<string | undefined>;
44
+ sendBitcoinWithConfirmation?(transaction: BitcoinTransaction): Promise<string | undefined>;
44
45
  buildPsbt(transaction: BitcoinTransaction): Promise<string | undefined>;
45
46
  getProvider<T>(): T & EventEmitter<string | symbol, any>;
46
47
  abstract signPsbt(request: BitcoinSignPsbtRequest | EmbeddedWalletSignPsbtRequest): Promise<BitcoinSignPsbtResponse | undefined>;
@@ -160,22 +160,57 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
160
160
  return _super.getBalance.call(this, (_a = this.activeAccountAddress) !== null && _a !== void 0 ? _a : address);
161
161
  });
162
162
  }
163
+ /**
164
+ * Finds the credential that owns the active address, either as its primary
165
+ * (mainnet) address or as one of its network-tagged additional addresses.
166
+ *
167
+ * This connector is shared across every Bitcoin wallet of the user —
168
+ * `verifiedCredentials` holds them all — so the owning credential must be
169
+ * looked up by the active address. Reading `verifiedCredential` instead
170
+ * would always answer with the user's *first* wallet.
171
+ */
172
+ findCredentialForActiveAddress() {
173
+ var _a;
174
+ const { activeAccountAddress } = this;
175
+ if (!activeAccountAddress) {
176
+ return undefined;
177
+ }
178
+ return ((_a = this.verifiedCredentials) !== null && _a !== void 0 ? _a : []).find((credential) => {
179
+ var _a;
180
+ return (credential === null || credential === void 0 ? void 0 : credential.address) === activeAccountAddress ||
181
+ ((_a = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []).some((additionalAddress) => additionalAddress.address === activeAccountAddress);
182
+ });
183
+ }
163
184
  /**
164
185
  * Wallet identity: keyshares/wallet records are keyed by the wallet's
165
186
  * primary (mainnet) address. On testnet the active address is the same
166
187
  * wallet's per-network encoding — WaaS lookups (password, signing) must use
167
188
  * the primary address (the same key signs both encodings).
189
+ *
190
+ * Resolved from the credential that owns the active address, so a user with
191
+ * several Bitcoin wallets gets the identity of the wallet being spent from.
192
+ * Never falls back to another wallet's address: a substituted key produces a
193
+ * signature that looks valid locally and is rejected by the network, so the
194
+ * failure has to surface here instead.
168
195
  */
169
196
  getWalletIdentityAddress() {
170
- var _a, _b;
171
- // The active address is a valid fallback only on mainnet, where it is
172
- // the primary encoding; a switched-network encoding must never be used
173
- // as the wallet identity.
174
- const identity = (_b = (_a = this.verifiedCredential) === null || _a === void 0 ? void 0 : _a.address) !== null && _b !== void 0 ? _b : (this.activeNetworkId === BITCOIN_MAINNET_NETWORK_ID
197
+ var _a, _b, _c, _d;
198
+ const owningCredential = this.findCredentialForActiveAddress();
199
+ // The primary address is normally on the credential itself; fall back to
200
+ // its mainnet-tagged payment address for credentials that only carry
201
+ // network-tagged entries.
202
+ const owningCredentialAddress = (_a = owningCredential === null || owningCredential === void 0 ? void 0 : owningCredential.address) !== null && _a !== void 0 ? _a : (_c = ((_b = owningCredential === null || owningCredential === void 0 ? void 0 : owningCredential.walletAdditionalAddresses) !== null && _b !== void 0 ? _b : []).find((addr) => addr.type === 'payment' &&
203
+ addr.network === 'mainnet')) === null || _c === void 0 ? void 0 : _c.address;
204
+ // With no credential owning the active address there is nothing to
205
+ // identify the wallet by except the active address itself, which on
206
+ // mainnet is the primary encoding. That is still the *requested* wallet,
207
+ // so it is a safe answer — unlike `verifiedCredential`, which is always
208
+ // the user's first wallet and would substitute a different key.
209
+ const identity = owningCredentialAddress !== null && owningCredentialAddress !== void 0 ? owningCredentialAddress : (this.activeNetworkId === BITCOIN_MAINNET_NETWORK_ID
175
210
  ? this.activeAccountAddress
176
211
  : undefined);
177
212
  if (!identity) {
178
- throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
213
+ throw new utils.DynamicError(`Could not resolve the wallet identity for active address ${(_d = this.activeAccountAddress) !== null && _d !== void 0 ? _d : '(unset)'}`, 'active_account_address_is_required');
179
214
  }
180
215
  return identity;
181
216
  }
@@ -185,14 +220,18 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
185
220
  * address (wallets created before per-network registration).
186
221
  */
187
222
  getPaymentAddressForNetwork(network) {
188
- var _a, _b, _c;
189
- const tagged = (_b = (_a = this.verifiedCredential) === null || _a === void 0 ? void 0 : _a.walletAdditionalAddresses) === null || _b === void 0 ? void 0 : _b.find((addr) => addr.type === 'payment' &&
223
+ var _a, _b;
224
+ // Resolved against the wallet that is currently active. Reading
225
+ // `verifiedCredential` alone would switch the network on the user's first
226
+ // wallet, handing back an address that belongs to a different wallet.
227
+ const credential = (_a = this.findCredentialForActiveAddress()) !== null && _a !== void 0 ? _a : this.verifiedCredential;
228
+ const tagged = (_b = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) === null || _b === void 0 ? void 0 : _b.find((addr) => addr.type === 'payment' &&
190
229
  addr.network === network);
191
230
  if (tagged === null || tagged === void 0 ? void 0 : tagged.address) {
192
231
  return tagged.address;
193
232
  }
194
233
  if (network === 'mainnet') {
195
- return (_c = this.verifiedCredential) === null || _c === void 0 ? void 0 : _c.address;
234
+ return credential === null || credential === void 0 ? void 0 : credential.address;
196
235
  }
197
236
  return undefined;
198
237
  }
@@ -352,6 +391,30 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
352
391
  return this.sendRawTransaction(signedTransactionHex);
353
392
  });
354
393
  }
394
+ sendBitcoinWithConfirmation(transaction) {
395
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
396
+ if (!this.activeAccountAddress) {
397
+ throw new utils.DynamicError('Active account address is required', 'active_account_address_is_required');
398
+ }
399
+ const uiTransaction = new BitcoinUiTransaction.BitcoinUiTransaction({
400
+ from: this.activeAccountAddress,
401
+ mempoolApiService: this.mempoolApiService,
402
+ onSubmit: (tx) => _tslib.__awaiter(this, void 0, void 0, function* () {
403
+ const txHash = yield this.sendBitcoin(tx);
404
+ if (!txHash) {
405
+ throw new utils.DynamicError('Failed to send Bitcoin transaction', 'send_bitcoin_transaction');
406
+ }
407
+ return txHash;
408
+ }),
409
+ });
410
+ uiTransaction.to = transaction.recipientAddress;
411
+ uiTransaction.value = transaction.amount;
412
+ if (transaction.feePriority) {
413
+ uiTransaction.feePriority = transaction.feePriority;
414
+ }
415
+ return this.walletUiUtils.sendTransaction(this, uiTransaction);
416
+ });
417
+ }
355
418
  /**
356
419
  * Creates a new Bitcoin wallet account with Bitcoin-specific defaults.
357
420
  * If `bitcoinConfig.addressType` is not provided, the underlying BTC client
@@ -637,7 +700,15 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
637
700
  if (allCredentials.length === 0) {
638
701
  throw new utils.DynamicError(`No verified credentials found. The address ${this.activeAccountAddress} may not be properly registered.`, 'no_verified_credentials_found_the_address');
639
702
  }
640
- for (const credential of allCredentials) {
703
+ // Scoped to the credential that owns the active address when there is one.
704
+ // Searching every credential would let another wallet of the same user
705
+ // answer for this one, and that is the failure this must not produce: a
706
+ // key that is not the wallet's own produces a witness the network rejects.
707
+ const owningCredential = this.findCredentialForActiveAddress();
708
+ const credentialsToSearch = owningCredential
709
+ ? [owningCredential]
710
+ : allCredentials;
711
+ for (const credential of credentialsToSearch) {
641
712
  const { walletAdditionalAddresses } = credential !== null && credential !== void 0 ? credential : {};
642
713
  if (!walletAdditionalAddresses) {
643
714
  continue;
@@ -647,8 +718,8 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
647
718
  return additionalAddress.publicKey;
648
719
  }
649
720
  }
650
- const allAvailableAddresses = allCredentials
651
- .flatMap((cred) => { var _a; return (_a = cred.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []; })
721
+ const allAvailableAddresses = credentialsToSearch
722
+ .flatMap((cred) => { var _a; return (_a = cred === null || cred === void 0 ? void 0 : cred.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []; })
652
723
  .map((addr) => addr.address)
653
724
  .join(', ');
654
725
  throw new utils.DynamicError(`No additional address found for ${this.activeAccountAddress}. Available addresses: ${allAvailableAddresses || 'none'}`, 'no_additional_address_found_for_available');
@@ -22,7 +22,6 @@ declare const DynamicWaasBitcoinConnector_base: (abstract new (...args: any[]) =
22
22
  scope: TokenScope;
23
23
  }) => Promise<string | undefined>) | undefined;
24
24
  onUnauthorized?: (() => void | Promise<void>) | undefined;
25
- /** Dynamic network ids for the Bitcoin networks (mirrors the networks catalog). */
26
25
  environmentId?: string | undefined;
27
26
  baseApiUrl?: string | undefined;
28
27
  relayUrl?: string | undefined;
@@ -56,9 +55,10 @@ declare const DynamicWaasBitcoinConnector_base: (abstract new (...args: any[]) =
56
55
  setRelayUrl(relayUrl: string): void;
57
56
  setGetSignedSessionIdFunction(getSignedSessionId: () => Promise<string>): void;
58
57
  setGetSessionPublicKeyFunction(getSessionPublicKey: () => Promise<string | undefined>): void;
59
- delegateKeyShares({ accountAddress, password, }: {
58
+ delegateKeyShares({ accountAddress, password, initialSignerRules, }: {
60
59
  accountAddress: string;
61
60
  password?: string | undefined;
61
+ initialSignerRules?: import("@dynamic-labs-wallet/browser-wallet-client").WaasPolicyRule[] | undefined;
62
62
  }): Promise<void>;
63
63
  createRevocableSignedSessionCallback(): {
64
64
  getSignedSessionId: () => Promise<string>;
@@ -144,11 +144,7 @@ declare const DynamicWaasBitcoinConnector_base: (abstract new (...args: any[]) =
144
144
  exportClientKeysharesFromGoogleDrive({ accountAddress, password, }: {
145
145
  accountAddress: string;
146
146
  password?: string | undefined;
147
- }): Promise<void>; /**
148
- * Resolves the wallet's payment address for a network from the credential's
149
- * network-tagged additional addresses. Mainnet falls back to the primary
150
- * address (wallets created before per-network registration).
151
- */
147
+ }): Promise<void>;
152
148
  backupKeySharesToICloud({ accountAddress, password, }: {
153
149
  accountAddress: string;
154
150
  password?: string | undefined;
@@ -298,11 +294,27 @@ export declare class DynamicWaasBitcoinConnector extends DynamicWaasBitcoinConne
298
294
  * routing is address-driven).
299
295
  */
300
296
  getBalance(address: string): Promise<string | undefined>;
297
+ /**
298
+ * Finds the credential that owns the active address, either as its primary
299
+ * (mainnet) address or as one of its network-tagged additional addresses.
300
+ *
301
+ * This connector is shared across every Bitcoin wallet of the user —
302
+ * `verifiedCredentials` holds them all — so the owning credential must be
303
+ * looked up by the active address. Reading `verifiedCredential` instead
304
+ * would always answer with the user's *first* wallet.
305
+ */
306
+ private findCredentialForActiveAddress;
301
307
  /**
302
308
  * Wallet identity: keyshares/wallet records are keyed by the wallet's
303
309
  * primary (mainnet) address. On testnet the active address is the same
304
310
  * wallet's per-network encoding — WaaS lookups (password, signing) must use
305
311
  * the primary address (the same key signs both encodings).
312
+ *
313
+ * Resolved from the credential that owns the active address, so a user with
314
+ * several Bitcoin wallets gets the identity of the wallet being spent from.
315
+ * Never falls back to another wallet's address: a substituted key produces a
316
+ * signature that looks valid locally and is rejected by the network, so the
317
+ * failure has to surface here instead.
306
318
  */
307
319
  private getWalletIdentityAddress;
308
320
  /**
@@ -365,6 +377,7 @@ export declare class DynamicWaasBitcoinConnector extends DynamicWaasBitcoinConne
365
377
  * @throws {DynamicError} If any step in the process fails
366
378
  */
367
379
  sendBitcoin(transaction: BitcoinTransaction): Promise<string | undefined>;
380
+ sendBitcoinWithConfirmation(transaction: BitcoinTransaction): Promise<string | undefined>;
368
381
  /**
369
382
  * Creates a new Bitcoin wallet account with Bitcoin-specific defaults.
370
383
  * If `bitcoinConfig.addressType` is not provided, the underlying BTC client
@@ -156,22 +156,57 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
156
156
  return _super.getBalance.call(this, (_a = this.activeAccountAddress) !== null && _a !== void 0 ? _a : address);
157
157
  });
158
158
  }
159
+ /**
160
+ * Finds the credential that owns the active address, either as its primary
161
+ * (mainnet) address or as one of its network-tagged additional addresses.
162
+ *
163
+ * This connector is shared across every Bitcoin wallet of the user —
164
+ * `verifiedCredentials` holds them all — so the owning credential must be
165
+ * looked up by the active address. Reading `verifiedCredential` instead
166
+ * would always answer with the user's *first* wallet.
167
+ */
168
+ findCredentialForActiveAddress() {
169
+ var _a;
170
+ const { activeAccountAddress } = this;
171
+ if (!activeAccountAddress) {
172
+ return undefined;
173
+ }
174
+ return ((_a = this.verifiedCredentials) !== null && _a !== void 0 ? _a : []).find((credential) => {
175
+ var _a;
176
+ return (credential === null || credential === void 0 ? void 0 : credential.address) === activeAccountAddress ||
177
+ ((_a = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []).some((additionalAddress) => additionalAddress.address === activeAccountAddress);
178
+ });
179
+ }
159
180
  /**
160
181
  * Wallet identity: keyshares/wallet records are keyed by the wallet's
161
182
  * primary (mainnet) address. On testnet the active address is the same
162
183
  * wallet's per-network encoding — WaaS lookups (password, signing) must use
163
184
  * the primary address (the same key signs both encodings).
185
+ *
186
+ * Resolved from the credential that owns the active address, so a user with
187
+ * several Bitcoin wallets gets the identity of the wallet being spent from.
188
+ * Never falls back to another wallet's address: a substituted key produces a
189
+ * signature that looks valid locally and is rejected by the network, so the
190
+ * failure has to surface here instead.
164
191
  */
165
192
  getWalletIdentityAddress() {
166
- var _a, _b;
167
- // The active address is a valid fallback only on mainnet, where it is
168
- // the primary encoding; a switched-network encoding must never be used
169
- // as the wallet identity.
170
- const identity = (_b = (_a = this.verifiedCredential) === null || _a === void 0 ? void 0 : _a.address) !== null && _b !== void 0 ? _b : (this.activeNetworkId === BITCOIN_MAINNET_NETWORK_ID
193
+ var _a, _b, _c, _d;
194
+ const owningCredential = this.findCredentialForActiveAddress();
195
+ // The primary address is normally on the credential itself; fall back to
196
+ // its mainnet-tagged payment address for credentials that only carry
197
+ // network-tagged entries.
198
+ const owningCredentialAddress = (_a = owningCredential === null || owningCredential === void 0 ? void 0 : owningCredential.address) !== null && _a !== void 0 ? _a : (_c = ((_b = owningCredential === null || owningCredential === void 0 ? void 0 : owningCredential.walletAdditionalAddresses) !== null && _b !== void 0 ? _b : []).find((addr) => addr.type === 'payment' &&
199
+ addr.network === 'mainnet')) === null || _c === void 0 ? void 0 : _c.address;
200
+ // With no credential owning the active address there is nothing to
201
+ // identify the wallet by except the active address itself, which on
202
+ // mainnet is the primary encoding. That is still the *requested* wallet,
203
+ // so it is a safe answer — unlike `verifiedCredential`, which is always
204
+ // the user's first wallet and would substitute a different key.
205
+ const identity = owningCredentialAddress !== null && owningCredentialAddress !== void 0 ? owningCredentialAddress : (this.activeNetworkId === BITCOIN_MAINNET_NETWORK_ID
171
206
  ? this.activeAccountAddress
172
207
  : undefined);
173
208
  if (!identity) {
174
- throw new DynamicError('Active account address is required', 'active_account_address_is_required');
209
+ throw new DynamicError(`Could not resolve the wallet identity for active address ${(_d = this.activeAccountAddress) !== null && _d !== void 0 ? _d : '(unset)'}`, 'active_account_address_is_required');
175
210
  }
176
211
  return identity;
177
212
  }
@@ -181,14 +216,18 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
181
216
  * address (wallets created before per-network registration).
182
217
  */
183
218
  getPaymentAddressForNetwork(network) {
184
- var _a, _b, _c;
185
- const tagged = (_b = (_a = this.verifiedCredential) === null || _a === void 0 ? void 0 : _a.walletAdditionalAddresses) === null || _b === void 0 ? void 0 : _b.find((addr) => addr.type === 'payment' &&
219
+ var _a, _b;
220
+ // Resolved against the wallet that is currently active. Reading
221
+ // `verifiedCredential` alone would switch the network on the user's first
222
+ // wallet, handing back an address that belongs to a different wallet.
223
+ const credential = (_a = this.findCredentialForActiveAddress()) !== null && _a !== void 0 ? _a : this.verifiedCredential;
224
+ const tagged = (_b = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) === null || _b === void 0 ? void 0 : _b.find((addr) => addr.type === 'payment' &&
186
225
  addr.network === network);
187
226
  if (tagged === null || tagged === void 0 ? void 0 : tagged.address) {
188
227
  return tagged.address;
189
228
  }
190
229
  if (network === 'mainnet') {
191
- return (_c = this.verifiedCredential) === null || _c === void 0 ? void 0 : _c.address;
230
+ return credential === null || credential === void 0 ? void 0 : credential.address;
192
231
  }
193
232
  return undefined;
194
233
  }
@@ -348,6 +387,30 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
348
387
  return this.sendRawTransaction(signedTransactionHex);
349
388
  });
350
389
  }
390
+ sendBitcoinWithConfirmation(transaction) {
391
+ return __awaiter(this, void 0, void 0, function* () {
392
+ if (!this.activeAccountAddress) {
393
+ throw new DynamicError('Active account address is required', 'active_account_address_is_required');
394
+ }
395
+ const uiTransaction = new BitcoinUiTransaction({
396
+ from: this.activeAccountAddress,
397
+ mempoolApiService: this.mempoolApiService,
398
+ onSubmit: (tx) => __awaiter(this, void 0, void 0, function* () {
399
+ const txHash = yield this.sendBitcoin(tx);
400
+ if (!txHash) {
401
+ throw new DynamicError('Failed to send Bitcoin transaction', 'send_bitcoin_transaction');
402
+ }
403
+ return txHash;
404
+ }),
405
+ });
406
+ uiTransaction.to = transaction.recipientAddress;
407
+ uiTransaction.value = transaction.amount;
408
+ if (transaction.feePriority) {
409
+ uiTransaction.feePriority = transaction.feePriority;
410
+ }
411
+ return this.walletUiUtils.sendTransaction(this, uiTransaction);
412
+ });
413
+ }
351
414
  /**
352
415
  * Creates a new Bitcoin wallet account with Bitcoin-specific defaults.
353
416
  * If `bitcoinConfig.addressType` is not provided, the underlying BTC client
@@ -633,7 +696,15 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
633
696
  if (allCredentials.length === 0) {
634
697
  throw new DynamicError(`No verified credentials found. The address ${this.activeAccountAddress} may not be properly registered.`, 'no_verified_credentials_found_the_address');
635
698
  }
636
- for (const credential of allCredentials) {
699
+ // Scoped to the credential that owns the active address when there is one.
700
+ // Searching every credential would let another wallet of the same user
701
+ // answer for this one, and that is the failure this must not produce: a
702
+ // key that is not the wallet's own produces a witness the network rejects.
703
+ const owningCredential = this.findCredentialForActiveAddress();
704
+ const credentialsToSearch = owningCredential
705
+ ? [owningCredential]
706
+ : allCredentials;
707
+ for (const credential of credentialsToSearch) {
637
708
  const { walletAdditionalAddresses } = credential !== null && credential !== void 0 ? credential : {};
638
709
  if (!walletAdditionalAddresses) {
639
710
  continue;
@@ -643,8 +714,8 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
643
714
  return additionalAddress.publicKey;
644
715
  }
645
716
  }
646
- const allAvailableAddresses = allCredentials
647
- .flatMap((cred) => { var _a; return (_a = cred.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []; })
717
+ const allAvailableAddresses = credentialsToSearch
718
+ .flatMap((cred) => { var _a; return (_a = cred === null || cred === void 0 ? void 0 : cred.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []; })
648
719
  .map((addr) => addr.address)
649
720
  .join(', ');
650
721
  throw new DynamicError(`No additional address found for ${this.activeAccountAddress}. Available addresses: ${allAvailableAddresses || 'none'}`, 'no_additional_address_found_for_available');
package/src/index.cjs CHANGED
@@ -69,6 +69,7 @@ exports.PsbtParser = PsbtParser.PsbtParser;
69
69
  exports.BitcoinWallet = BitcoinWallet.BitcoinWallet;
70
70
  exports.isBitcoinWallet = isBitcoinWallet.isBitcoinWallet;
71
71
  exports.BitcoinProviderHelper = bitcoinProviderHelper.BitcoinProviderHelper;
72
+ exports.DUST_LIMIT = _const.DUST_LIMIT;
72
73
  exports.HTTP_STATUS_NOT_FOUND = _const.HTTP_STATUS_NOT_FOUND;
73
74
  exports.HTTP_STATUS_TOO_MANY_REQUESTS = _const.HTTP_STATUS_TOO_MANY_REQUESTS;
74
75
  exports.BitcoinWalletConnectors = BitcoinWalletConnectors;
package/src/index.d.ts CHANGED
@@ -6,4 +6,4 @@ export * from './wallet';
6
6
  export { UnisatConnector, BitcoinSatsConnectConnector, DynamicWaasBitcoinConnector, } from './connectors';
7
7
  export declare const BitcoinWalletConnectors: (props: any) => (import("dist/packages/wallet-connector-core/src").WalletConnectorConstructor | typeof OkxConnector)[];
8
8
  export { BitcoinProviderHelper } from './bitcoinProviderHelper';
9
- export { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_TOO_MANY_REQUESTS } from './const';
9
+ export { DUST_LIMIT, HTTP_STATUS_NOT_FOUND, HTTP_STATUS_TOO_MANY_REQUESTS, } from './const';
package/src/index.js CHANGED
@@ -34,7 +34,7 @@ export { PsbtParser } from './utils/psbtParser/PsbtParser.js';
34
34
  export { BitcoinWallet } from './wallet/BitcoinWallet.js';
35
35
  export { isBitcoinWallet } from './wallet/isBitcoinWallet/isBitcoinWallet.js';
36
36
  export { BitcoinProviderHelper } from './bitcoinProviderHelper.js';
37
- export { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_TOO_MANY_REQUESTS } from './const.js';
37
+ export { DUST_LIMIT, HTTP_STATUS_NOT_FOUND, HTTP_STATUS_TOO_MANY_REQUESTS } from './const.js';
38
38
 
39
39
  assertPackageVersion('@dynamic-labs/bitcoin', version);
40
40
  const BitcoinWalletConnectors = (props) => [
@@ -140,7 +140,10 @@ 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', 'sendrawtransaction_failed_to_send_transaction');
143
+ // The node's reject reason is the only thing that distinguishes a bad
144
+ // witness from a fee or mempool problem, so it goes in the thrown
145
+ // message rather than only into a debug log the integrator cannot read.
146
+ throw new utils.DynamicError(`sendRawTransaction - failed to send transaction${error ? `: ${error}` : ''}`, 'sendrawtransaction_failed_to_send_transaction');
144
147
  }
145
148
  return response.text();
146
149
  });
@@ -136,7 +136,10 @@ 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', 'sendrawtransaction_failed_to_send_transaction');
139
+ // The node's reject reason is the only thing that distinguishes a bad
140
+ // witness from a fee or mempool problem, so it goes in the thrown
141
+ // message rather than only into a debug log the integrator cannot read.
142
+ throw new DynamicError(`sendRawTransaction - failed to send transaction${error ? `: ${error}` : ''}`, 'sendrawtransaction_failed_to_send_transaction');
140
143
  }
141
144
  return response.text();
142
145
  });
@@ -42,6 +42,21 @@ class BitcoinWallet extends walletConnectorCore.Wallet {
42
42
  return this._connector.sendBitcoin(transaction);
43
43
  });
44
44
  }
45
+ /**
46
+ * Sends satoshis to a bitcoin address, showing a confirmation modal when
47
+ * the connector supports it (e.g. DynamicWaasBitcoinConnector). Falls back
48
+ * to sendBitcoin for connectors that have no confirmation UI.
49
+ * @returns A promise that resolves to the transaction id
50
+ */
51
+ sendBitcoinWithConfirmation(transaction) {
52
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
53
+ yield this.sync();
54
+ if (this._connector.sendBitcoinWithConfirmation) {
55
+ return this._connector.sendBitcoinWithConfirmation(transaction);
56
+ }
57
+ return this._connector.sendBitcoin(transaction);
58
+ });
59
+ }
45
60
  /**
46
61
  * Signs a message using a specific address type (payment or ordinals).
47
62
  * @param messageToSign - The message to sign.
@@ -23,6 +23,13 @@ export declare class BitcoinWallet extends Wallet<BitcoinWalletConnector> {
23
23
  * @returns A promise that resolves to the transaction id
24
24
  */
25
25
  sendBitcoin(transaction: BitcoinTransaction): Promise<string | undefined>;
26
+ /**
27
+ * Sends satoshis to a bitcoin address, showing a confirmation modal when
28
+ * the connector supports it (e.g. DynamicWaasBitcoinConnector). Falls back
29
+ * to sendBitcoin for connectors that have no confirmation UI.
30
+ * @returns A promise that resolves to the transaction id
31
+ */
32
+ sendBitcoinWithConfirmation(transaction: BitcoinTransaction): Promise<string | undefined>;
26
33
  /**
27
34
  * Signs a message using a specific address type (payment or ordinals).
28
35
  * @param messageToSign - The message to sign.
@@ -38,6 +38,21 @@ class BitcoinWallet extends Wallet {
38
38
  return this._connector.sendBitcoin(transaction);
39
39
  });
40
40
  }
41
+ /**
42
+ * Sends satoshis to a bitcoin address, showing a confirmation modal when
43
+ * the connector supports it (e.g. DynamicWaasBitcoinConnector). Falls back
44
+ * to sendBitcoin for connectors that have no confirmation UI.
45
+ * @returns A promise that resolves to the transaction id
46
+ */
47
+ sendBitcoinWithConfirmation(transaction) {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ yield this.sync();
50
+ if (this._connector.sendBitcoinWithConfirmation) {
51
+ return this._connector.sendBitcoinWithConfirmation(transaction);
52
+ }
53
+ return this._connector.sendBitcoin(transaction);
54
+ });
55
+ }
41
56
  /**
42
57
  * Signs a message using a specific address type (payment or ordinals).
43
58
  * @param messageToSign - The message to sign.