@btc-vision/transaction 1.1.15 → 1.1.17

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.
Files changed (38) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/keypair/Address.d.ts +2 -0
  4. package/browser/opnet.d.ts +1 -0
  5. package/browser/signer/SignerUtils.d.ts +6 -0
  6. package/browser/transaction/shared/TweakedTransaction.d.ts +5 -6
  7. package/browser/utxo/interfaces/IUTXO.d.ts +1 -0
  8. package/build/_version.d.ts +1 -1
  9. package/build/_version.js +1 -1
  10. package/build/keypair/Address.d.ts +2 -0
  11. package/build/keypair/Address.js +9 -0
  12. package/build/opnet.d.ts +1 -0
  13. package/build/opnet.js +1 -0
  14. package/build/signer/SignerUtils.d.ts +6 -0
  15. package/build/signer/SignerUtils.js +56 -0
  16. package/build/transaction/browser/extensions/UnisatSigner.js +5 -32
  17. package/build/transaction/browser/extensions/XverseSigner.js +5 -48
  18. package/build/transaction/builders/FundingTransaction.js +6 -1
  19. package/build/transaction/builders/TransactionBuilder.js +3 -1
  20. package/build/transaction/shared/TweakedTransaction.d.ts +5 -6
  21. package/build/transaction/shared/TweakedTransaction.js +121 -91
  22. package/build/utils/BitcoinUtils.js +4 -4
  23. package/build/utxo/OPNetLimitedProvider.js +1 -0
  24. package/build/utxo/interfaces/IUTXO.d.ts +1 -0
  25. package/package.json +2 -5
  26. package/src/_version.ts +1 -1
  27. package/src/keypair/Address.ts +15 -0
  28. package/src/opnet.ts +2 -0
  29. package/src/signer/SignerUtils.ts +78 -0
  30. package/src/transaction/TransactionFactory.ts +0 -253
  31. package/src/transaction/browser/extensions/UnisatSigner.ts +4 -40
  32. package/src/transaction/browser/extensions/XverseSigner.ts +9 -68
  33. package/src/transaction/builders/FundingTransaction.ts +7 -2
  34. package/src/transaction/builders/TransactionBuilder.ts +3 -1
  35. package/src/transaction/shared/TweakedTransaction.ts +224 -77
  36. package/src/utils/BitcoinUtils.ts +4 -4
  37. package/src/utxo/OPNetLimitedProvider.ts +1 -0
  38. package/src/utxo/interfaces/IUTXO.ts +2 -0
@@ -51,15 +51,6 @@ export interface BitcoinTransferResponse {
51
51
  readonly nextUTXOs: UTXO[];
52
52
  }
53
53
 
