@scure/btc-signer 0.5.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/README.md ADDED
@@ -0,0 +1,836 @@
1
+ # scure-btc-signer
2
+
3
+ Audited minimal library for creating, signing & decoding Bitcoin transactions.
4
+
5
+ - โœ๏ธ Create transactions, inputs, outputs, sign them
6
+ - ๐Ÿ“ก No network code: simplified audits and offline usage
7
+ - ๐ŸŽป Classic & SegWit: P2PK, P2PKH, P2WPKH, P2SH, P2WSH, P2MS
8
+ - ๐Ÿงช Schnorr & Taproot BIP340/BIP341: P2TR, P2TR-NS, P2TR-MS
9
+ - ๐Ÿ“จ BIP174 PSBT
10
+ - ๐Ÿ‘ฅ Multisig support
11
+ - ๐Ÿ”’ [**Audited**](#security) by an independent security firm
12
+ - ๐Ÿชถ ~2600 lines
13
+
14
+ Initial development has been funded by [Ryan Shea](https://shea.io). Check out [the demo](https://signerdemo.micro-btc.dev/) & [its github](https://github.com/shea256/micro-btc-web-demo).
15
+
16
+ ### This library belongs to _scure_
17
+
18
+ > **scure** โ€” secure, independently audited packages for every use case.
19
+
20
+ - All releases are signed with PGP keys
21
+ - Check out all libraries:
22
+ [base](https://github.com/paulmillr/scure-base),
23
+ [bip32](https://github.com/paulmillr/scure-bip32),
24
+ [bip39](https://github.com/paulmillr/scure-bip39),
25
+ [btc-signer](https://github.com/paulmillr/scure-btc-signer)
26
+
27
+ Also, check out all _web3 utility libraries:_ [micro-eth-signer](https://github.com/paulmillr/micro-eth-signer), [scure-btc-signer](https://github.com/paulmillr/scure-btc-signer), [micro-sol-signer](https://github.com/paulmillr/micro-sol-signer), [micro-web3](https://github.com/paulmillr/micro-web3), [tx-tor-broadcaster](https://github.com/paulmillr/tx-tor-broadcaster)
28
+
29
+ ## Usage
30
+
31
+ Use NPM for browser / node.js:
32
+
33
+ > npm install @scure/btc-signer
34
+
35
+ For [Deno](https://deno.land), use it with [npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers).
36
+
37
+ ```ts
38
+ import * as btc from '@scure/btc-signer';
39
+ // import * as btc from "npm:@scure/btc-signer@0.5.0"; // Deno
40
+ ```
41
+
42
+ ### Table of Contents
43
+
44
+ - [Payments](#payments)
45
+ - [P2PK Pay To Public Key](#p2pk-pay-to-public-key)
46
+ - [P2PKH Public Key Hash](#p2pkh-public-key-hash)
47
+ - [P2WPKH Witness Public Key Hash](#p2wpkh-witness-public-key-hash)
48
+ - [P2SH Script Hash](#p2sh-script-hash)
49
+ - [P2WSH Witness Script Hash](#p2wsh-witness-script-hash)
50
+ - [P2SH-P2WSH](#p2sh-p2wsh)
51
+ - [P2MS classic multisig](#p2ms-classic-multisig)
52
+ - [P2TR Taproot](#p2tr-taproot)
53
+ - [P2TR-NS Taproot multisig](#p2tr-ns-taproot-multisig)
54
+ - [P2TR-MS Taproot M-of-N multisig](#p2tr-ms-taproot-m-of-n-multisig)
55
+ - [Transaction](#transaction)
56
+ - [Encode/decode](#encodedecode)
57
+ - [Inputs](#inputs)
58
+ - [Outputs](#outputs)
59
+ - [Basic transaction sign](#basic-transaction-sign)
60
+ - [BIP174 PSBT multi-sig example](#bip174-psbt-multi-sig-example)
61
+ - [Utils](#utils)
62
+ - [getAddress](#getaddress)
63
+ - [WIF](#wif)
64
+ - [Script](#script)
65
+ - [OutScript](#outscript)
66
+
67
+ ## Payments
68
+
69
+ BTC has several UTXO types:
70
+
71
+ - P2PK: Legacy, from 2010
72
+ - P2PKH, P2SH, P2MS: Classic
73
+ - P2WPKH, P2WSH: classic, SegWit
74
+ - P2TR: Taproot, recommended
75
+
76
+ For test examples, the usage is as following:
77
+
78
+ ```sh
79
+ npm install @scure/btc-signer @scure/base assert
80
+ ```
81
+
82
+ ```ts
83
+ import * as btc from '@scure/btc-signer';
84
+ import { hex } from '@scure/base';
85
+ import { deepStrictEqual, throws } from 'assert';
86
+ ```
87
+
88
+ ### P2PK (Pay To Public Key)
89
+
90
+ Old script, doesn't have address at all. Should be wrapped in P2SH/P2WSH/P2SH-P2WSH.
91
+
92
+ ```ts
93
+ const uncompressed = hex.decode(
94
+ '04ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28'
95
+ );
96
+
97
+ deepStrictEqual(btc.p2pk(uncompressed), {
98
+ type: 'pk',
99
+ script: hex.decode(
100
+ '4104ad90e5b6bc86b3ec7fac2c5fbda7423fc8ef0d58df594c773fa05e2c281b2bfe877677c668bd13603944e34f4818ee03cadd81a88542b8b4d5431264180e2c28ac'
101
+ ),
102
+ });
103
+ ```
104
+
105
+ ### P2PKH (Public Key Hash)
106
+
107
+ Classic address (pre-SegWit)
108
+
109
+ ```ts
110
+ const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
111
+ deepStrictEqual(btc.p2pkh(PubKey), {
112
+ type: 'pkh',
113
+ address: '134D6gYy8DsR5m4416BnmgASuMBqKvogQh',
114
+ script: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
115
+ });
116
+ // P2SH-P2PKH
117
+ deepStrictEqual(btc.p2sh(btc.p2pkh(PubKey)), {
118
+ type: 'sh',
119
+ address: '3EPhLJ1FuR2noj6qrTs4YvepCvB6sbShoV',
120
+ script: hex.decode('a9148b530b962725af3bb7c818f197c619db3f71495087'),
121
+ redeemScript: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
122
+ });
123
+ // P2WSH-P2PKH
124
+ deepStrictEqual(btc.p2wsh(btc.p2pkh(PubKey)), {
125
+ type: 'wsh',
126
+ address: 'bc1qhxtthndg70cthfasy8y4qlk9h7r3006azn9md0fad5dg9hh76nkqaufnuz',
127
+ script: hex.decode('0020b996bbcda8f3f0bba7b021c9507ec5bf8717bf5d14cbb6bd3d6d1a82defed4ec'),
128
+ witnessScript: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
129
+ });
130
+ // P2SH-P2WSH-P2PKH
131
+ deepStrictEqual(btc.p2sh(btc.p2wsh(btc.p2pkh(PubKey))), {
132
+ type: 'sh',
133
+ address: '3EHxWHyLv5Seu5Cd6D1cH56jLKxSi3ps8C',
134
+ script: hex.decode('a9148a3d36fb710a9c7cae06cfcdf39792ff5773e8f187'),
135
+ redeemScript: hex.decode('0020b996bbcda8f3f0bba7b021c9507ec5bf8717bf5d14cbb6bd3d6d1a82defed4ec'),
136
+ witnessScript: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
137
+ });
138
+ ```
139
+
140
+ ### P2WPKH (Witness Public Key Hash)
141
+
142
+ Same as P2PKH, but for SegWit V0. Basic bech32 address.
143
+ Cannot be wrapped in P2WSH.
144
+
145
+ ```ts
146
+ const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
147
+ deepStrictEqual(btc.p2wpkh(PubKey), {
148
+ type: 'wpkh',
149
+ address: 'bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm',
150
+ script: hex.decode('0014168b992bcfc44050310b3a94bd0771136d0b28d1'),
151
+ });
152
+ // P2SH-P2WPKH
153
+ deepStrictEqual(btc.p2sh(btc.p2wpkh(PubKey)), {
154
+ type: 'sh',
155
+ address: '3BCuRViGCTXmQjyJ9zjeRUYrdZTUa38zjC',
156
+ script: hex.decode('a91468602f2db7b7d7cdcd2639ab6bf7f5bfe828e53f87'),
157
+ redeemScript: hex.decode('0014168b992bcfc44050310b3a94bd0771136d0b28d1'),
158
+ });
159
+ ```
160
+
161
+ ### P2SH (Script Hash)
162
+
163
+ Classic (pre-SegWit) script address. Useful for multisig and other smart-contracts.
164
+ Takes full output of other payments, not just script.
165
+
166
+ **_NOTE_**: redeemScript should be added to transaction input in order to spend.
167
+
168
+ ```ts
169
+ const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
170
+ // Wrap P2PKH in P2SH
171
+ deepStrictEqual(btc.p2sh(btc.p2pkh(PubKey)), {
172
+ type: 'sh',
173
+ address: '3EPhLJ1FuR2noj6qrTs4YvepCvB6sbShoV',
174
+ script: hex.decode('a9148b530b962725af3bb7c818f197c619db3f71495087'),
175
+ redeemScript: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
176
+ });
177
+ ```
178
+
179
+ ### P2WSH (Witness Script Hash)
180
+
181
+ Same as P2SH but for SegWit V0.
182
+
183
+ **_NOTE_**: witnessScript should be added to transaction input in order to spend.
184
+
185
+ ```ts
186
+ const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
187
+ deepStrictEqual(btc.p2wsh(btc.p2pkh(PubKey)), {
188
+ type: 'wsh',
189
+ address: 'bc1qhxtthndg70cthfasy8y4qlk9h7r3006azn9md0fad5dg9hh76nkqaufnuz',
190
+ script: hex.decode('0020b996bbcda8f3f0bba7b021c9507ec5bf8717bf5d14cbb6bd3d6d1a82defed4ec'),
191
+ witnessScript: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
192
+ });
193
+ ```
194
+
195
+ ### P2SH-P2WSH
196
+
197
+ Not really script type, but construction of P2WSH inside P2SH.
198
+
199
+ **_NOTE_**: both reedemScript and witnessScript should be added to transaction in order to spend.
200
+
201
+ ```ts
202
+ const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
203
+ deepStrictEqual(btc.p2sh(btc.p2wsh(btc.p2pkh(PubKey))), {
204
+ type: 'sh',
205
+ address: '3EHxWHyLv5Seu5Cd6D1cH56jLKxSi3ps8C',
206
+ script: hex.decode('a9148a3d36fb710a9c7cae06cfcdf39792ff5773e8f187'),
207
+ redeemScript: hex.decode('0020b996bbcda8f3f0bba7b021c9507ec5bf8717bf5d14cbb6bd3d6d1a82defed4ec'),
208
+ witnessScript: hex.decode('76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac'),
209
+ });
210
+ ```
211
+
212
+ ### P2MS (classic multisig)
213
+
214
+ Classic (pre-taproot) M-of-N Multisig, doesn't have an address, should be wrapped in P2SH/P2WSH/P2SH-P2WSH.
215
+
216
+ **_NOTE_**: By default we don't accept duplicate public keys, to avoid creating wrong multisig by mistake. However there is a flag: allowSamePubkeys, in case you really need that.
217
+ Valid use-case: `2-of-[A,A,B,C]`, can be signed by `A or (B and C)`.
218
+
219
+ ```ts
220
+ const PubKeys = [
221
+ hex.decode('030000000000000000000000000000000000000000000000000000000000000001'),
222
+ hex.decode('030000000000000000000000000000000000000000000000000000000000000002'),
223
+ hex.decode('030000000000000000000000000000000000000000000000000000000000000003'),
224
+ ];
225
+ // Multisig 2-of-3 wrapped in P2SH
226
+ deepStrictEqual(btc.p2sh(btc.p2ms(2, PubKeys)), {
227
+ type: 'sh',
228
+ address: '3G4AeQtzCLoDAyv2eb3UVTG5atfkyHtuRn',
229
+ script: hex.decode('a9149d91c6de4eacde72a7cc86bff98d1915b3c7818f87'),
230
+ redeemScript: hex.decode(
231
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
232
+ ),
233
+ });
234
+ // Multisig 2-of-3 wrapped in P2WSH
235
+ deepStrictEqual(btc.p2wsh(btc.p2ms(2, PubKeys)), {
236
+ type: 'wsh',
237
+ address: 'bc1qwnhzkn8wcyyrnfyfcp7555urssu5dq0rmnvg70hg02z3nxgg4f0qljmr2h',
238
+ script: hex.decode('002074ee2b4ceec10839a489c07d4a538384394681e3dcd88f3ee87a85199908aa5e'),
239
+ witnessScript: hex.decode(
240
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
241
+ ),
242
+ });
243
+ // Multisig 2-of-3 wrapped in P2SH-P2WSH
244
+ deepStrictEqual(btc.p2sh(btc.p2wsh(btc.p2ms(2, PubKeys))), {
245
+ type: 'sh',
246
+ address: '3HKWSo57kmcJZ3h43pXS3m5UESR4wXcWTd',
247
+ script: hex.decode('a914ab70ab84b12b891364b4b2a14ca813cac308b24287'),
248
+ redeemScript: hex.decode('002074ee2b4ceec10839a489c07d4a538384394681e3dcd88f3ee87a85199908aa5e'),
249
+ witnessScript: hex.decode(
250
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
251
+ ),
252
+ });
253
+ // Useful util: wraps P2MS in P2SH or P2WSH
254
+ deepStrictEqual(btc.p2sh(btc.p2ms(2, PubKeys)), btc.multisig(2, PubKeys));
255
+ deepStrictEqual(btc.p2wsh(btc.p2ms(2, PubKeys)), btc.multisig(2, PubKeys, undefined, true));
256
+ // Sorted multisig (BIP67)
257
+ deepStrictEqual(btc.p2sh(btc.p2ms(2, PubKeys)), btc.sortedMultisig(2, PubKeys));
258
+ deepStrictEqual(btc.p2wsh(btc.p2ms(2, PubKeys)), btc.sortedMultisig(2, PubKeys, true));
259
+ ```
260
+
261
+ ### P2TR (Taproot)
262
+
263
+ TapRoot (SegWit V1) script which replaces both public key and script types from previous versions.
264
+
265
+ **_NOTE_**: it takes `p2tr(PubKey?, ScriptTree?)` and works as PubKey OR ScriptTree, which means
266
+ if you use any spendable PubKey and ScriptTree of multi-sig, owner of private key for PubKey will
267
+ be able to spend output. If PubKey is undefined we use static unspendable PubKey by default, which leaks information about script type. However, any dynamic unspendable keys will require complex interaction
268
+ to sign multi-sig wallets, and there is no BIP/PSBT fields for that yet.
269
+
270
+ **_NOTE_**: tapInternalKey, tapMerkleRoot, tapLeafScript should be added to transaction input in order to spend.
271
+
272
+ ```ts
273
+ const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
274
+ // Key Path Spend (owned of private key for PubKey can spend)
275
+ deepStrictEqual(btc.p2tr(PubKey), {
276
+ type: 'tr',
277
+ address: 'bc1p7yu5dsly83jg5tkxcljsa30vnpdpl22wr6rty98t6x6p6ekz2gkqzf2t2s',
278
+ script: hex.decode('5120f13946c3e43c648a2ec6c7e50ec5ec985a1fa94e1e86b214ebd1b41d66c2522c'),
279
+ tweakedPubkey: hex.decode('f13946c3e43c648a2ec6c7e50ec5ec985a1fa94e1e86b214ebd1b41d66c2522c'),
280
+ tapInternalKey: hex.decode('0101010101010101010101010101010101010101010101010101010101010101'),
281
+ });
282
+
283
+ const clean = (x) => ({ type: x.type, address: x.address, script: hex.encode(x.script) });
284
+
285
+ const PubKey2 = hex.decode('0202020202020202020202020202020202020202020202020202020202020202');
286
+ const PubKey3 = hex.decode('1212121212121212121212121212121212121212121212121212121212121212');
287
+ // Nested P2TR, owner of private key for any of PubKeys can spend whole
288
+ // NOTE: by default P2TR expects binary tree, but btc.p2tr can build it if list of scripts passed.
289
+ // Also, you can include {weight: N} to scripts to create differently balanced tree.
290
+ deepStrictEqual(
291
+ clean(btc.p2tr(undefined, [btc.p2tr_pk(PubKey), btc.p2tr_pk(PubKey2), btc.p2tr_pk(PubKey3)])),
292
+ {
293
+ type: 'tr',
294
+ // weights for bitcoinjs-lib: [3,2,1]
295
+ address: 'bc1pj2uvajyygyu2zw0rg0d6yxdsc920kzc5pamfgtlqepe30za922cqjjmkta',
296
+ script: '512092b8cec8844138a139e343dba219b0c154fb0b140f76942fe0c873178ba552b0',
297
+ }
298
+ );
299
+ // If scriptsTree is already binary tree, it will be used as-is
300
+ deepStrictEqual(
301
+ clean(btc.p2tr(undefined, [btc.p2tr_pk(PubKey2), [btc.p2tr_pk(PubKey), btc.p2tr_pk(PubKey3)]])),
302
+ {
303
+ type: 'tr',
304
+ // default weights for bitcoinjs-lib
305
+ address: 'bc1pvue6sk9efyvcvpzzqkg8at4qy2u67zj7rj5sfsy573m7alxavqjqucc26a',
306
+ script: '51206733a858b9491986044205907eaea022b9af0a5e1ca904c094f477eefcdd6024',
307
+ }
308
+ );
309
+ ```
310
+
311
+ ### P2TR-NS (Taproot multisig)
312
+
313
+ Taproot N-of-N multisig (`[<PubKeys[0:n-1]> CHECKSIGVERIFY] <PubKeys[n-1]> CHECKSIG`).
314
+
315
+ **_NOTE_**: First arg is M, if M!=PubKeys.length, it will create a multi-leaf M-of-N taproot script tree.
316
+ This allows one to reveal only `M` PubKeys on spend, without any information about the others.
317
+ This is fast for cases like 15-of-20, but extremely slow for cases like 5-of-20.
318
+
319
+ **_NOTE_**: By default we don't accept duplicate public keys, to avoid creating the wrong multisig by mistake. However there is a flag called allowSamePubkeys, in case you really need that.
320
+ Valid use-case: `2-of-[A,A,B,C]`, can be signed by `A or (B and C)`.
321
+
322
+ ```ts
323
+ const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
324
+ const PubKey2 = hex.decode('0202020202020202020202020202020202020202020202020202020202020202');
325
+ const PubKey3 = hex.decode('1212121212121212121212121212121212121212121212121212121212121212');
326
+
327
+ // Simple 3-of-3 multisig
328
+ // Creates a single script that requires all three pubkeys: [PubKey, PubKey2, PubKey3]
329
+ deepStrictEqual(btc.p2tr_ns(3, [PubKey, PubKey2, PubKey3]), [
330
+ {
331
+ type: 'tr_ns',
332
+ script: hex.decode(
333
+ '200101010101010101010101010101010101010101010101010101010101010101ad200202020202020202020202020202020202020202020202020202020202020202ad201212121212121212121212121212121212121212121212121212121212121212ac'
334
+ ),
335
+ },
336
+ ]);
337
+ // Simple 2-of-3 multisig
338
+ // If M (pubkeys required) is less than N (# of pubkeys), then multiple scripts are created: [[PubKey, PubKey2], [PubKey, PubKey3], [PubKey2, PubKey3]]
339
+ const clean = (x) => ({ type: x.type, address: x.address, script: hex.encode(x.script) });
340
+ deepStrictEqual(clean(btc.p2tr(undefined, btc.p2tr_ns(2, [PubKey, PubKey2, PubKey3]))), {
341
+ type: 'tr',
342
+ address: 'bc1pevfcmnkqqq09a4n0fs8c7mwlc6r4efqpvgyqpjvegllavgw235fq3kz7a0',
343
+ script: '5120cb138dcec0001e5ed66f4c0f8f6ddfc6875ca401620800c99947ffd621ca8d12',
344
+ });
345
+ ```
346
+
347
+ ### P2TR-MS (Taproot M-of-N multisig)
348
+
349
+ M-of-N single leaf TapRoot multisig (`<PubKeys[0]> CHECKSIG [<PubKeys[1:n]> CHECKSIGADD] <M> NUMEQUAL`)
350
+
351
+ **_NOTE_**: By default, we don't accept duplicate public keys in order to avoid creating the wrong multisig by mistake. However, there is a flag called allowSamePubkeys, in case you really need that.
352
+ Valid use-case: `2-of-[A,A,B,C]`, can be signed by `A or (B and C)`.
353
+
354
+ **_NOTE_**: experimental, use at your own risk.
355
+
356
+ ```ts
357
+ const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
358
+ const PubKey2 = hex.decode('0202020202020202020202020202020202020202020202020202020202020202');
359
+ const PubKey3 = hex.decode('1212121212121212121212121212121212121212121212121212121212121212');
360
+ // 2-of-3 TapRoot multisig
361
+ deepStrictEqual(btc.p2tr_ms(2, [PubKey, PubKey2, PubKey3]), {
362
+ type: 'tr_ms',
363
+ script: hex.decode(
364
+ '200101010101010101010101010101010101010101010101010101010101010101ac200202020202020202020202020202020202020202020202020202020202020202ba201212121212121212121212121212121212121212121212121212121212121212ba529c'
365
+ ),
366
+ });
367
+ // Creates a single script for [PubKey, PubKey2, PubKey3]
368
+ const clean = (x) => ({ type: x.type, address: x.address, script: hex.encode(x.script) });
369
+ deepStrictEqual(clean(btc.p2tr(undefined, btc.p2tr_ms(2, [PubKey, PubKey2, PubKey3]))), {
370
+ type: 'tr',
371
+ address: 'bc1p6m2xevckax9zucumnnyvu4xhxem66ugc5r2zlw2a20s0hxnutl8qfef23s',
372
+ script: '5120d6d46cb316e98a2e639b9cc8ce54d73677ad7118a0d42fb95d53e0fb9a7c5fce',
373
+ });
374
+ ```
375
+
376
+ ### P2TR-PK (Taproot single P2PK script)
377
+
378
+ This is a specific case of `p2tr_ns(1, [pubkey])`, which is the same as the BTC descriptor: `tr($H,pk(PUBKEY))`
379
+
380
+ ```ts
381
+ const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
382
+ // P2PK for taproot
383
+ const clean = (x) => ({ type: x.type, address: x.address, script: hex.encode(x.script) });
384
+ deepStrictEqual(clean(btc.p2tr(undefined, [btc.p2tr_pk(PubKey)])), {
385
+ type: 'tr',
386
+ address: 'bc1pfj6w68w3v2f4pkzesc9tsqfvy5znw5qgydwa832v3v83vjn76kdsmr4360',
387
+ script: '51204cb4ed1dd1629350d859860ab8012c2505375008235dd3c54c8b0f164a7ed59b',
388
+ });
389
+ ```
390
+
391
+ ## Transaction
392
+
393
+ ### Encode/decode
394
+
395
+ **_NOTE_**: we support both PSBTv0 and draft PSBTv2 (there is no PSBTv1). If PSBTv2 transaction encoded into PSBTv1, all PSBTv2 fields will be stripped.
396
+ **_NOTE_**: we strip 'unknown' keys inside PSBT, they needed for new version/features support, however any unsupported feature/new version can significantly break assumptions about code.
397
+ If you have use-case where they needed, please open issue.
398
+ For PSBTv2: tx_modifiable, taproot+bip32 is not supported yet.
399
+
400
+ ```ts
401
+ // Decode
402
+ Transaction.fromRaw(raw: Bytes, opts: TxOpts = {}); // Raw tx
403
+ Transaction.fromPSBT(psbt: Bytes, opts: TxOpts = {}); // PSBT tx
404
+ // Encode
405
+ tx.unsignedTx; // Bytes of raw unsigned tx
406
+ tx.hex; // hex encoded signed raw tx
407
+ tx.toPSBT(ver = this.PSBTVersion); // PSBT
408
+ ```
409
+
410
+ ### Inputs
411
+
412
+ We have txid (BE) instead of hash (LE) in transactions. We can support both,
413
+ but txid is consistent across block explorers, while some explorers treat hash
414
+ as txid - so hash is not consistent.
415
+
416
+ ```ts
417
+ type TransactionInput = {
418
+ txid?: Bytes,
419
+ index?: number,
420
+ nonWitnessUtxo?: <RawTransactionBytesOrHex>,
421
+ witnessUtxo?: {script?: Bytes; amount: bigint},
422
+ partialSig?: [Bytes, Bytes][]; // [PubKey, Signature]
423
+ sighashType?: P.U32LE,
424
+ redeemScript?: Bytes,
425
+ witnessScript?: Bytes,
426
+ bip32Derivation?: [Bytes, {fingerprint: number; path: number[]}]; // [PubKey, DeriviationPath]
427
+ finalScriptSig?: Bytes,
428
+ finalScriptWitness?: Bytes[],
429
+ porCommitment?: Bytes,
430
+ sequence?: number,
431
+ requiredTimeLocktime?: number,
432
+ requiredHeightLocktime?: number,
433
+ tapKeySig?: Bytes,
434
+ tapScriptSig?: [Bytes, Bytes][]; // [PubKeySchnorr, LeafHash]
435
+ // [ControlBlock, ScriptWithVersion]
436
+ tapLeafScript?: [{version: number; internalKey: Bytes; merklePath: Bytes[]}, Bytes];
437
+ tapInternalKey?: Bytes,
438
+ tapMerkleRoot?: Bytes,
439
+ };
440
+
441
+ tx.addInput(input: TransactionInput): number;
442
+ tx.updateInput(idx: number, input: TransactionInput);
443
+
444
+ // Input
445
+ tx.addInput({ txid: new Uint8Array(32), index: 0 });
446
+ deepStrictEqual(tx.inputs[0], {
447
+ txid: new Uint8Array(32),
448
+ index: 0,
449
+ sequence: btc.DEFAULT_SEQUENCE,
450
+ });
451
+ // Update basic value
452
+ tx.updateInput(0, { index: 10 });
453
+ deepStrictEqual(tx.inputs[0], {
454
+ txid: new Uint8Array(32),
455
+ index: 10,
456
+ sequence: btc.DEFAULT_SEQUENCE,
457
+ });
458
+ // Add value as hex
459
+ tx.addInput({
460
+ txid: '0000000000000000000000000000000000000000000000000000000000000000',
461
+ index: 0,
462
+ });
463
+ deepStrictEqual(tx.inputs[2], {
464
+ txid: new Uint8Array(32),
465
+ index: 0,
466
+ sequence: btc.DEFAULT_SEQUENCE,
467
+ });
468
+ // Update key map
469
+ const pubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
470
+ const bip1 = [pubKey, { fingerprint: 5, path: [1, 2, 3] }];
471
+ const pubKey2 = hex.decode('030000000000000000000000000000000000000000000000000000000000000002');
472
+ const bip2 = [pubKey2, { fingerprint: 6, path: [4, 5, 6] }];
473
+ const pubKey3 = hex.decode('030000000000000000000000000000000000000000000000000000000000000003');
474
+ const bip3 = [pubKey3, { fingerprint: 7, path: [7, 8, 9] }];
475
+ // Add K-V
476
+ tx.updateInput(0, { bip32Derivation: [bip1] });
477
+ deepStrictEqual(tx.inputs[0].bip32Derivation, [bip1]);
478
+ // Add another K-V
479
+ tx.updateInput(0, { bip32Derivation: [bip2] });
480
+ deepStrictEqual(tx.inputs[0].bip32Derivation, [bip1, bip2]);
481
+ // Delete K-V
482
+ tx.updateInput(0, { bip32Derivation: [[pubKey, undefined]] });
483
+ deepStrictEqual(tx.inputs[0].bip32Derivation, [bip2]);
484
+ // Second add of same k-v does nothing
485
+ tx.updateInput(0, { bip32Derivation: [bip2] });
486
+ deepStrictEqual(tx.inputs[0].bip32Derivation, [bip2]);
487
+ // Second add of k-v with different value breaks
488
+ throws(() => tx.updateInput(0, { bip32Derivation: [[pubKey2, bip1[1]]] }));
489
+ tx.updateInput(0, { bip32Derivation: [bip1, bip2, bip3] });
490
+ // Preserves order (re-ordered on PSBT encoding)
491
+ deepStrictEqual(tx.inputs[0].bip32Derivation, [bip2, bip1, bip3]);
492
+ // PSBT encoding re-order k-v
493
+ const tx2 = btc.Transaction.fromPSBT(tx.toPSBT());
494
+ deepStrictEqual(tx2.inputs[0].bip32Derivation, [bip1, bip2, bip3]);
495
+ // Remove field
496
+ tx.updateInput(0, { bip32Derivation: undefined });
497
+ deepStrictEqual(tx.inputs[0], {
498
+ txid: new Uint8Array(32),
499
+ index: 10,
500
+ sequence: btc.DEFAULT_SEQUENCE,
501
+ });
502
+ ```
503
+
504
+ ### Outputs
505
+
506
+ **_NOTE_**: amount in addOutputAddress handled as 'bitcoins' if string, and as satoshi if bigint.
507
+ Why? BigInt usually comes from calculations/API, which is usually in satoshi. String is probably user input, so it is worth
508
+ to handle conversion to satoshi's in that case.
509
+
510
+ ```ts
511
+ type TransactionOutput = {
512
+ script?: Bytes,
513
+ amount?: bigint,
514
+ redeemScript?: Bytes,
515
+ witnessScript?: Bytes,
516
+ bip32Derivation?: [Bytes, {fingerprint: number; path: number[]}]; // [PubKey, DeriviationPath]
517
+ tapInternalKey?: Bytes,
518
+ };
519
+
520
+ tx.addOutput(o: TransactionOutput): number;
521
+ tx.updateOutput(idx: number, output: TransactionOutput);
522
+ tx.addOutputAddress(address: string, amount: string | bigint, network = NETWORK): number;
523
+
524
+ const compressed = hex.decode(
525
+ '030000000000000000000000000000000000000000000000000000000000000001'
526
+ );
527
+ const script = btc.p2pkh(compressed).script;
528
+ tx.addOutput({ script, amount: 100n });
529
+ deepStrictEqual(tx.outputs[0], {
530
+ script,
531
+ amount: 100n,
532
+ });
533
+ // Update basic value
534
+ tx.updateOutput(0, { amount: 200n });
535
+ deepStrictEqual(tx.outputs[0], {
536
+ script,
537
+ amount: 200n,
538
+ });
539
+ // Add K-V
540
+ tx.updateOutput(0, { bip32Derivation: [bip1] });
541
+ deepStrictEqual(tx.outputs[0].bip32Derivation, [bip1]);
542
+ // Add another K-V
543
+ tx.updateOutput(0, { bip32Derivation: [bip2] });
544
+ deepStrictEqual(tx.outputs[0].bip32Derivation, [bip1, bip2]);
545
+ // Delete K-V
546
+ tx.updateOutput(0, { bip32Derivation: [[pubKey, undefined]] });
547
+ deepStrictEqual(tx.outputs[0].bip32Derivation, [bip2]);
548
+ // Second add of same k-v does nothing
549
+ tx.updateOutput(0, { bip32Derivation: [bip2] });
550
+ deepStrictEqual(tx.outputs[0].bip32Derivation, [bip2]);
551
+ // Second add of k-v with different value breaks
552
+ throws(() => tx.updateOutput(0, { bip32Derivation: [[pubKey2, bip1[1]]] }));
553
+ tx.updateOutput(0, { bip32Derivation: [bip1, bip2, bip3] });
554
+ // Preserves order (re-ordered on PSBT encoding)
555
+ deepStrictEqual(tx.outputs[0].bip32Derivation, [bip2, bip1, bip3]);
556
+ // PSBT encoding re-order k-v
557
+ const tx3 = btc.Transaction.fromPSBT(tx.toPSBT());
558
+ deepStrictEqual(tx3.outputs[0].bip32Derivation, [bip1, bip2, bip3]);
559
+ // Remove field
560
+ tx.updateOutput(0, { bip32Derivation: undefined });
561
+ deepStrictEqual(tx.outputs[0], {
562
+ script,
563
+ amount: 200n,
564
+ });
565
+ ```
566
+
567
+ ### Basic transaction sign
568
+
569
+ ```ts
570
+ const privKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
571
+ const txP2WPKH = new btc.Transaction();
572
+ for (const inp of TX_TEST_INPUTS) {
573
+ txP2WPKH.addInput({
574
+ txid: inp.txid,
575
+ index: inp.index,
576
+ witnessUtxo: {
577
+ amount: inp.amount,
578
+ script: btc.p2wpkh(secp256k1.getPublicKey(privKey, true)).script,
579
+ },
580
+ });
581
+ }
582
+ for (const [address, amount] of TX_TEST_OUTPUTS) tx32.addOutputAddress(address, amount);
583
+ deepStrictEqual(hex.encode(tx32.unsignedTx), RAW_TX_HEX);
584
+ txP2WPKH.sign(privKey);
585
+ txP2WPKH.finalize();
586
+ deepStrictEqual(txP2WPKH.id, 'cbb94443b19861df0824914fa654212facc071854e0df6f7388b482a6394526d');
587
+ deepStrictEqual(
588
+ txP2WPKH.hex,
589
+ '010000000001033edaa6c4e0740ae334dbb5857dd8c6faf6ea5196760652ad7033ed9031c261c00000000000ffffffff0d9ae8a4191b3ba5a2b856c21af0f7a4feb97957ae80725ef38a933c906519a20000000000ffffffffc7a4a37d38c2b0de3d3b3e8d8e8a331977c12532fc2a4632df27a89c311ee2fa0000000000ffffffff03e8030000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac881300000000000017a914a860f76561c85551594c18eecceffaee8c4822d7876b24000000000000160014e8df018c7e326cc253faac7e46cdc51e68542c4202473044022024e7b1a6ae19a95c69c192745db09cc54385a80cc7684570cfbf2da84cbbfa0802205ad55efb2019a1aa6edc03cf243989ea428c4d216699cbae2cfaf3c26ddef5650121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0247304402204415ef16f341e888ca2483b767b47fcf22977b6d673c3f7c6cae2f6b4bc2ac08022055be98747345b02a6f40edcc2f80390dcef4efe57b38c1bb7d16bdbca710abfd0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f02473044022069769fb5c97a7dd9401dbd3f6d32a38fe82bc8934c49c7c4cd3b39c6d120080c02202c181604203dc45c10e5290ded103195fae117d7fb0db19cdc411e73a76da6cb0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f00000000'
590
+ );
591
+ ```
592
+
593
+ ### BIP174 PSBT multi-sig example
594
+
595
+ ```ts
596
+ const testnet = {
597
+ wif: 0xef,
598
+ bip32: {
599
+ public: 0x043587cf,
600
+ private: 0x04358394,
601
+ },
602
+ };
603
+ // The private keys in the tests below are derived from the following master private key:
604
+ const epriv =
605
+ 'tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF';
606
+ const hdkey = bip32.HDKey.fromExtendedKey(epriv, testnet.bip32);
607
+ // const seed = 'cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T';
608
+ const tx = new btc.Transaction(2);
609
+ // A creator creating a PSBT for a transaction which creates the following outputs:
610
+ tx.addOutput({ script: '0014d85c2b71d0060b09c9886aeb815e50991dda124d', amount: '1.49990000' });
611
+ tx.addOutput({ script: '001400aea9a2e5f0f876a588df5546e8742d1d87008f', amount: '1.00000000' });
612
+ // and spends the following inputs:
613
+ tx.addInput({
614
+ txid: '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858',
615
+ index: 0,
616
+ });
617
+ tx.addInput({
618
+ txid: '1dea7cd05979072a3578cab271c02244ea8a090bbb46aa680a65ecd027048d83',
619
+ index: 1,
620
+ });
621
+ // must create this PSBT:
622
+ const psbt1 = tx.toPSBT();
623
+ // Given the above PSBT, an updater with only the following:
624
+ const tx2 = btc.Transaction.fromPSBT(psbt1);
625
+ tx2.updateInput(0, {
626
+ nonWitnessUtxo:
627
+ '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000',
628
+ redeemScript:
629
+ '5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae',
630
+ bip32Derivation: [
631
+ [
632
+ '029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f',
633
+ { fingerprint: hdkey.fingerprint, path: btc.bip32Path("m/0'/0'/0'") },
634
+ ],
635
+ [
636
+ '02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7',
637
+ { fingerprint: hdkey.fingerprint, path: btc.bip32Path("m/0'/0'/1'") },
638
+ ],
639
+ ],
640
+ });
641
+ tx2.updateInput(1, {
642
+ // use witness utxo ({script, amount})
643
+ witnessUtxo: btc.RawTx.decode(
644
+ hex.decode(
645
+ '0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000'
646
+ )
647
+ ).outputs[1],
648
+ redeemScript: '00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903',
649
+ witnessScript:
650
+ '522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae',
651
+ bip32Derivation: [
652
+ [
653
+ '03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc',
654
+ { fingerprint: hdkey.fingerprint, path: btc.bip32Path("m/0'/0'/2'") },
655
+ ],
656
+ [
657
+ '023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73',
658
+ { fingerprint: hdkey.fingerprint, path: btc.bip32Path("m/0'/0'/3'") },
659
+ ],
660
+ ],
661
+ });
662
+ tx2.updateOutput(0, {
663
+ bip32Derivation: [
664
+ [
665
+ '03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771',
666
+ { fingerprint: hdkey.fingerprint, path: btc.bip32Path("m/0'/0'/4'") },
667
+ ],
668
+ ],
669
+ });
670
+ tx2.updateOutput(1, {
671
+ bip32Derivation: [
672
+ [
673
+ '027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096',
674
+ { fingerprint: hdkey.fingerprint, path: btc.bip32Path("m/0'/0'/5'") },
675
+ ],
676
+ ],
677
+ });
678
+ // Must create this PSBT:
679
+ const psbt2 = tx2.toPSBT();
680
+ // An updater which adds SIGHASH_ALL to the above PSBT must create this PSBT:
681
+ const tx3 = btc.Transaction.fromPSBT(psbt2);
682
+ for (let i = 0; i < tx3.inputs.length; i++)
683
+ tx3.updateInput(i, { sighashType: btc.SignatureHash.ALL });
684
+ const psbt3 = tx3.toPSBT();
685
+ /*
686
+ Given the above updated PSBT, a signer that supports SIGHASH_ALL for P2PKH and P2WPKH spends and uses RFC6979 for nonce generation and has the following keys:
687
+ - cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr (m/0'/0'/0')
688
+ - cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d (m/0'/0'/2')
689
+ */
690
+ // NOTE: we don't use HDKey, because it will everything because of bip32 derivation
691
+ const tx4 = btc.Transaction.fromPSBT(psbt3);
692
+ tx4.sign(btc.WIF(testnet).decode('cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr'));
693
+ tx4.sign(btc.WIF(testnet).decode('cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d'));
694
+ // must create this PSBT:
695
+ const psbt4 = tx4.toPSBT();
696
+ // Given the above updated PSBT, a signer with the following keys:
697
+ // cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au (m/0'/0'/1')
698
+ // cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE (m/0'/0'/3')
699
+ const tx5 = btc.Transaction.fromPSBT(psbt3);
700
+ tx5.sign(btc.WIF(testnet).decode('cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au'));
701
+ tx5.sign(btc.WIF(testnet).decode('cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE'));
702
+ // must create this PSBT:
703
+ const psbt5 = tx5.toPSBT();
704
+ // Given both of the above PSBTs, a combiner must create this PSBT:
705
+ const psbt6 = btc.PSBTCombine([psbt4, psbt5]);
706
+ // Given the above PSBT, an input finalizer must create this PSBT:
707
+ const tx7 = btc.Transaction.fromPSBT(psbt6);
708
+ tx7.finalize();
709
+ const psbt7 = tx7.toPSBT();
710
+ // Given the above PSBT, a transaction extractor must create this Bitcoin transaction:
711
+ const tx8 = btc.Transaction.fromPSBT(psbt7);
712
+ deepStrictEqual(
713
+ tx8.extract(),
714
+ hex.decode(
715
+ '0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000'
716
+ )
717
+ );
718
+ ```
719
+
720
+ ## Utils
721
+
722
+ ### getAddress
723
+
724
+ Returns common addresses from privateKey
725
+
726
+ ```ts
727
+ const privKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
728
+ deepStrictEqual(btc.getAddress('pkh', privKey), '1C6Rc3w25VHud3dLDamutaqfKWqhrLRTaD'); // P2PKH (legacy address)
729
+ deepStrictEqual(btc.getAddress('wpkh', privKey), 'bc1q0xcqpzrky6eff2g52qdye53xkk9jxkvrh6yhyw'); // SegWit V0 address
730
+ deepStrictEqual(
731
+ btc.getAddress('tr', priv),
732
+ 'bc1p33wm0auhr9kkahzd6l0kqj85af4cswn276hsxg6zpz85xe2r0y8syx4e5t'
733
+ ); // TapRoot KeyPathSpend
734
+ ```
735
+
736
+ #### WIF
737
+
738
+ Encoding/decoding of WIF privateKeys. Only compessed keys are supported for now.
739
+
740
+ ```ts
741
+ const privKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
742
+ deepStrictEqual(btc.WIF().encode(privKey), 'KwFfNUhSDaASSAwtG7ssQM1uVX8RgX5GHWnnLfhfiQDigjioWXHH');
743
+ deepStrictEqual(
744
+ hex.encode(btc.WIF().decode('KwFfNUhSDaASSAwtG7ssQM1uVX8RgX5GHWnnLfhfiQDigjioWXHH')),
745
+ '0101010101010101010101010101010101010101010101010101010101010101'
746
+ );
747
+ ```
748
+
749
+ ### Script
750
+
751
+ Encoding/decoding bitcoin scripts
752
+
753
+ ```ts
754
+ deepStrictEqual(
755
+ btc.Script.decode(
756
+ hex.decode(
757
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
758
+ )
759
+ ).map((i) => (P.isBytes(i) ? hex.encode(i) : i)),
760
+ [
761
+ 'OP_2',
762
+ '030000000000000000000000000000000000000000000000000000000000000001',
763
+ '030000000000000000000000000000000000000000000000000000000000000002',
764
+ '030000000000000000000000000000000000000000000000000000000000000003',
765
+ 'OP_3',
766
+ 'CHECKMULTISIG',
767
+ ]
768
+ );
769
+ deepStrictEqual(
770
+ hex.encode(
771
+ btc.Script.encode([
772
+ 'OP_2',
773
+ hex.decode('030000000000000000000000000000000000000000000000000000000000000001'),
774
+ hex.decode('030000000000000000000000000000000000000000000000000000000000000002'),
775
+ hex.decode('030000000000000000000000000000000000000000000000000000000000000003'),
776
+ 'OP_3',
777
+ 'CHECKMULTISIG',
778
+ ])
779
+ ),
780
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
781
+ );
782
+ ```
783
+
784
+ ### OutScript
785
+
786
+ Encoding/decoding of output scripts
787
+
788
+ ```ts
789
+ deepStrictEqual(
790
+ btc.OutScript.decode(
791
+ hex.decode(
792
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
793
+ )
794
+ ),
795
+ {
796
+ type: 'ms',
797
+ m: 2,
798
+ pubkeys: [
799
+ '030000000000000000000000000000000000000000000000000000000000000001',
800
+ '030000000000000000000000000000000000000000000000000000000000000002',
801
+ '030000000000000000000000000000000000000000000000000000000000000003',
802
+ ].map(hex.decode),
803
+ }
804
+ );
805
+ deepStrictEqual(
806
+ hex.encode(
807
+ btc.OutScript.encode({
808
+ type: 'ms',
809
+ m: 2,
810
+ pubkeys: [
811
+ '030000000000000000000000000000000000000000000000000000000000000001',
812
+ '030000000000000000000000000000000000000000000000000000000000000002',
813
+ '030000000000000000000000000000000000000000000000000000000000000003',
814
+ ].map(hex.decode),
815
+ })
816
+ ),
817
+ '5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae'
818
+ );
819
+ ```
820
+
821
+ ## Security
822
+
823
+ The library has been audited on Feb 21, 2023 by an independent security firm cure53: [PDF](./audit/2023-02-21-cure53-audit-report.pdf). The audit has been funded by Ryan Shea.
824
+
825
+ Target was v0.3.0 (commit 397ed56), see [changes since audit](https://github.com/paulmillr/scure-btc-signer/compare/0.3.0..main).
826
+
827
+ We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector. As for dependencies used by the library:
828
+
829
+ - noble-curves, noble-hashes are audited cryptography libraries also developed by us and follow the same practices
830
+ - scure-base is used for bech32 / base64 and was also audited
831
+ - micro-packed is used for binary encoding, has not been audited
832
+ - devDependencies contain scure-bip32, micro-packed-debugger, micro-should (our packages). Locked versions of prettier (linter) and typescript which are rarely updated. Every update is checked with `npm-diff`. They are only used if you clone the git repo and want to add some feature to it. End-users won't use them
833
+
834
+ ## License
835
+
836
+ MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file.