@dynamic-labs/bitcoin 5.3.0 → 5.3.2
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 +19 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +10 -10
- package/src/connectors/DynamicWaasBitcoinConnector/DynamicWaasBitcoinConnector.cjs +84 -18
- package/src/connectors/DynamicWaasBitcoinConnector/DynamicWaasBitcoinConnector.d.ts +54 -7
- package/src/connectors/DynamicWaasBitcoinConnector/DynamicWaasBitcoinConnector.js +84 -18
- package/src/index.d.ts +2 -2
- package/src/types.d.ts +13 -0
- package/src/utils/psbtParser/PsbtParser.cjs +26 -23
- package/src/utils/psbtParser/PsbtParser.d.ts +7 -3
- package/src/utils/psbtParser/PsbtParser.js +27 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
|
|
2
|
+
### [5.3.2](https://github.com/dynamic-labs/dynamic-auth/compare/v5.3.1...v5.3.2) (2026-08-19)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **bitcoin:** add allowedSighash to embedded wallet signPsbt ([#12233](https://github.com/dynamic-labs/dynamic-auth/issues/12233)) ([d0ff829](https://github.com/dynamic-labs/dynamic-auth/commit/d0ff8291a3d1771d5ca356172acc10af94349b15))
|
|
8
|
+
* **bitcoin:** forward signingIndexes to embedded PSBT signing ([#12211](https://github.com/dynamic-labs/dynamic-auth/issues/12211)) ([733da3d](https://github.com/dynamic-labs/dynamic-auth/commit/733da3d32f39a9ae6d813df84fcff6350ba12eb5))
|
|
9
|
+
* expose createOfflineRecoveryShares SDK function in webview and legacy client ([#12239](https://github.com/dynamic-labs/dynamic-auth/issues/12239)) ([c8056e8](https://github.com/dynamic-labs/dynamic-auth/commit/c8056e8d5a1c66ac0146b046bc7cc198de118f0b))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **bitcoin:** parse PSBTs with the active network's address encoding ([#12219](https://github.com/dynamic-labs/dynamic-auth/issues/12219)) ([8b151e2](https://github.com/dynamic-labs/dynamic-auth/commit/8b151e23440094cfa0d48728e1d0f19de567ca05))
|
|
15
|
+
* **bitcoin:** stop ensureActiveAccount from resetting a switched network ([#12215](https://github.com/dynamic-labs/dynamic-auth/issues/12215)) ([86668b7](https://github.com/dynamic-labs/dynamic-auth/commit/86668b79d23cd89b7e96518551cdaecab7dd5ce3))
|
|
16
|
+
* remediate high-severity dependency vulnerabilities ([#12238](https://github.com/dynamic-labs/dynamic-auth/issues/12238)) ([a4fc78c](https://github.com/dynamic-labs/dynamic-auth/commit/a4fc78ca27ee44ce6a1038c4e0d07f55528b4994))
|
|
17
|
+
* **waas-svm:** retry SVM gas sponsorship before falling back, then throw ([#12240](https://github.com/dynamic-labs/dynamic-auth/issues/12240)) ([f599a7a](https://github.com/dynamic-labs/dynamic-auth/commit/f599a7af55727e06373772acf574ee7c43e56bbb))
|
|
18
|
+
|
|
19
|
+
### [5.3.1](https://github.com/dynamic-labs/dynamic-auth/compare/v5.3.0...v5.3.1) (2026-08-14)
|
|
20
|
+
|
|
2
21
|
## [5.3.0](https://github.com/dynamic-labs/dynamic-auth/compare/v5.2.0...v5.3.0) (2026-08-12)
|
|
3
22
|
|
|
4
23
|
|
package/package.cjs
CHANGED
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/bitcoin",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.2",
|
|
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.
|
|
21
|
+
"@dynamic-labs-wallet/browser-wallet-client": "1.0.106",
|
|
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
|
|
25
|
+
"@dynamic-labs/sdk-api-core": "0.12.0",
|
|
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.3.
|
|
33
|
-
"@dynamic-labs/logger": "5.3.
|
|
34
|
-
"@dynamic-labs/types": "5.3.
|
|
35
|
-
"@dynamic-labs/utils": "5.3.
|
|
36
|
-
"@dynamic-labs/waas": "5.3.
|
|
37
|
-
"@dynamic-labs/wallet-book": "5.3.
|
|
38
|
-
"@dynamic-labs/wallet-connector-core": "5.3.
|
|
32
|
+
"@dynamic-labs/assert-package-version": "5.3.2",
|
|
33
|
+
"@dynamic-labs/logger": "5.3.2",
|
|
34
|
+
"@dynamic-labs/types": "5.3.2",
|
|
35
|
+
"@dynamic-labs/utils": "5.3.2",
|
|
36
|
+
"@dynamic-labs/waas": "5.3.2",
|
|
37
|
+
"@dynamic-labs/wallet-book": "5.3.2",
|
|
38
|
+
"@dynamic-labs/wallet-connector-core": "5.3.2",
|
|
39
39
|
"eventemitter3": "5.0.1"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {}
|
|
@@ -17,6 +17,7 @@ var BitcoinWalletConnector = require('../BitcoinWalletConnector.cjs');
|
|
|
17
17
|
require('@wallet-standard/app');
|
|
18
18
|
require('jsontokens');
|
|
19
19
|
var btcToSatoshis = require('../../utils/btcToSatoshis/btcToSatoshis.cjs');
|
|
20
|
+
var getBitcoinNetwork = require('../../utils/getBitcoinNetwork/getBitcoinNetwork.cjs');
|
|
20
21
|
var PsbtParser = require('../../utils/psbtParser/PsbtParser.cjs');
|
|
21
22
|
var BitcoinUiTransaction = require('../../utils/BitcoinUiTransaction/BitcoinUiTransaction.cjs');
|
|
22
23
|
|
|
@@ -161,26 +162,65 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
161
162
|
});
|
|
162
163
|
}
|
|
163
164
|
/**
|
|
164
|
-
* Finds the credential that owns
|
|
165
|
-
*
|
|
165
|
+
* Finds the credential that owns `address`, either as its primary (mainnet)
|
|
166
|
+
* address or as one of its network-tagged additional addresses.
|
|
166
167
|
*
|
|
167
168
|
* This connector is shared across every Bitcoin wallet of the user —
|
|
168
169
|
* `verifiedCredentials` holds them all — so the owning credential must be
|
|
169
|
-
* looked up by
|
|
170
|
-
*
|
|
170
|
+
* looked up by address. Reading `verifiedCredential` instead would always
|
|
171
|
+
* answer with the user's *first* wallet.
|
|
171
172
|
*/
|
|
172
|
-
|
|
173
|
+
findCredentialForAddress(address) {
|
|
173
174
|
var _a;
|
|
174
|
-
|
|
175
|
-
if (!activeAccountAddress) {
|
|
175
|
+
if (!address) {
|
|
176
176
|
return undefined;
|
|
177
177
|
}
|
|
178
178
|
return ((_a = this.verifiedCredentials) !== null && _a !== void 0 ? _a : []).find((credential) => {
|
|
179
179
|
var _a;
|
|
180
|
-
return (credential === null || credential === void 0 ? void 0 : credential.address) ===
|
|
181
|
-
((_a = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []).some((additionalAddress) => additionalAddress.address ===
|
|
180
|
+
return (credential === null || credential === void 0 ? void 0 : credential.address) === address ||
|
|
181
|
+
((_a = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []).some((additionalAddress) => additionalAddress.address === address);
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
|
+
/** @see findCredentialForAddress */
|
|
185
|
+
findCredentialForActiveAddress() {
|
|
186
|
+
return this.findCredentialForAddress(this.activeAccountAddress);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* True when `accountAddress` belongs to the same wallet as the currently
|
|
190
|
+
* active address — including when the active address is a *different*
|
|
191
|
+
* network-tagged encoding of that same wallet (e.g. the testnet address
|
|
192
|
+
* set by switchNetwork, compared against the wallet's primary mainnet
|
|
193
|
+
* address).
|
|
194
|
+
*
|
|
195
|
+
* Used by getWalletClientByAddress to avoid clobbering an explicit
|
|
196
|
+
* switchNetwork: callers like ensureActiveAccount re-assert the wallet's
|
|
197
|
+
* primary address before every send/sign, and setting activeAccountAddress
|
|
198
|
+
* back to that primary address would silently undo the network switch —
|
|
199
|
+
* UTXO/PSBT/broadcast calls would then run against the wrong (likely
|
|
200
|
+
* unfunded) encoding while getNetwork still reports the switched network.
|
|
201
|
+
*
|
|
202
|
+
* Compares the two resolved credential *objects* by reference rather than
|
|
203
|
+
* by id: both lookups run against the same verifiedCredentials array in
|
|
204
|
+
* the same synchronous call, so `.find()` returns the identical element
|
|
205
|
+
* for both addresses whenever they belong to the same wallet. Comparing
|
|
206
|
+
* `.id` instead would be wrong whenever accountAddress doesn't resolve to
|
|
207
|
+
* any known credential yet (e.g. a brand-new wallet not yet synced) —
|
|
208
|
+
* `undefined === undefined` would falsely read as "same wallet".
|
|
209
|
+
*/
|
|
210
|
+
isSameWalletAddress(accountAddress) {
|
|
211
|
+
if (!this.activeAccountAddress) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (accountAddress === this.activeAccountAddress) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
const activeCredential = this.findCredentialForActiveAddress();
|
|
218
|
+
if (!activeCredential) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
const candidateCredential = this.findCredentialForAddress(accountAddress);
|
|
222
|
+
return (Boolean(candidateCredential) && candidateCredential === activeCredential);
|
|
223
|
+
}
|
|
184
224
|
/**
|
|
185
225
|
* Wallet identity: keyshares/wallet records are keyed by the wallet's
|
|
186
226
|
* primary (mainnet) address. On testnet the active address is the same
|
|
@@ -302,7 +342,11 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
302
342
|
* Signs a Partially Signed Bitcoin Transaction (PSBT) for embedded wallets
|
|
303
343
|
*
|
|
304
344
|
* Embedded wallets only support PSBT format and automatically sign all inputs
|
|
305
|
-
* that belong to the wallet address
|
|
345
|
+
* that belong to the wallet address, unless `signingIndexes` narrows that set.
|
|
346
|
+
*
|
|
347
|
+
* The sighash type is read from the PSBT itself, falling back to
|
|
348
|
+
* SIGHASH_DEFAULT (0x00) for Taproot and SIGHASH_ALL (0x01) otherwise.
|
|
349
|
+
* `allowedSighash` restricts which declared types are accepted.
|
|
306
350
|
*
|
|
307
351
|
* @param request - The PSBT signing request. Only unsignedPsbtBase64 is required.
|
|
308
352
|
* @returns The signed (but not finalized) PSBT response
|
|
@@ -331,12 +375,14 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
331
375
|
accountAddress: walletIdentityAddress,
|
|
332
376
|
});
|
|
333
377
|
const signedTransaction = yield walletClient.signTransaction({
|
|
378
|
+
allowedSighash: request.allowedSighash,
|
|
334
379
|
authToken: (_d = this.getAuthToken) === null || _d === void 0 ? void 0 : _d.call(this),
|
|
335
380
|
elevatedAccessToken,
|
|
336
381
|
mfaToken,
|
|
337
382
|
password,
|
|
338
383
|
senderAddress: walletIdentityAddress,
|
|
339
384
|
signedSessionId,
|
|
385
|
+
signingIndexes: request.signingIndexes,
|
|
340
386
|
transaction: request.unsignedPsbtBase64,
|
|
341
387
|
});
|
|
342
388
|
return {
|
|
@@ -472,12 +518,18 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
472
518
|
});
|
|
473
519
|
}
|
|
474
520
|
/**
|
|
475
|
-
* Gets the wallet client for a specific account address and sets it as
|
|
521
|
+
* Gets the wallet client for a specific account address and sets it as
|
|
522
|
+
* active — unless `accountAddress` is just a different network-tagged
|
|
523
|
+
* encoding of the wallet that's already active (see isSameWalletAddress),
|
|
524
|
+
* in which case the current (possibly switchNetwork'd) active address is
|
|
525
|
+
* left untouched.
|
|
476
526
|
* @param accountAddress - The Bitcoin account address
|
|
477
527
|
* @returns The WaaS wallet client instance
|
|
478
528
|
*/
|
|
479
529
|
getWalletClientByAddress({ accountAddress }) {
|
|
480
|
-
this.
|
|
530
|
+
if (!this.isSameWalletAddress(accountAddress)) {
|
|
531
|
+
this.setActiveAccountAddress(accountAddress);
|
|
532
|
+
}
|
|
481
533
|
return this.getWaasWalletClient();
|
|
482
534
|
}
|
|
483
535
|
/**
|
|
@@ -497,7 +549,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
497
549
|
*/
|
|
498
550
|
validateActiveWallet(expectedAddress) {
|
|
499
551
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
500
|
-
var _a
|
|
552
|
+
var _a;
|
|
501
553
|
const walletClient = yield this.getWaasWalletClient();
|
|
502
554
|
const targetWallet = yield walletClient.getWallet({
|
|
503
555
|
accountAddress: expectedAddress,
|
|
@@ -511,8 +563,12 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
511
563
|
const isWalletActive = walletConnectorCore.isSameAddress(targetWallet.accountAddress, this.activeAccountAddress || '', this.connectedChain);
|
|
512
564
|
// The active address may be a per-network encoding of this same wallet
|
|
513
565
|
// (one key, network-tagged additional addresses). Resetting it here would
|
|
514
|
-
// silently un-switch the selected network.
|
|
515
|
-
|
|
566
|
+
// silently un-switch the selected network. Resolved via isSameWalletAddress
|
|
567
|
+
// (which looks up the credential that actually owns the active address)
|
|
568
|
+
// rather than `verifiedCredential`, which is always the user's *first*
|
|
569
|
+
// wallet — using it here would wrongly reset the address for any other
|
|
570
|
+
// wallet the user validates.
|
|
571
|
+
const isPerNetworkVariantOfWallet = this.isSameWalletAddress(targetWallet.accountAddress);
|
|
516
572
|
if (!isWalletActive && !isPerNetworkVariantOfWallet) {
|
|
517
573
|
this.activeAccountAddress = targetWallet.accountAddress;
|
|
518
574
|
}
|
|
@@ -665,7 +721,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
665
721
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
666
722
|
*/
|
|
667
723
|
parsePsbt(psbtInput) {
|
|
668
|
-
return PsbtParser.PsbtParser.parsePsbt(psbtInput);
|
|
724
|
+
return PsbtParser.PsbtParser.parsePsbt(psbtInput, this.getPsbtNetwork());
|
|
669
725
|
}
|
|
670
726
|
/**
|
|
671
727
|
* Parse a PSBT or finalized transaction from hex format
|
|
@@ -673,7 +729,7 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
673
729
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
674
730
|
*/
|
|
675
731
|
parseHexPsbt(psbtHex) {
|
|
676
|
-
return PsbtParser.PsbtParser.parseHexPsbt(psbtHex);
|
|
732
|
+
return PsbtParser.PsbtParser.parseHexPsbt(psbtHex, this.getPsbtNetwork());
|
|
677
733
|
}
|
|
678
734
|
/**
|
|
679
735
|
* Parse a PSBT or finalized transaction from base64 format
|
|
@@ -681,7 +737,17 @@ class DynamicWaasBitcoinConnector extends waas.withDynamicWaas(BitcoinWalletConn
|
|
|
681
737
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
682
738
|
*/
|
|
683
739
|
parseBase64Psbt(psbtBase64) {
|
|
684
|
-
return PsbtParser.PsbtParser.parseBase64Psbt(psbtBase64);
|
|
740
|
+
return PsbtParser.PsbtParser.parseBase64Psbt(psbtBase64, this.getPsbtNetwork());
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* bitcoinjs network for the address encoding the parsers should emit.
|
|
744
|
+
* Derived from the active address rather than activeNetworkId, which is
|
|
745
|
+
* in-memory only and can report mainnet while a testnet address is active.
|
|
746
|
+
*/
|
|
747
|
+
getPsbtNetwork() {
|
|
748
|
+
return this.activeAccountAddress
|
|
749
|
+
? getBitcoinNetwork.getBitcoinNetwork(this.activeAccountAddress)
|
|
750
|
+
: bitcoinjsLib.networks.bitcoin;
|
|
685
751
|
}
|
|
686
752
|
/**
|
|
687
753
|
* Gets the public key for the active account
|
|
@@ -104,7 +104,9 @@ declare const DynamicWaasBitcoinConnector_base: (abstract new (...args: any[]) =
|
|
|
104
104
|
deviceId: string;
|
|
105
105
|
jwt: string;
|
|
106
106
|
walletPassword: string;
|
|
107
|
-
ncwStorage: Record<string, string>;
|
|
107
|
+
ncwStorage: Record<string, string>; /**
|
|
108
|
+
* Gets the current Bitcoin network id (1 mainnet, 2 testnet)
|
|
109
|
+
*/
|
|
108
110
|
expectedAddresses: Record<string, string>;
|
|
109
111
|
coinTypes?: Record<string, number> | undefined;
|
|
110
112
|
password?: string | undefined;
|
|
@@ -189,6 +191,12 @@ declare const DynamicWaasBitcoinConnector_base: (abstract new (...args: any[]) =
|
|
|
189
191
|
getWalletRecoveryState({ accountAddress, }: {
|
|
190
192
|
accountAddress: string;
|
|
191
193
|
}): Promise<import("@dynamic-labs-wallet/browser-wallet-client").WalletRecoveryState>;
|
|
194
|
+
createOfflineRecoveryShares({ accountAddress, password, cloudProviderAccessToken, mfaToken, }: {
|
|
195
|
+
accountAddress: string;
|
|
196
|
+
password?: string | undefined;
|
|
197
|
+
cloudProviderAccessToken?: string | undefined;
|
|
198
|
+
mfaToken?: string | undefined;
|
|
199
|
+
}): Promise<void>;
|
|
192
200
|
endSession(reason?: import("@dynamic-labs-sdk/client").LogoutReason | undefined): Promise<void>;
|
|
193
201
|
getActiveAccountAddress(): Promise<string | undefined>;
|
|
194
202
|
getConnectedAccounts(): Promise<string[]>;
|
|
@@ -295,15 +303,40 @@ export declare class DynamicWaasBitcoinConnector extends DynamicWaasBitcoinConne
|
|
|
295
303
|
*/
|
|
296
304
|
getBalance(address: string): Promise<string | undefined>;
|
|
297
305
|
/**
|
|
298
|
-
* Finds the credential that owns
|
|
299
|
-
*
|
|
306
|
+
* Finds the credential that owns `address`, either as its primary (mainnet)
|
|
307
|
+
* address or as one of its network-tagged additional addresses.
|
|
300
308
|
*
|
|
301
309
|
* This connector is shared across every Bitcoin wallet of the user —
|
|
302
310
|
* `verifiedCredentials` holds them all — so the owning credential must be
|
|
303
|
-
* looked up by
|
|
304
|
-
*
|
|
311
|
+
* looked up by address. Reading `verifiedCredential` instead would always
|
|
312
|
+
* answer with the user's *first* wallet.
|
|
305
313
|
*/
|
|
314
|
+
private findCredentialForAddress;
|
|
315
|
+
/** @see findCredentialForAddress */
|
|
306
316
|
private findCredentialForActiveAddress;
|
|
317
|
+
/**
|
|
318
|
+
* True when `accountAddress` belongs to the same wallet as the currently
|
|
319
|
+
* active address — including when the active address is a *different*
|
|
320
|
+
* network-tagged encoding of that same wallet (e.g. the testnet address
|
|
321
|
+
* set by switchNetwork, compared against the wallet's primary mainnet
|
|
322
|
+
* address).
|
|
323
|
+
*
|
|
324
|
+
* Used by getWalletClientByAddress to avoid clobbering an explicit
|
|
325
|
+
* switchNetwork: callers like ensureActiveAccount re-assert the wallet's
|
|
326
|
+
* primary address before every send/sign, and setting activeAccountAddress
|
|
327
|
+
* back to that primary address would silently undo the network switch —
|
|
328
|
+
* UTXO/PSBT/broadcast calls would then run against the wrong (likely
|
|
329
|
+
* unfunded) encoding while getNetwork still reports the switched network.
|
|
330
|
+
*
|
|
331
|
+
* Compares the two resolved credential *objects* by reference rather than
|
|
332
|
+
* by id: both lookups run against the same verifiedCredentials array in
|
|
333
|
+
* the same synchronous call, so `.find()` returns the identical element
|
|
334
|
+
* for both addresses whenever they belong to the same wallet. Comparing
|
|
335
|
+
* `.id` instead would be wrong whenever accountAddress doesn't resolve to
|
|
336
|
+
* any known credential yet (e.g. a brand-new wallet not yet synced) —
|
|
337
|
+
* `undefined === undefined` would falsely read as "same wallet".
|
|
338
|
+
*/
|
|
339
|
+
private isSameWalletAddress;
|
|
307
340
|
/**
|
|
308
341
|
* Wallet identity: keyshares/wallet records are keyed by the wallet's
|
|
309
342
|
* primary (mainnet) address. On testnet the active address is the same
|
|
@@ -354,7 +387,11 @@ export declare class DynamicWaasBitcoinConnector extends DynamicWaasBitcoinConne
|
|
|
354
387
|
* Signs a Partially Signed Bitcoin Transaction (PSBT) for embedded wallets
|
|
355
388
|
*
|
|
356
389
|
* Embedded wallets only support PSBT format and automatically sign all inputs
|
|
357
|
-
* that belong to the wallet address
|
|
390
|
+
* that belong to the wallet address, unless `signingIndexes` narrows that set.
|
|
391
|
+
*
|
|
392
|
+
* The sighash type is read from the PSBT itself, falling back to
|
|
393
|
+
* SIGHASH_DEFAULT (0x00) for Taproot and SIGHASH_ALL (0x01) otherwise.
|
|
394
|
+
* `allowedSighash` restricts which declared types are accepted.
|
|
358
395
|
*
|
|
359
396
|
* @param request - The PSBT signing request. Only unsignedPsbtBase64 is required.
|
|
360
397
|
* @returns The signed (but not finalized) PSBT response
|
|
@@ -414,7 +451,11 @@ export declare class DynamicWaasBitcoinConnector extends DynamicWaasBitcoinConne
|
|
|
414
451
|
legacyWalletId?: string;
|
|
415
452
|
}): Promise<void>;
|
|
416
453
|
/**
|
|
417
|
-
* Gets the wallet client for a specific account address and sets it as
|
|
454
|
+
* Gets the wallet client for a specific account address and sets it as
|
|
455
|
+
* active — unless `accountAddress` is just a different network-tagged
|
|
456
|
+
* encoding of the wallet that's already active (see isSameWalletAddress),
|
|
457
|
+
* in which case the current (possibly switchNetwork'd) active address is
|
|
458
|
+
* left untouched.
|
|
418
459
|
* @param accountAddress - The Bitcoin account address
|
|
419
460
|
* @returns The WaaS wallet client instance
|
|
420
461
|
*/
|
|
@@ -480,6 +521,12 @@ export declare class DynamicWaasBitcoinConnector extends DynamicWaasBitcoinConne
|
|
|
480
521
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
481
522
|
*/
|
|
482
523
|
parseBase64Psbt(psbtBase64: string): ParsedTransaction | null;
|
|
524
|
+
/**
|
|
525
|
+
* bitcoinjs network for the address encoding the parsers should emit.
|
|
526
|
+
* Derived from the active address rather than activeNetworkId, which is
|
|
527
|
+
* in-memory only and can report mainnet while a testnet address is active.
|
|
528
|
+
*/
|
|
529
|
+
private getPsbtNetwork;
|
|
483
530
|
/**
|
|
484
531
|
* Gets the public key for the active account
|
|
485
532
|
* Falls back to the account address if public key retrieval fails
|
|
@@ -13,6 +13,7 @@ import { BitcoinWalletConnector } from '../BitcoinWalletConnector.js';
|
|
|
13
13
|
import '@wallet-standard/app';
|
|
14
14
|
import 'jsontokens';
|
|
15
15
|
import { btcToSatoshis } from '../../utils/btcToSatoshis/btcToSatoshis.js';
|
|
16
|
+
import { getBitcoinNetwork } from '../../utils/getBitcoinNetwork/getBitcoinNetwork.js';
|
|
16
17
|
import { PsbtParser } from '../../utils/psbtParser/PsbtParser.js';
|
|
17
18
|
import { BitcoinUiTransaction } from '../../utils/BitcoinUiTransaction/BitcoinUiTransaction.js';
|
|
18
19
|
|
|
@@ -157,26 +158,65 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
/**
|
|
160
|
-
* Finds the credential that owns
|
|
161
|
-
*
|
|
161
|
+
* Finds the credential that owns `address`, either as its primary (mainnet)
|
|
162
|
+
* address or as one of its network-tagged additional addresses.
|
|
162
163
|
*
|
|
163
164
|
* This connector is shared across every Bitcoin wallet of the user —
|
|
164
165
|
* `verifiedCredentials` holds them all — so the owning credential must be
|
|
165
|
-
* looked up by
|
|
166
|
-
*
|
|
166
|
+
* looked up by address. Reading `verifiedCredential` instead would always
|
|
167
|
+
* answer with the user's *first* wallet.
|
|
167
168
|
*/
|
|
168
|
-
|
|
169
|
+
findCredentialForAddress(address) {
|
|
169
170
|
var _a;
|
|
170
|
-
|
|
171
|
-
if (!activeAccountAddress) {
|
|
171
|
+
if (!address) {
|
|
172
172
|
return undefined;
|
|
173
173
|
}
|
|
174
174
|
return ((_a = this.verifiedCredentials) !== null && _a !== void 0 ? _a : []).find((credential) => {
|
|
175
175
|
var _a;
|
|
176
|
-
return (credential === null || credential === void 0 ? void 0 : credential.address) ===
|
|
177
|
-
((_a = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []).some((additionalAddress) => additionalAddress.address ===
|
|
176
|
+
return (credential === null || credential === void 0 ? void 0 : credential.address) === address ||
|
|
177
|
+
((_a = credential === null || credential === void 0 ? void 0 : credential.walletAdditionalAddresses) !== null && _a !== void 0 ? _a : []).some((additionalAddress) => additionalAddress.address === address);
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
|
+
/** @see findCredentialForAddress */
|
|
181
|
+
findCredentialForActiveAddress() {
|
|
182
|
+
return this.findCredentialForAddress(this.activeAccountAddress);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* True when `accountAddress` belongs to the same wallet as the currently
|
|
186
|
+
* active address — including when the active address is a *different*
|
|
187
|
+
* network-tagged encoding of that same wallet (e.g. the testnet address
|
|
188
|
+
* set by switchNetwork, compared against the wallet's primary mainnet
|
|
189
|
+
* address).
|
|
190
|
+
*
|
|
191
|
+
* Used by getWalletClientByAddress to avoid clobbering an explicit
|
|
192
|
+
* switchNetwork: callers like ensureActiveAccount re-assert the wallet's
|
|
193
|
+
* primary address before every send/sign, and setting activeAccountAddress
|
|
194
|
+
* back to that primary address would silently undo the network switch —
|
|
195
|
+
* UTXO/PSBT/broadcast calls would then run against the wrong (likely
|
|
196
|
+
* unfunded) encoding while getNetwork still reports the switched network.
|
|
197
|
+
*
|
|
198
|
+
* Compares the two resolved credential *objects* by reference rather than
|
|
199
|
+
* by id: both lookups run against the same verifiedCredentials array in
|
|
200
|
+
* the same synchronous call, so `.find()` returns the identical element
|
|
201
|
+
* for both addresses whenever they belong to the same wallet. Comparing
|
|
202
|
+
* `.id` instead would be wrong whenever accountAddress doesn't resolve to
|
|
203
|
+
* any known credential yet (e.g. a brand-new wallet not yet synced) —
|
|
204
|
+
* `undefined === undefined` would falsely read as "same wallet".
|
|
205
|
+
*/
|
|
206
|
+
isSameWalletAddress(accountAddress) {
|
|
207
|
+
if (!this.activeAccountAddress) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
if (accountAddress === this.activeAccountAddress) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
const activeCredential = this.findCredentialForActiveAddress();
|
|
214
|
+
if (!activeCredential) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const candidateCredential = this.findCredentialForAddress(accountAddress);
|
|
218
|
+
return (Boolean(candidateCredential) && candidateCredential === activeCredential);
|
|
219
|
+
}
|
|
180
220
|
/**
|
|
181
221
|
* Wallet identity: keyshares/wallet records are keyed by the wallet's
|
|
182
222
|
* primary (mainnet) address. On testnet the active address is the same
|
|
@@ -298,7 +338,11 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
298
338
|
* Signs a Partially Signed Bitcoin Transaction (PSBT) for embedded wallets
|
|
299
339
|
*
|
|
300
340
|
* Embedded wallets only support PSBT format and automatically sign all inputs
|
|
301
|
-
* that belong to the wallet address
|
|
341
|
+
* that belong to the wallet address, unless `signingIndexes` narrows that set.
|
|
342
|
+
*
|
|
343
|
+
* The sighash type is read from the PSBT itself, falling back to
|
|
344
|
+
* SIGHASH_DEFAULT (0x00) for Taproot and SIGHASH_ALL (0x01) otherwise.
|
|
345
|
+
* `allowedSighash` restricts which declared types are accepted.
|
|
302
346
|
*
|
|
303
347
|
* @param request - The PSBT signing request. Only unsignedPsbtBase64 is required.
|
|
304
348
|
* @returns The signed (but not finalized) PSBT response
|
|
@@ -327,12 +371,14 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
327
371
|
accountAddress: walletIdentityAddress,
|
|
328
372
|
});
|
|
329
373
|
const signedTransaction = yield walletClient.signTransaction({
|
|
374
|
+
allowedSighash: request.allowedSighash,
|
|
330
375
|
authToken: (_d = this.getAuthToken) === null || _d === void 0 ? void 0 : _d.call(this),
|
|
331
376
|
elevatedAccessToken,
|
|
332
377
|
mfaToken,
|
|
333
378
|
password,
|
|
334
379
|
senderAddress: walletIdentityAddress,
|
|
335
380
|
signedSessionId,
|
|
381
|
+
signingIndexes: request.signingIndexes,
|
|
336
382
|
transaction: request.unsignedPsbtBase64,
|
|
337
383
|
});
|
|
338
384
|
return {
|
|
@@ -468,12 +514,18 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
468
514
|
});
|
|
469
515
|
}
|
|
470
516
|
/**
|
|
471
|
-
* Gets the wallet client for a specific account address and sets it as
|
|
517
|
+
* Gets the wallet client for a specific account address and sets it as
|
|
518
|
+
* active — unless `accountAddress` is just a different network-tagged
|
|
519
|
+
* encoding of the wallet that's already active (see isSameWalletAddress),
|
|
520
|
+
* in which case the current (possibly switchNetwork'd) active address is
|
|
521
|
+
* left untouched.
|
|
472
522
|
* @param accountAddress - The Bitcoin account address
|
|
473
523
|
* @returns The WaaS wallet client instance
|
|
474
524
|
*/
|
|
475
525
|
getWalletClientByAddress({ accountAddress }) {
|
|
476
|
-
this.
|
|
526
|
+
if (!this.isSameWalletAddress(accountAddress)) {
|
|
527
|
+
this.setActiveAccountAddress(accountAddress);
|
|
528
|
+
}
|
|
477
529
|
return this.getWaasWalletClient();
|
|
478
530
|
}
|
|
479
531
|
/**
|
|
@@ -493,7 +545,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
493
545
|
*/
|
|
494
546
|
validateActiveWallet(expectedAddress) {
|
|
495
547
|
return __awaiter(this, void 0, void 0, function* () {
|
|
496
|
-
var _a
|
|
548
|
+
var _a;
|
|
497
549
|
const walletClient = yield this.getWaasWalletClient();
|
|
498
550
|
const targetWallet = yield walletClient.getWallet({
|
|
499
551
|
accountAddress: expectedAddress,
|
|
@@ -507,8 +559,12 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
507
559
|
const isWalletActive = isSameAddress(targetWallet.accountAddress, this.activeAccountAddress || '', this.connectedChain);
|
|
508
560
|
// The active address may be a per-network encoding of this same wallet
|
|
509
561
|
// (one key, network-tagged additional addresses). Resetting it here would
|
|
510
|
-
// silently un-switch the selected network.
|
|
511
|
-
|
|
562
|
+
// silently un-switch the selected network. Resolved via isSameWalletAddress
|
|
563
|
+
// (which looks up the credential that actually owns the active address)
|
|
564
|
+
// rather than `verifiedCredential`, which is always the user's *first*
|
|
565
|
+
// wallet — using it here would wrongly reset the address for any other
|
|
566
|
+
// wallet the user validates.
|
|
567
|
+
const isPerNetworkVariantOfWallet = this.isSameWalletAddress(targetWallet.accountAddress);
|
|
512
568
|
if (!isWalletActive && !isPerNetworkVariantOfWallet) {
|
|
513
569
|
this.activeAccountAddress = targetWallet.accountAddress;
|
|
514
570
|
}
|
|
@@ -661,7 +717,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
661
717
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
662
718
|
*/
|
|
663
719
|
parsePsbt(psbtInput) {
|
|
664
|
-
return PsbtParser.parsePsbt(psbtInput);
|
|
720
|
+
return PsbtParser.parsePsbt(psbtInput, this.getPsbtNetwork());
|
|
665
721
|
}
|
|
666
722
|
/**
|
|
667
723
|
* Parse a PSBT or finalized transaction from hex format
|
|
@@ -669,7 +725,7 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
669
725
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
670
726
|
*/
|
|
671
727
|
parseHexPsbt(psbtHex) {
|
|
672
|
-
return PsbtParser.parseHexPsbt(psbtHex);
|
|
728
|
+
return PsbtParser.parseHexPsbt(psbtHex, this.getPsbtNetwork());
|
|
673
729
|
}
|
|
674
730
|
/**
|
|
675
731
|
* Parse a PSBT or finalized transaction from base64 format
|
|
@@ -677,7 +733,17 @@ class DynamicWaasBitcoinConnector extends withDynamicWaas(BitcoinWalletConnector
|
|
|
677
733
|
* @returns Parsed transaction data with inputs and outputs, or null if parsing fails
|
|
678
734
|
*/
|
|
679
735
|
parseBase64Psbt(psbtBase64) {
|
|
680
|
-
return PsbtParser.parseBase64Psbt(psbtBase64);
|
|
736
|
+
return PsbtParser.parseBase64Psbt(psbtBase64, this.getPsbtNetwork());
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* bitcoinjs network for the address encoding the parsers should emit.
|
|
740
|
+
* Derived from the active address rather than activeNetworkId, which is
|
|
741
|
+
* in-memory only and can report mainnet while a testnet address is active.
|
|
742
|
+
*/
|
|
743
|
+
getPsbtNetwork() {
|
|
744
|
+
return this.activeAccountAddress
|
|
745
|
+
? getBitcoinNetwork(this.activeAccountAddress)
|
|
746
|
+
: networks.bitcoin;
|
|
681
747
|
}
|
|
682
748
|
/**
|
|
683
749
|
* Gets the public key for the active account
|
package/src/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { OkxConnector } from './connectors';
|
|
1
|
+
import { OkxConnector, DynamicWaasBitcoinConnector } from './connectors';
|
|
2
2
|
export { BitcoinWalletConnector, type BitcoinWalletConnectorOpts, } from './connectors';
|
|
3
3
|
export type { BitcoinTransaction, BitcoinSignProtocol, BitcoinSignPsbtRequest, BitcoinSignPsbtResponse, BitcoinWalletStandardMethods, SignPsbtOptions, EmbeddedWalletSignPsbtRequest, } from './types';
|
|
4
4
|
export * from './utils';
|
|
5
5
|
export * from './wallet';
|
|
6
6
|
export { UnisatConnector, BitcoinSatsConnectConnector, DynamicWaasBitcoinConnector, } from './connectors';
|
|
7
|
-
export declare const BitcoinWalletConnectors: (props: any) => (import("dist/packages/wallet-connector-core/src").WalletConnectorConstructor | typeof OkxConnector)[];
|
|
7
|
+
export declare const BitcoinWalletConnectors: (props: any) => (import("dist/packages/wallet-connector-core/src").WalletConnectorConstructor | typeof OkxConnector | typeof DynamicWaasBitcoinConnector)[];
|
|
8
8
|
export { BitcoinProviderHelper } from './bitcoinProviderHelper';
|
|
9
9
|
export { DUST_LIMIT, HTTP_STATUS_NOT_FOUND, HTTP_STATUS_TOO_MANY_REQUESTS, } from './const';
|
package/src/types.d.ts
CHANGED
|
@@ -67,6 +67,19 @@ export type EmbeddedWalletSignPsbtRequest = {
|
|
|
67
67
|
* Embedded wallets only support PSBT format, not raw transaction hex.
|
|
68
68
|
*/
|
|
69
69
|
unsignedPsbtBase64: string;
|
|
70
|
+
/**
|
|
71
|
+
* Optional subset of PSBT input indexes to sign. When provided, only these
|
|
72
|
+
* indexes are signed (they must still belong to the wallet's own address).
|
|
73
|
+
* When omitted, behavior is unchanged: all wallet-owned inputs are signed.
|
|
74
|
+
*/
|
|
75
|
+
signingIndexes?: number[];
|
|
76
|
+
/**
|
|
77
|
+
* Optional allow-list of sighash types. The sighash type itself is read from
|
|
78
|
+
* the PSBT (BIP-174 `PSBT_IN_SIGHASH_TYPE`) — this only restricts which
|
|
79
|
+
* declared types are accepted, and signing throws if an input declares one
|
|
80
|
+
* outside the list. When omitted, any declared type is signed.
|
|
81
|
+
*/
|
|
82
|
+
allowedSighash?: number[];
|
|
70
83
|
};
|
|
71
84
|
export type SatsConnectSignTransactionInput = {
|
|
72
85
|
message?: string;
|
|
@@ -13,16 +13,19 @@ class PsbtParser {
|
|
|
13
13
|
* Parse a PSBT or finalized transaction and extract transaction information
|
|
14
14
|
* Auto-detects format (hex/base64) and type (PSBT/finalized transaction)
|
|
15
15
|
* @param psbtInput - The PSBT or finalized transaction in Base64 or hex format
|
|
16
|
+
* @param network - Network used to encode the extracted addresses. Scripts are
|
|
17
|
+
* network-agnostic, so passing the wrong one silently renders a valid address
|
|
18
|
+
* under the wrong prefix (a testnet output shown as `bc1…`).
|
|
16
19
|
* @returns Parsed transaction data with inputs and outputs
|
|
17
20
|
* @throws Error if parsing fails
|
|
18
21
|
*/
|
|
19
|
-
static parsePsbt(psbtInput) {
|
|
22
|
+
static parsePsbt(psbtInput, network = bitcoinjsLib.networks.bitcoin) {
|
|
20
23
|
const isHex = /^[0-9a-fA-F]+$/.test(psbtInput) && psbtInput.length > 100;
|
|
21
24
|
if (isHex) {
|
|
22
|
-
return PsbtParser.parseHexPsbt(psbtInput);
|
|
25
|
+
return PsbtParser.parseHexPsbt(psbtInput, network);
|
|
23
26
|
}
|
|
24
27
|
else {
|
|
25
|
-
return PsbtParser.parseBase64Psbt(psbtInput);
|
|
28
|
+
return PsbtParser.parseBase64Psbt(psbtInput, network);
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
@@ -31,14 +34,14 @@ class PsbtParser {
|
|
|
31
34
|
* @returns Parsed transaction data with inputs and outputs
|
|
32
35
|
* @throws Error if parsing fails
|
|
33
36
|
*/
|
|
34
|
-
static parseHexPsbt(psbtHex) {
|
|
37
|
+
static parseHexPsbt(psbtHex, network = bitcoinjsLib.networks.bitcoin) {
|
|
35
38
|
try {
|
|
36
39
|
try {
|
|
37
|
-
const psbt = bitcoinjsLib.Psbt.fromHex(psbtHex, { network
|
|
38
|
-
return PsbtParser.extractPsbtData(psbt);
|
|
40
|
+
const psbt = bitcoinjsLib.Psbt.fromHex(psbtHex, { network });
|
|
41
|
+
return PsbtParser.extractPsbtData(psbt, network);
|
|
39
42
|
}
|
|
40
43
|
catch (psbtError) {
|
|
41
|
-
return PsbtParser.parseFinalizedTransactionHex(psbtHex);
|
|
44
|
+
return PsbtParser.parseFinalizedTransactionHex(psbtHex, network);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
catch (error) {
|
|
@@ -51,14 +54,14 @@ class PsbtParser {
|
|
|
51
54
|
* @returns Parsed transaction data with inputs and outputs
|
|
52
55
|
* @throws Error if parsing fails
|
|
53
56
|
*/
|
|
54
|
-
static parseBase64Psbt(psbtBase64) {
|
|
57
|
+
static parseBase64Psbt(psbtBase64, network = bitcoinjsLib.networks.bitcoin) {
|
|
55
58
|
try {
|
|
56
59
|
try {
|
|
57
|
-
const psbt = bitcoinjsLib.Psbt.fromBase64(psbtBase64, { network
|
|
58
|
-
return PsbtParser.extractPsbtData(psbt);
|
|
60
|
+
const psbt = bitcoinjsLib.Psbt.fromBase64(psbtBase64, { network });
|
|
61
|
+
return PsbtParser.extractPsbtData(psbt, network);
|
|
59
62
|
}
|
|
60
63
|
catch (psbtError) {
|
|
61
|
-
return PsbtParser.parseFinalizedTransactionBase64(psbtBase64);
|
|
64
|
+
return PsbtParser.parseFinalizedTransactionBase64(psbtBase64, network);
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
catch (error) {
|
|
@@ -71,7 +74,7 @@ class PsbtParser {
|
|
|
71
74
|
* @param psbt - The parsed PSBT object
|
|
72
75
|
* @returns Parsed transaction data with inputs and outputs
|
|
73
76
|
*/
|
|
74
|
-
static extractPsbtData(psbt) {
|
|
77
|
+
static extractPsbtData(psbt, network) {
|
|
75
78
|
const inputs = [];
|
|
76
79
|
const outputs = [];
|
|
77
80
|
psbt.txInputs.forEach((txInput, index) => {
|
|
@@ -83,7 +86,7 @@ class PsbtParser {
|
|
|
83
86
|
if (input.witnessUtxo) {
|
|
84
87
|
inputData.value = input.witnessUtxo.value;
|
|
85
88
|
try {
|
|
86
|
-
inputData.address = bitcoinjsLib.address.fromOutputScript(input.witnessUtxo.script,
|
|
89
|
+
inputData.address = bitcoinjsLib.address.fromOutputScript(input.witnessUtxo.script, network);
|
|
87
90
|
}
|
|
88
91
|
catch (e) {
|
|
89
92
|
// Address extraction failed
|
|
@@ -95,7 +98,7 @@ class PsbtParser {
|
|
|
95
98
|
const prevOut = prevTx.outs[txInput.index];
|
|
96
99
|
inputData.value = prevOut.value;
|
|
97
100
|
try {
|
|
98
|
-
inputData.address = bitcoinjsLib.address.fromOutputScript(prevOut.script,
|
|
101
|
+
inputData.address = bitcoinjsLib.address.fromOutputScript(prevOut.script, network);
|
|
99
102
|
}
|
|
100
103
|
catch (e) {
|
|
101
104
|
// Address extraction failed
|
|
@@ -107,7 +110,7 @@ class PsbtParser {
|
|
|
107
110
|
}
|
|
108
111
|
inputs.push(inputData);
|
|
109
112
|
});
|
|
110
|
-
PsbtParser.extractOutputs(psbt.txOutputs, outputs);
|
|
113
|
+
PsbtParser.extractOutputs(psbt.txOutputs, outputs, network);
|
|
111
114
|
return { inputs, outputs };
|
|
112
115
|
}
|
|
113
116
|
/**
|
|
@@ -116,13 +119,13 @@ class PsbtParser {
|
|
|
116
119
|
* @param txOutputs - Array of transaction outputs (from PSBT or Transaction)
|
|
117
120
|
* @param outputs - Array to populate with parsed output data
|
|
118
121
|
*/
|
|
119
|
-
static extractOutputs(txOutputs, outputs) {
|
|
122
|
+
static extractOutputs(txOutputs, outputs, network) {
|
|
120
123
|
txOutputs.forEach((txOutput) => {
|
|
121
124
|
const outputData = {
|
|
122
125
|
value: txOutput.value,
|
|
123
126
|
};
|
|
124
127
|
try {
|
|
125
|
-
outputData.address = bitcoinjsLib.address.fromOutputScript(txOutput.script,
|
|
128
|
+
outputData.address = bitcoinjsLib.address.fromOutputScript(txOutput.script, network);
|
|
126
129
|
}
|
|
127
130
|
catch (e) {
|
|
128
131
|
// Address extraction failed - continue without address
|
|
@@ -136,10 +139,10 @@ class PsbtParser {
|
|
|
136
139
|
* @returns Parsed transaction data with inputs and outputs
|
|
137
140
|
* @throws Error if parsing fails
|
|
138
141
|
*/
|
|
139
|
-
static parseFinalizedTransactionHex(txHex) {
|
|
142
|
+
static parseFinalizedTransactionHex(txHex, network) {
|
|
140
143
|
try {
|
|
141
144
|
const tx = bitcoinjsLib.Transaction.fromHex(txHex);
|
|
142
|
-
return PsbtParser.extractTransactionData(tx);
|
|
145
|
+
return PsbtParser.extractTransactionData(tx, network);
|
|
143
146
|
}
|
|
144
147
|
catch (error) {
|
|
145
148
|
throw new Error(`Failed to parse finalized transaction hex: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -151,11 +154,11 @@ class PsbtParser {
|
|
|
151
154
|
* @returns Parsed transaction data with inputs and outputs
|
|
152
155
|
* @throws Error if parsing fails
|
|
153
156
|
*/
|
|
154
|
-
static parseFinalizedTransactionBase64(txBase64) {
|
|
157
|
+
static parseFinalizedTransactionBase64(txBase64, network) {
|
|
155
158
|
try {
|
|
156
159
|
const buffer = Buffer.from(txBase64, 'base64');
|
|
157
160
|
const tx = bitcoinjsLib.Transaction.fromBuffer(buffer);
|
|
158
|
-
return PsbtParser.extractTransactionData(tx);
|
|
161
|
+
return PsbtParser.extractTransactionData(tx, network);
|
|
159
162
|
}
|
|
160
163
|
catch (error) {
|
|
161
164
|
throw new Error(`Failed to parse finalized transaction base64: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -167,7 +170,7 @@ class PsbtParser {
|
|
|
167
170
|
* @param tx - The parsed Transaction object
|
|
168
171
|
* @returns Parsed transaction data with inputs and outputs
|
|
169
172
|
*/
|
|
170
|
-
static extractTransactionData(tx) {
|
|
173
|
+
static extractTransactionData(tx, network) {
|
|
171
174
|
const inputs = [];
|
|
172
175
|
const outputs = [];
|
|
173
176
|
tx.ins.forEach((input) => {
|
|
@@ -177,7 +180,7 @@ class PsbtParser {
|
|
|
177
180
|
};
|
|
178
181
|
inputs.push(inputData);
|
|
179
182
|
});
|
|
180
|
-
PsbtParser.extractOutputs(tx.outs, outputs);
|
|
183
|
+
PsbtParser.extractOutputs(tx.outs, outputs, network);
|
|
181
184
|
return { inputs, outputs };
|
|
182
185
|
}
|
|
183
186
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Network } from 'bitcoinjs-lib';
|
|
1
2
|
import type { ParsedTransaction } from '../../types';
|
|
2
3
|
/**
|
|
3
4
|
* Utility class for parsing PSBTs and finalized Bitcoin transactions
|
|
@@ -7,24 +8,27 @@ export declare class PsbtParser {
|
|
|
7
8
|
* Parse a PSBT or finalized transaction and extract transaction information
|
|
8
9
|
* Auto-detects format (hex/base64) and type (PSBT/finalized transaction)
|
|
9
10
|
* @param psbtInput - The PSBT or finalized transaction in Base64 or hex format
|
|
11
|
+
* @param network - Network used to encode the extracted addresses. Scripts are
|
|
12
|
+
* network-agnostic, so passing the wrong one silently renders a valid address
|
|
13
|
+
* under the wrong prefix (a testnet output shown as `bc1…`).
|
|
10
14
|
* @returns Parsed transaction data with inputs and outputs
|
|
11
15
|
* @throws Error if parsing fails
|
|
12
16
|
*/
|
|
13
|
-
static parsePsbt(psbtInput: string): ParsedTransaction;
|
|
17
|
+
static parsePsbt(psbtInput: string, network?: Network): ParsedTransaction;
|
|
14
18
|
/**
|
|
15
19
|
* Parse a PSBT or finalized transaction from hex format
|
|
16
20
|
* @param psbtHex - The PSBT or finalized transaction in hex format
|
|
17
21
|
* @returns Parsed transaction data with inputs and outputs
|
|
18
22
|
* @throws Error if parsing fails
|
|
19
23
|
*/
|
|
20
|
-
static parseHexPsbt(psbtHex: string): ParsedTransaction;
|
|
24
|
+
static parseHexPsbt(psbtHex: string, network?: Network): ParsedTransaction;
|
|
21
25
|
/**
|
|
22
26
|
* Parse a PSBT or finalized transaction from base64 format
|
|
23
27
|
* @param psbtBase64 - The PSBT or finalized transaction in base64 format
|
|
24
28
|
* @returns Parsed transaction data with inputs and outputs
|
|
25
29
|
* @throws Error if parsing fails
|
|
26
30
|
*/
|
|
27
|
-
static parseBase64Psbt(psbtBase64: string): ParsedTransaction;
|
|
31
|
+
static parseBase64Psbt(psbtBase64: string, network?: Network): ParsedTransaction;
|
|
28
32
|
/**
|
|
29
33
|
* Extract transaction data from a PSBT
|
|
30
34
|
* Shared logic used by both parseHexPsbt and parseBase64Psbt
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import {
|
|
2
|
+
import { networks, Psbt, address, Transaction } from 'bitcoinjs-lib';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Utility class for parsing PSBTs and finalized Bitcoin transactions
|
|
@@ -9,16 +9,19 @@ class PsbtParser {
|
|
|
9
9
|
* Parse a PSBT or finalized transaction and extract transaction information
|
|
10
10
|
* Auto-detects format (hex/base64) and type (PSBT/finalized transaction)
|
|
11
11
|
* @param psbtInput - The PSBT or finalized transaction in Base64 or hex format
|
|
12
|
+
* @param network - Network used to encode the extracted addresses. Scripts are
|
|
13
|
+
* network-agnostic, so passing the wrong one silently renders a valid address
|
|
14
|
+
* under the wrong prefix (a testnet output shown as `bc1…`).
|
|
12
15
|
* @returns Parsed transaction data with inputs and outputs
|
|
13
16
|
* @throws Error if parsing fails
|
|
14
17
|
*/
|
|
15
|
-
static parsePsbt(psbtInput) {
|
|
18
|
+
static parsePsbt(psbtInput, network = networks.bitcoin) {
|
|
16
19
|
const isHex = /^[0-9a-fA-F]+$/.test(psbtInput) && psbtInput.length > 100;
|
|
17
20
|
if (isHex) {
|
|
18
|
-
return PsbtParser.parseHexPsbt(psbtInput);
|
|
21
|
+
return PsbtParser.parseHexPsbt(psbtInput, network);
|
|
19
22
|
}
|
|
20
23
|
else {
|
|
21
|
-
return PsbtParser.parseBase64Psbt(psbtInput);
|
|
24
|
+
return PsbtParser.parseBase64Psbt(psbtInput, network);
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
@@ -27,14 +30,14 @@ class PsbtParser {
|
|
|
27
30
|
* @returns Parsed transaction data with inputs and outputs
|
|
28
31
|
* @throws Error if parsing fails
|
|
29
32
|
*/
|
|
30
|
-
static parseHexPsbt(psbtHex) {
|
|
33
|
+
static parseHexPsbt(psbtHex, network = networks.bitcoin) {
|
|
31
34
|
try {
|
|
32
35
|
try {
|
|
33
|
-
const psbt = Psbt.fromHex(psbtHex, { network
|
|
34
|
-
return PsbtParser.extractPsbtData(psbt);
|
|
36
|
+
const psbt = Psbt.fromHex(psbtHex, { network });
|
|
37
|
+
return PsbtParser.extractPsbtData(psbt, network);
|
|
35
38
|
}
|
|
36
39
|
catch (psbtError) {
|
|
37
|
-
return PsbtParser.parseFinalizedTransactionHex(psbtHex);
|
|
40
|
+
return PsbtParser.parseFinalizedTransactionHex(psbtHex, network);
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
catch (error) {
|
|
@@ -47,14 +50,14 @@ class PsbtParser {
|
|
|
47
50
|
* @returns Parsed transaction data with inputs and outputs
|
|
48
51
|
* @throws Error if parsing fails
|
|
49
52
|
*/
|
|
50
|
-
static parseBase64Psbt(psbtBase64) {
|
|
53
|
+
static parseBase64Psbt(psbtBase64, network = networks.bitcoin) {
|
|
51
54
|
try {
|
|
52
55
|
try {
|
|
53
|
-
const psbt = Psbt.fromBase64(psbtBase64, { network
|
|
54
|
-
return PsbtParser.extractPsbtData(psbt);
|
|
56
|
+
const psbt = Psbt.fromBase64(psbtBase64, { network });
|
|
57
|
+
return PsbtParser.extractPsbtData(psbt, network);
|
|
55
58
|
}
|
|
56
59
|
catch (psbtError) {
|
|
57
|
-
return PsbtParser.parseFinalizedTransactionBase64(psbtBase64);
|
|
60
|
+
return PsbtParser.parseFinalizedTransactionBase64(psbtBase64, network);
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
catch (error) {
|
|
@@ -67,7 +70,7 @@ class PsbtParser {
|
|
|
67
70
|
* @param psbt - The parsed PSBT object
|
|
68
71
|
* @returns Parsed transaction data with inputs and outputs
|
|
69
72
|
*/
|
|
70
|
-
static extractPsbtData(psbt) {
|
|
73
|
+
static extractPsbtData(psbt, network) {
|
|
71
74
|
const inputs = [];
|
|
72
75
|
const outputs = [];
|
|
73
76
|
psbt.txInputs.forEach((txInput, index) => {
|
|
@@ -79,7 +82,7 @@ class PsbtParser {
|
|
|
79
82
|
if (input.witnessUtxo) {
|
|
80
83
|
inputData.value = input.witnessUtxo.value;
|
|
81
84
|
try {
|
|
82
|
-
inputData.address = address.fromOutputScript(input.witnessUtxo.script,
|
|
85
|
+
inputData.address = address.fromOutputScript(input.witnessUtxo.script, network);
|
|
83
86
|
}
|
|
84
87
|
catch (e) {
|
|
85
88
|
// Address extraction failed
|
|
@@ -91,7 +94,7 @@ class PsbtParser {
|
|
|
91
94
|
const prevOut = prevTx.outs[txInput.index];
|
|
92
95
|
inputData.value = prevOut.value;
|
|
93
96
|
try {
|
|
94
|
-
inputData.address = address.fromOutputScript(prevOut.script,
|
|
97
|
+
inputData.address = address.fromOutputScript(prevOut.script, network);
|
|
95
98
|
}
|
|
96
99
|
catch (e) {
|
|
97
100
|
// Address extraction failed
|
|
@@ -103,7 +106,7 @@ class PsbtParser {
|
|
|
103
106
|
}
|
|
104
107
|
inputs.push(inputData);
|
|
105
108
|
});
|
|
106
|
-
PsbtParser.extractOutputs(psbt.txOutputs, outputs);
|
|
109
|
+
PsbtParser.extractOutputs(psbt.txOutputs, outputs, network);
|
|
107
110
|
return { inputs, outputs };
|
|
108
111
|
}
|
|
109
112
|
/**
|
|
@@ -112,13 +115,13 @@ class PsbtParser {
|
|
|
112
115
|
* @param txOutputs - Array of transaction outputs (from PSBT or Transaction)
|
|
113
116
|
* @param outputs - Array to populate with parsed output data
|
|
114
117
|
*/
|
|
115
|
-
static extractOutputs(txOutputs, outputs) {
|
|
118
|
+
static extractOutputs(txOutputs, outputs, network) {
|
|
116
119
|
txOutputs.forEach((txOutput) => {
|
|
117
120
|
const outputData = {
|
|
118
121
|
value: txOutput.value,
|
|
119
122
|
};
|
|
120
123
|
try {
|
|
121
|
-
outputData.address = address.fromOutputScript(txOutput.script,
|
|
124
|
+
outputData.address = address.fromOutputScript(txOutput.script, network);
|
|
122
125
|
}
|
|
123
126
|
catch (e) {
|
|
124
127
|
// Address extraction failed - continue without address
|
|
@@ -132,10 +135,10 @@ class PsbtParser {
|
|
|
132
135
|
* @returns Parsed transaction data with inputs and outputs
|
|
133
136
|
* @throws Error if parsing fails
|
|
134
137
|
*/
|
|
135
|
-
static parseFinalizedTransactionHex(txHex) {
|
|
138
|
+
static parseFinalizedTransactionHex(txHex, network) {
|
|
136
139
|
try {
|
|
137
140
|
const tx = Transaction.fromHex(txHex);
|
|
138
|
-
return PsbtParser.extractTransactionData(tx);
|
|
141
|
+
return PsbtParser.extractTransactionData(tx, network);
|
|
139
142
|
}
|
|
140
143
|
catch (error) {
|
|
141
144
|
throw new Error(`Failed to parse finalized transaction hex: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -147,11 +150,11 @@ class PsbtParser {
|
|
|
147
150
|
* @returns Parsed transaction data with inputs and outputs
|
|
148
151
|
* @throws Error if parsing fails
|
|
149
152
|
*/
|
|
150
|
-
static parseFinalizedTransactionBase64(txBase64) {
|
|
153
|
+
static parseFinalizedTransactionBase64(txBase64, network) {
|
|
151
154
|
try {
|
|
152
155
|
const buffer = Buffer.from(txBase64, 'base64');
|
|
153
156
|
const tx = Transaction.fromBuffer(buffer);
|
|
154
|
-
return PsbtParser.extractTransactionData(tx);
|
|
157
|
+
return PsbtParser.extractTransactionData(tx, network);
|
|
155
158
|
}
|
|
156
159
|
catch (error) {
|
|
157
160
|
throw new Error(`Failed to parse finalized transaction base64: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -163,7 +166,7 @@ class PsbtParser {
|
|
|
163
166
|
* @param tx - The parsed Transaction object
|
|
164
167
|
* @returns Parsed transaction data with inputs and outputs
|
|
165
168
|
*/
|
|
166
|
-
static extractTransactionData(tx) {
|
|
169
|
+
static extractTransactionData(tx, network) {
|
|
167
170
|
const inputs = [];
|
|
168
171
|
const outputs = [];
|
|
169
172
|
tx.ins.forEach((input) => {
|
|
@@ -173,7 +176,7 @@ class PsbtParser {
|
|
|
173
176
|
};
|
|
174
177
|
inputs.push(inputData);
|
|
175
178
|
});
|
|
176
|
-
PsbtParser.extractOutputs(tx.outs, outputs);
|
|
179
|
+
PsbtParser.extractOutputs(tx.outs, outputs, network);
|
|
177
180
|
return { inputs, outputs };
|
|
178
181
|
}
|
|
179
182
|
}
|