54
- /*export interface UnwrapResult {
55
- readonly fundingTransaction: string;
56
- readonly psbt: string;
57
-
58
- readonly feeRefundOrLoss: bigint;
59
-
60
- readonly utxos: UTXO[];
61
- }*/
62
-
63
54
  export class TransactionFactory {
64
55
  /**
65
56
  * @description Generate a transaction with a custom script.
@@ -283,220 +274,6 @@ export class TransactionFactory {
283
274
  };
284
275
  }
285
276
 
286
- /**
287
- * Basically it's fun to manage UTXOs.
288
- * @param {IWrapParameters} wrapParameters - The wrap parameters
289
- * @returns {Promise<WrapResult>} - The signed transaction
290
- * @throws {Error} - If the transaction could not be signed
291
- */
292
- /*public async wrap(wrapParameters: Omit<IWrapParameters, 'calldata'>): Promise<WrapResult> {
293
- if (wrapParameters.amount < currentConsensusConfig.VAULT_MINIMUM_AMOUNT) {
294
- throw new Error(
295
- `Amount is too low. Minimum consolidation is ${currentConsensusConfig.VAULT_MINIMUM_AMOUNT} sat. Received ${wrapParameters.amount} sat. Make sure that you cover the unwrap consolidation fees of ${currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT}sat.`,
296
- );
297
- }
298
-
299
- const childTransactionRequiredValue: bigint =
300
- wrapParameters.amount +
301
- currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT +
302
- this.getPriorityFee(wrapParameters);
303
-
304
- const wbtc: wBTC = new wBTC(wrapParameters.network, wrapParameters.chainId);
305
- const to = wbtc.getAddress();
306
- const fundingParameters: IFundingTransactionParameters = {
307
- ...wrapParameters,
308
- amount: childTransactionRequiredValue,
309
- to: wrapParameters.to ?? to,
310
- };
311
-
312
- const preFundingTransaction = await this.createFundTransaction(fundingParameters);
313
- wrapParameters.utxos = this.getUTXOAsTransaction(preFundingTransaction.tx, to, 0);
314
-
315
- const preTransaction: WrapTransaction = new WrapTransaction(wrapParameters);
316
-
317
- // Initial generation
318
- await preTransaction.signTransaction();
319
-
320
- const parameters: IFundingTransactionParameters =
321
- await preTransaction.getFundingTransactionParameters();
322
-
323
- // We add the amount
324
- parameters.amount += childTransactionRequiredValue;
325
- parameters.utxos = fundingParameters.utxos;
326
-
327
- const signedTransaction = await this.createFundTransaction(parameters);
328
- if (!signedTransaction) {
329
- throw new Error('Could not sign funding transaction.');
330
- }
331
-
332
- const newParams: IWrapParameters = {
333
- ...wrapParameters,
334
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, to, 0), // always 0
335
- randomBytes: preTransaction.getRndBytes(),
336
- nonWitnessUtxo: signedTransaction.tx.toBuffer(),
337
- };
338
-
339
- const finalTransaction: WrapTransaction = new WrapTransaction(newParams);
340
-
341
- // We have to regenerate using the new utxo
342
- const outTx: Transaction = await finalTransaction.signTransaction();
343
- return {
344
- transaction: [signedTransaction.tx.toHex(), outTx.toHex()],
345
- vaultAddress: finalTransaction.vault,
346
- amount: finalTransaction.amount,
347
- receiverAddress: finalTransaction.receiver.p2tr(wrapParameters.network),
348
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, wrapParameters.from, 1),
349
- };
350
- }*/
351
-
352
- /**
353
- * Unwrap bitcoin.
354
- * @param {IUnwrapParameters} unwrapParameters - The unwrap parameters
355
- * @returns {Promise<UnwrapResult>} - The signed transaction
356
- * @throws {Error} - If the transaction could not be signed
357
- * @deprecated
358
- */
359
- /*public async unwrapSegwit(unwrapParameters: IUnwrapParameters): Promise<UnwrapResult> {
360
- console.error('The "unwrap" method is deprecated. Use unwrapTap instead.');
361
-
362
- const transaction: UnwrapSegwitTransaction = new UnwrapSegwitTransaction(unwrapParameters);
363
- await transaction.signTransaction();
364
-
365
- const to = transaction.toAddress();
366
- if (!to) throw new Error('To address is required');
367
-
368
- // Initial generation
369
- const estimatedGas = await transaction.estimateTransactionFees();
370
- const estimatedFees = transaction.preEstimateTransactionFees(
371
- BigInt(unwrapParameters.feeRate),
372
- this.calculateNumInputs(unwrapParameters.unwrapUTXOs),
373
- 2n,
374
- this.calculateNumSignatures(unwrapParameters.unwrapUTXOs),
375
- this.maxPubKeySize(unwrapParameters.unwrapUTXOs),
376
- );
377
-
378
- const fundingParameters: IFundingTransactionParameters = {
379
- ...unwrapParameters,
380
- amount: estimatedGas + estimatedFees,
381
- to: to,
382
- };
383
-
384
- const preFundingTransaction = await this.createFundTransaction(fundingParameters);
385
- unwrapParameters.utxos = this.getUTXOAsTransaction(preFundingTransaction.tx, to, 0);
386
-
387
- const preTransaction: UnwrapSegwitTransaction = new UnwrapSegwitTransaction({
388
- ...unwrapParameters,
389
- randomBytes: transaction.getRndBytes(),
390
- });
391
-
392
- // Initial generation
393
- await preTransaction.signTransaction();
394
-
395
- const parameters: IFundingTransactionParameters =
396
- await preTransaction.getFundingTransactionParameters();
397
-
398
- parameters.utxos = fundingParameters.utxos;
399
- parameters.amount = (await preTransaction.estimateTransactionFees()) + estimatedFees;
400
-
401
- const signedTransaction = await this.createFundTransaction(parameters);
402
- if (!signedTransaction) {
403
- throw new Error('Could not sign funding transaction.');
404
- }
405
-
406
- const newParams: IUnwrapParameters = {
407
- ...unwrapParameters,
408
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, to, 0), // always 0
409
- randomBytes: preTransaction.getRndBytes(),
410
- nonWitnessUtxo: signedTransaction.tx.toBuffer(),
411
- };
412
-
413
- const finalTransaction: UnwrapSegwitTransaction = new UnwrapSegwitTransaction(newParams);
414
-
415
- // We have to regenerate using the new utxo
416
- const outTx: Psbt = await finalTransaction.signPSBT();
417
- const asBase64 = outTx.toBase64();
418
- const psbt = this.writePSBTHeader(PSBTTypes.UNWRAP, asBase64);
419
-
420
- return {
421
- fundingTransaction: signedTransaction.tx.toHex(),
422
- psbt: psbt,
423
- feeRefundOrLoss: estimatedFees,
424
- utxos: [],
425
- };
426
- }*/
427
-
428
- /**
429
- * Unwrap bitcoin via taproot.
430
- * @param {IUnwrapParameters} unwrapParameters - The unwrap parameters
431
- * @returns {Promise<UnwrapResult>} - The signed transaction
432
- * @throws {Error} - If the transaction could not be signed
433
- */
434
-
435
- /*public async unwrap(unwrapParameters: IUnwrapParameters): Promise<UnwrapResult> {
436
- if (!unwrapParameters.from) {
437
- throw new Error('Field "from" not provided.');
438
- }
439
-
440
- const transaction: UnwrapTransaction = new UnwrapTransaction(unwrapParameters);
441
- await transaction.signTransaction();
442
-
443
- const to = transaction.toAddress();
444
- if (!to) throw new Error('To address is required');
445
-
446
- // Initial generation
447
- const estimatedGas = await transaction.estimateTransactionFees();
448
- const fundingParameters: IFundingTransactionParameters = {
449
- ...unwrapParameters,
450
- amount: estimatedGas,
451
- to: to,
452
- };
453
-
454
- const preFundingTransaction = await this.createFundTransaction(fundingParameters);
455
- unwrapParameters.utxos = this.getUTXOAsTransaction(preFundingTransaction.tx, to, 0);
456
-
457
- const preTransaction: UnwrapTransaction = new UnwrapTransaction({
458
- ...unwrapParameters,
459
- randomBytes: transaction.getRndBytes(),
460
- });
461
-
462
- // Initial generation
463
- await preTransaction.signTransaction();
464
-
465
- const parameters: IFundingTransactionParameters =
466
- await preTransaction.getFundingTransactionParameters();
467
-
468
- parameters.utxos = fundingParameters.utxos;
469
- parameters.amount =
470
- (await preTransaction.estimateTransactionFees()) +
471
- this.getPriorityFee(unwrapParameters);
472
-
473
- const signedTransaction = await this.createFundTransaction(parameters);
474
- if (!signedTransaction) {
475
- throw new Error('Could not sign funding transaction.');
476
- }
477
-
478
- const newParams: IUnwrapParameters = {
479
- ...unwrapParameters,
480
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, to, 0), // always 0
481
- randomBytes: preTransaction.getRndBytes(),
482
- nonWitnessUtxo: signedTransaction.tx.toBuffer(),
483
- };
484
-
485
- const finalTransaction: UnwrapTransaction = new UnwrapTransaction(newParams);
486
-
487
- // We have to regenerate using the new utxo
488
- const outTx: Psbt = await finalTransaction.signPSBT();
489
- const asBase64 = outTx.toBase64();
490
- const psbt = this.writePSBTHeader(PSBTTypes.UNWRAP, asBase64);
491
-
492
- return {
493
- fundingTransaction: signedTransaction.tx.toHex(),
494
- psbt: psbt,
495
- feeRefundOrLoss: finalTransaction.getFeeLossOrRefund(),
496
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, unwrapParameters.from, 1),
497
- };
498
- }*/
499
-
500
277
  /**
501
278
  * @description Creates a funding transaction.
502
279
  * @param {IFundingTransactionParameters} parameters - The funding transaction parameters
@@ -566,36 +343,6 @@ export class TransactionFactory {
566
343
  };
567
344
  }
568
345
 
569
- /*private calculateNumSignatures(vault: VaultUTXOs[]): bigint {
570
- let numSignatures = 0n;
571
-
572
- for (const v of vault) {
573
- numSignatures += BigInt(v.minimum * v.utxos.length);
574
- }
575
-
576
- return numSignatures;
577
- }
578
-
579
- private calculateNumInputs(vault: VaultUTXOs[]): bigint {
580
- let numSignatures = 0n;
581
-
582
- for (const v of vault) {
583
- numSignatures += BigInt(v.utxos.length);
584
- }
585
-
586
- return numSignatures;
587
- }
588
-
589
- private maxPubKeySize(vault: VaultUTXOs[]): bigint {
590
- let size = 0;
591
-
592
- for (const v of vault) {
593
- size = Math.max(size, v.publicKeys.length);
594
- }
595
-
596
- return BigInt(size);
597
- }*/
598
-
599
346
  private writePSBTHeader(type: PSBTTypes, psbt: string): string {
600
347
  const buf = Buffer.from(psbt, 'base64');
601
348
 
@@ -2,9 +2,7 @@ import {
2
2
  crypto as bitCrypto,
3
3
  Network,
4
4
  networks,
5
- opcodes,
6
5
  Psbt,
7
- PsbtInput,
8
6
  script as bitScript,
9
7
  TapScriptSig,
10
8
  } from '@btc-vision/bitcoin';
@@ -14,6 +12,7 @@ import { CustomKeypair } from '../BrowserSignerBase.js';
14
12
  import { PsbtSignatureOptions, Unisat, UnisatNetwork } from '../types/Unisat.js';
15
13
  import { PartialSig } from 'bip174/src/lib/interfaces.js';
16
14
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
15
+ import { canSignNonTaprootInput, isTaprootInput } from '../../../signer/SignerUtils.js';
17
16
 
18
17
  declare global {
19
18
  interface Window {
@@ -219,14 +218,10 @@ export class UnisatSigner extends CustomKeypair {
219
218
  viaTaproot = true;
220
219
  }
221
220
  }
222
- } else {
221
+ } else if (canSignNonTaprootInput(input, this.publicKey)) {
223
222
  // Non-Taproot input
224
- const script = getInputRelevantScript(input);
225
-
226
- if (script && pubkeyInScript(this.publicKey, script)) {
227
- needsToSign = true;
228
- viaTaproot = false;
229
- }
223
+ needsToSign = true;
224
+ viaTaproot = false;
230
225
  }
231
226
 
232
227
  if (needsToSign) {
@@ -357,37 +352,6 @@ export class UnisatSigner extends CustomKeypair {
357
352
  }
358
353
  }
359
354
 
360
- // Helper functions
361
- function isTaprootInput(input: PsbtInput): boolean {
362
- if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
363
- return true;
364
- }
365
-
366
- if (input.witnessUtxo) {
367
- const script = input.witnessUtxo.script;
368
- return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
369
- }
370
-
371
- return false;
372
- }
373
-
374
- function getInputRelevantScript(input: PsbtInput): Buffer | null {
375
- if (input.redeemScript) {
376
- return input.redeemScript;
377
- }
378
- if (input.witnessScript) {
379
- return input.witnessScript;
380
- }
381
- if (input.witnessUtxo) {
382
- return input.witnessUtxo.script;
383
- }
384
- if (input.nonWitnessUtxo) {
385
- // Additional logic can be added here if needed
386
- return null;
387
- }
388
- return null;
389
- }
390
-
391
355
  function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
392
356
  return pubkeyPositionInScript(pubkey, script) !== -1;
393
357
  }
@@ -1,13 +1,4 @@
1
- import {
2
- crypto as bitCrypto,
3
- script as bitScript,
4
- Network,
5
- networks,
6
- opcodes,
7
- Psbt,
8
- PsbtInput,
9
- TapScriptSig,
10
- } from '@btc-vision/bitcoin';
1
+ import { Network, networks, Psbt, TapScriptSig } from '@btc-vision/bitcoin';
11
2
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
12
3
  import { PartialSig } from 'bip174/src/lib/interfaces.js';
13
4
  import { ECPairInterface } from 'ecpair';
@@ -15,6 +6,11 @@ import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
15
6
  import { CustomKeypair } from '../BrowserSignerBase.js';
16
7
  import { PsbtSignatureOptions } from '../types/Unisat.js';
17
8
  import { Xverse, XverseRPCGetAccountResponse, XverseRPCSignPsbtResponse } from '../types/Xverse.js';
9
+ import {
10
+ canSignNonTaprootInput,
11
+ isTaprootInput,
12
+ pubkeyInScript,
13
+ } from '../../../signer/SignerUtils.js';
18
14
 
19
15
  declare global {
20
16
  interface Window {
@@ -226,14 +222,10 @@ export class XverseSigner extends CustomKeypair {
226
222
  viaTaproot = true;
227
223
  }
228
224
  }
229
- } else {
225
+ } else if (canSignNonTaprootInput(input, this.publicKey)) {
230
226
  // Non-Taproot input
231
- const script = getInputRelevantScript(input);
232
-
233
- if (script && pubkeyInScript(this.publicKey, script)) {
234
- needsToSign = true;
235
- viaTaproot = false;
236
- }
227
+ needsToSign = true;
228
+ viaTaproot = false;
237
229
  }
238
230
 
239
231
  if (needsToSign) {
@@ -376,54 +368,3 @@ export class XverseSigner extends CustomKeypair {
376
368
  return nonDuplicate;
377
369
  }
378
370
  }
379
-
380
- // Helper functions
381
- function isTaprootInput(input: PsbtInput): boolean {
382
- if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
383
- return true;
384
- }
385
-
386
- if (input.witnessUtxo) {
387
- const script = input.witnessUtxo.script;
388
- return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
389
- }
390
-
391
- return false;
392
- }
393
-
394
- function getInputRelevantScript(input: PsbtInput): Buffer | null {
395
- if (input.redeemScript) {
396
- return input.redeemScript;
397
- }
398
- if (input.witnessScript) {
399
- return input.witnessScript;
400
- }
401
- if (input.witnessUtxo) {
402
- return input.witnessUtxo.script;
403
- }
404
- if (input.nonWitnessUtxo) {
405
- // Additional logic can be added here if needed
406
- return null;
407
- }
408
- return null;
409
- }
410
-
411
- function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
412
- return pubkeyPositionInScript(pubkey, script) !== -1;
413
- }
414
-
415
- function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
416
- const pubkeyHash = bitCrypto.hash160(pubkey);
417
- const pubkeyXOnly = toXOnly(pubkey);
418
-
419
- const decompiled = bitScript.decompile(script);
420
- if (decompiled === null) throw new Error('Unknown script error');
421
-
422
- return decompiled.findIndex((element) => {
423
- if (typeof element === 'number') return false;
424
- return (
425
- Buffer.isBuffer(element) &&
426
- (element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly))
427
- );
428
- });
429
- }
@@ -1,6 +1,6 @@
1
1
  import { TransactionType } from '../enums/TransactionType.js';
2
2
  import { IFundingTransactionParameters } from '../interfaces/ITransactionParameters.js';
3
- import { Signer } from '@btc-vision/bitcoin';
3
+ import { opcodes, script, Signer } from '@btc-vision/bitcoin';
4
4
  import { TransactionBuilder } from './TransactionBuilder.js';
5
5
  import { ECPairInterface } from 'ecpair';
6
6
 
@@ -29,9 +29,14 @@ export class FundingTransaction extends TransactionBuilder<TransactionType.FUNDI
29
29
  if (this.splitInputsInto > 1) {
30
30
  this.splitInputs(this.amount);
31
31
  } else if (this.isPubKeyDestination) {
32
+ const p2pkScript = script.compile([
33
+ Buffer.from(this.to.replace('0x', ''), 'hex'),
34
+ opcodes.OP_CHECKSIG,
35
+ ]);
36
+
32
37
  this.addOutput({
33
38
  value: Number(this.amount),
34
- script: Buffer.from(this.to.slice(2), 'hex'),
39
+ script: p2pkScript,
35
40
  });
36
41
  } else {
37
42
  this.addOutput({
@@ -151,7 +151,9 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
151
151
  this.utxos = parameters.utxos;
152
152
  this.to = parameters.to || undefined;
153
153
 
154
- this.isPubKeyDestination = this.to ? this.to.startsWith('0x') : false;
154
+ this.isPubKeyDestination = this.to
155
+ ? AddressVerificator.isValidPublicKey(this.to, this.network)
156
+ : false;
155
157
 
156
158
  this.optionalOutputs = parameters.optionalOutputs;
157
159