@btc-vision/transaction 1.3.3 → 1.3.5

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 (29) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/opnet.d.ts +7 -0
  4. package/browser/transaction/TransactionFactory.d.ts +2 -0
  5. package/browser/transaction/browser/extensions/UnisatSigner.d.ts +3 -5
  6. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  7. package/browser/transaction/shared/TweakedTransaction.d.ts +2 -1
  8. package/build/_version.d.ts +1 -1
  9. package/build/_version.js +1 -1
  10. package/build/opnet.d.ts +7 -0
  11. package/build/transaction/TransactionFactory.d.ts +2 -0
  12. package/build/transaction/TransactionFactory.js +70 -7
  13. package/build/transaction/browser/extensions/UnisatSigner.d.ts +3 -5
  14. package/build/transaction/browser/extensions/UnisatSigner.js +2 -0
  15. package/build/transaction/builders/DeploymentTransaction.js +12 -2
  16. package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  17. package/build/transaction/builders/SharedInteractionTransaction.js +24 -14
  18. package/build/transaction/builders/TransactionBuilder.js +3 -3
  19. package/build/transaction/shared/TweakedTransaction.d.ts +2 -1
  20. package/build/transaction/shared/TweakedTransaction.js +21 -7
  21. package/package.json +1 -1
  22. package/src/_version.ts +1 -1
  23. package/src/opnet.ts +9 -0
  24. package/src/transaction/TransactionFactory.ts +101 -12
  25. package/src/transaction/browser/extensions/UnisatSigner.ts +6 -6
  26. package/src/transaction/builders/DeploymentTransaction.ts +12 -3
  27. package/src/transaction/builders/SharedInteractionTransaction.ts +31 -28
  28. package/src/transaction/builders/TransactionBuilder.ts +7 -3
  29. package/src/transaction/shared/TweakedTransaction.ts +40 -9
@@ -19,7 +19,11 @@ import {
19
19
  } from './interfaces/ITransactionParameters.js';
20
20
  import { PSBTTypes } from './psbt/PSBTTypes.js';
21
21
  import { ChallengeSolutionTransaction } from './builders/ChallengeSolutionTransaction.js';
22
- import { InteractionParametersWithoutSigner } from './browser/Web3Provider.js';
22
+ import {
23
+ IDeploymentParametersWithoutSigner,
24
+ InteractionParametersWithoutSigner,
25
+ } from './browser/Web3Provider.js';
26
+ import { WindowWithWallets } from './browser/extensions/UnisatSigner.js';
23
27
 
24
28
  export interface DeploymentResult {
25
29
  readonly transaction: [string, string];
@@ -174,9 +178,11 @@ export class TransactionFactory {
174
178
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
175
179
  }
176
180
 
181
+ const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
177
182
  const preTransaction: InteractionTransaction = new InteractionTransaction({
178
183
  ...interactionParameters,
179
184
  utxos: [interactionParameters.utxos[0]], // we simulate one input here.
185
+ optionalInputs: inputs,
180
186
  });
181
187
 
182
188
  // we don't sign that transaction, we just need the parameters.
@@ -221,11 +227,14 @@ export class TransactionFactory {
221
227
 
222
228
  const newParams: IInteractionParameters = {
223
229
  ...interactionParameters,
224
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0), // always 0
230
+ utxos: [
231
+ ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
232
+ ], // always 0
225
233
  randomBytes: preTransaction.getRndBytes(),
226
234
  preimage: preTransaction.getPreimage(),
227
235
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
228
236
  estimatedFees: preTransaction.estimatedFees,
237
+ optionalInputs: inputs,
229
238
  };
230
239
 
231
240
  const finalTransaction: InteractionTransaction = new InteractionTransaction(newParams);
@@ -253,21 +262,46 @@ export class TransactionFactory {
253
262
  public async signDeployment(
254
263
  deploymentParameters: IDeploymentParameters,
255
264
  ): Promise<DeploymentResult> {
256
- const preTransaction: DeploymentTransaction = new DeploymentTransaction(
257
- deploymentParameters,
258
- );
265
+ const opWalletDeployment = await this.detectDeploymentOPWallet(deploymentParameters);
266
+ if (opWalletDeployment) {
267
+ return opWalletDeployment;
268
+ }
269
+
270
+ if (!('signer' in deploymentParameters)) {
271
+ throw new Error('Field "signer" not provided, OP_WALLET not detected.');
272
+ }
259
273
 
260
- // Initial generation
261
- await preTransaction.signTransaction();
274
+ const inputs = this.parseOptionalInputs(deploymentParameters.optionalInputs);
275
+ const preTransaction: DeploymentTransaction = new DeploymentTransaction({
276
+ ...deploymentParameters,
277
+ utxos: [deploymentParameters.utxos[0]], // we simulate one input here.
278
+ optionalInputs: inputs,
279
+ });
280
+
281
+ // we don't sign that transaction, we just need the parameters.
282
+ await preTransaction.generateTransactionMinimalSignatures();
262
283
 
263
284
  const parameters: IFundingTransactionParameters =
264
285
  await preTransaction.getFundingTransactionParameters();
265
286
 
287
+ parameters.utxos = deploymentParameters.utxos;
266
288
  parameters.amount =
267
289
  (await preTransaction.estimateTransactionFees()) +
268
290
  this.getPriorityFee(deploymentParameters) +
269
291
  preTransaction.getOptionalOutputValue();
270
292
 
293
+ const feeEstimationFundingTransaction = await this.createFundTransaction({
294
+ ...parameters,
295
+ optionalOutputs: [],
296
+ optionalInputs: [],
297
+ });
298
+
299
+ if (!feeEstimationFundingTransaction) {
300
+ throw new Error('Could not sign funding transaction.');
301
+ }
302
+
303
+ parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
304
+
271
305
  const fundingTransaction: FundingTransaction = new FundingTransaction({
272
306
  ...parameters,
273
307
  optionalInputs: [],
@@ -292,12 +326,12 @@ export class TransactionFactory {
292
326
 
293
327
  const newParams: IDeploymentParameters = {
294
328
  ...deploymentParameters,
295
- utxos: [newUtxo],
329
+ utxos: [newUtxo], // always 0
296
330
  randomBytes: preTransaction.getRndBytes(),
297
331
  preimage: preTransaction.getPreimage(),
298
332
  nonWitnessUtxo: signedTransaction.toBuffer(),
299
- optionalOutputs: [],
300
- optionalInputs: [],
333
+ estimatedFees: preTransaction.estimatedFees,
334
+ optionalInputs: inputs,
301
335
  };
302
336
 
303
337
  const finalTransaction: DeploymentTransaction = new DeploymentTransaction(newParams);
@@ -396,14 +430,41 @@ export class TransactionFactory {
396
430
  return utxos;
397
431
  }
398
432
 
433
+ private parseOptionalInputs(optionalInputs?: UTXO[]): UTXO[] {
434
+ return (optionalInputs || []).map((input) => {
435
+ let nonWitness = input.nonWitnessUtxo;
436
+ if (
437
+ nonWitness &&
438
+ !(nonWitness instanceof Uint8Array) &&
439
+ typeof nonWitness === 'object'
440
+ ) {
441
+ nonWitness = Buffer.from(
442
+ Uint8Array.from(
443
+ Object.values(input.nonWitnessUtxo as unknown as Record<number, number>),
444
+ ),
445
+ );
446
+ }
447
+
448
+ return {
449
+ ...input,
450
+ nonWitnessUtxo: nonWitness,
451
+ };
452
+ });
453
+ }
454
+
399
455
  private async detectInteractionOPWallet(
400
456
  interactionParameters: IInteractionParameters | InteractionParametersWithoutSigner,
401
457
  ): Promise<InteractionResponse | null> {
402
- if (typeof window === 'undefined' || !window.opnet || !window.opnet.web3) {
458
+ if (typeof window === 'undefined') {
403
459
  return null;
404
460
  }
405
461
 
406
- const opnet = window.opnet.web3;
462
+ const _window = window as WindowWithWallets;
463
+ if (!_window || !_window.opnet || !_window.opnet.web3) {
464
+ return null;
465
+ }
466
+
467
+ const opnet = _window.opnet.web3;
407
468
  const interaction = await opnet.signInteraction({
408
469
  ...interactionParameters,
409
470
 
@@ -418,6 +479,33 @@ export class TransactionFactory {
418
479
  return interaction;
419
480
  }
420
481
 
482
+ private async detectDeploymentOPWallet(
483
+ deploymentParameters: IDeploymentParameters | IDeploymentParametersWithoutSigner,
484
+ ): Promise<DeploymentResult | null> {
485
+ if (typeof window === 'undefined') {
486
+ return null;
487
+ }
488
+
489
+ const _window = window as WindowWithWallets;
490
+ if (!_window || !_window.opnet || !_window.opnet.web3) {
491
+ return null;
492
+ }
493
+
494
+ const opnet = _window.opnet.web3;
495
+ const deployment = await opnet.deployContract({
496
+ ...deploymentParameters,
497
+
498
+ // @ts-expect-error no, this is ok
499
+ signer: undefined,
500
+ });
501
+
502
+ if (!deployment) {
503
+ throw new Error('Could not sign interaction transaction.');
504
+ }
505
+
506
+ return deployment;
507
+ }
508
+
421
509
  private async _createChallengeSolution(
422
510
  parameters: IChallengeSolutionTransactionParameters,
423
511
  ): Promise<ChallengeSolutionResponse> {
@@ -426,6 +514,7 @@ export class TransactionFactory {
426
514
  const challengeTransaction: ChallengeSolutionTransaction = new ChallengeSolutionTransaction(
427
515
  parameters,
428
516
  );
517
+
429
518
  const signedTransaction: Transaction = await challengeTransaction.signTransaction();
430
519
  if (!signedTransaction) {
431
520
  throw new Error('Could not sign funding transaction.');
@@ -14,11 +14,9 @@ import { canSignNonTaprootInput, isTaprootInput } from '../../../signer/SignerUt
14
14
  import { CustomKeypair } from '../BrowserSignerBase.js';
15
15
  import { PsbtSignatureOptions, SignatureType, Unisat, UnisatNetwork } from '../types/Unisat.js';
16
16
 
17
- declare global {
18
- interface Window {
19
- unisat?: Unisat;
20
- opnet?: Unisat;
21
- }
17
+ export interface WindowWithWallets {
18
+ unisat?: Unisat;
19
+ opnet?: Unisat;
22
20
  }
23
21
 
24
22
  export class UnisatSigner extends CustomKeypair {
@@ -83,7 +81,9 @@ export class UnisatSigner extends CustomKeypair {
83
81
  }
84
82
 
85
83
  public get unisat(): Unisat {
86
- const module = window.unisat;
84
+ if (!window) throw new Error('Window not found');
85
+
86
+ const module = (window as WindowWithWallets).unisat;
87
87
  if (!module) {
88
88
  throw new Error('Unisat extension not found');
89
89
  }
@@ -118,7 +118,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
118
118
  this.verifyCalldata();
119
119
  }
120
120
 
121
- if(!parameters.preimage) throw new Error('Preimage is required');
121
+ if (!parameters.preimage) throw new Error('Preimage is required');
122
122
 
123
123
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
124
124
  this.preimage = parameters.preimage;
@@ -274,7 +274,11 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
274
274
  if (i === 0) {
275
275
  transaction.finalizeInput(i, this.customFinalizer);
276
276
  } else {
277
- transaction.finalizeInput(i);
277
+ try {
278
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
279
+ } catch (e) {
280
+ transaction.finalizeInput(i);
281
+ }
278
282
  }
279
283
  }
280
284
  }
@@ -305,7 +309,12 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
305
309
  transaction.finalizeInput(0, this.customFinalizer);
306
310
  } else {
307
311
  transaction.signInput(i, this.getSignerKey());
308
- transaction.finalizeInput(i);
312
+
313
+ try {
314
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
315
+ } catch (e) {
316
+ transaction.finalizeInput(i);
317
+ }
309
318
  }
310
319
  }
311
320
  }
@@ -67,7 +67,7 @@ export abstract class SharedInteractionTransaction<
67
67
  throw new Error('Calldata is required');
68
68
  }
69
69
 
70
- if(!parameters.preimage) {
70
+ if (!parameters.preimage) {
71
71
  throw new Error('Preimage is required');
72
72
  }
73
73
 
@@ -319,7 +319,36 @@ export abstract class SharedInteractionTransaction<
319
319
  if (i === 0) {
320
320
  transaction.finalizeInput(i, this.customFinalizer);
321
321
  } else {
322
- transaction.finalizeInput(i);
322
+ try {
323
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
324
+ } catch (e) {
325
+ transaction.finalizeInput(i);
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ protected override async signInputsNonWalletBased(transaction: Psbt): Promise<void> {
332
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
333
+ if (i === 0) {
334
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
335
+
336
+ await this.signInput(
337
+ transaction,
338
+ transaction.data.inputs[i],
339
+ i,
340
+ this.getSignerKey(),
341
+ );
342
+
343
+ transaction.finalizeInput(0, this.customFinalizer);
344
+ } else {
345
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.signer);
346
+
347
+ try {
348
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
349
+ } catch (e) {
350
+ transaction.finalizeInput(i);
351
+ }
323
352
  }
324
353
  }
325
354
  }
@@ -359,32 +388,6 @@ export abstract class SharedInteractionTransaction<
359
388
  }
360
389
  }
361
390
 
362
- private async signInputsNonWalletBased(transaction: Psbt): Promise<void> {
363
- for (let i = 0; i < transaction.data.inputs.length; i++) {
364
- if (i === 0) {
365
- await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
366
-
367
- await this.signInput(
368
- transaction,
369
- transaction.data.inputs[i],
370
- i,
371
- this.getSignerKey(),
372
- );
373
-
374
- transaction.finalizeInput(i, this.customFinalizer);
375
- } else {
376
- await this.signInput(
377
- transaction,
378
- transaction.data.inputs[i],
379
- i,
380
- this.getSignerKey(),
381
- );
382
-
383
- transaction.finalizeInput(i);
384
- }
385
- }
386
- }
387
-
388
391
  /**
389
392
  * Get the public keys
390
393
  * @private
@@ -626,9 +626,13 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
626
626
  }
627
627
 
628
628
  if (this.optionalInputs) {
629
- for (let i = 0; i < this.optionalInputs.length; i++) {
630
- const utxo = this.optionalInputs[i];
631
- const input = this.generatePsbtInputExtended(utxo, i);
629
+ for (
630
+ let i = this.utxos.length;
631
+ i < this.optionalInputs.length + this.utxos.length;
632
+ i++
633
+ ) {
634
+ const utxo = this.optionalInputs[i - this.utxos.length];
635
+ const input = this.generatePsbtInputExtended(utxo, i, true);
632
636
 
633
637
  this.addInput(input);
634
638
  }
@@ -462,6 +462,10 @@ export abstract class TweakedTransaction extends Logger {
462
462
  return;
463
463
  }
464
464
 
465
+ await this.signInputsNonWalletBased(transaction);
466
+ }
467
+
468
+ protected async signInputsNonWalletBased(transaction: Psbt): Promise<void> {
465
469
  // non web based signing.
466
470
  const txs: PsbtInput[] = transaction.data.inputs;
467
471
 
@@ -646,10 +650,15 @@ export abstract class TweakedTransaction extends Logger {
646
650
  * Generate the PSBT input extended, supporting various script types
647
651
  * @param {UTXO} utxo The UTXO
648
652
  * @param {number} i The index of the input
653
+ * @param {UTXO} extra Extra UTXO
649
654
  * @protected
650
655
  * @returns {PsbtInputExtended} The PSBT input extended
651
656
  */
652
- protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended {
657
+ protected generatePsbtInputExtended(
658
+ utxo: UTXO,
659
+ i: number,
660
+ extra: boolean = false,
661
+ ): PsbtInputExtended {
653
662
  const script = Buffer.from(utxo.scriptPubKey.hex, 'hex');
654
663
 
655
664
  const input: PsbtInputExtended = {
@@ -788,16 +797,29 @@ export abstract class TweakedTransaction extends Logger {
788
797
  this.error(`Unknown or unsupported script type for output: ${utxo.scriptPubKey.hex}`);
789
798
  }
790
799
 
791
- // TapLeafScript if available
792
- if (this.tapLeafScript) {
793
- input.tapLeafScript = [this.tapLeafScript];
794
- }
800
+ if (i === 0) {
801
+ // TapLeafScript if available
802
+ if (this.tapLeafScript) {
803
+ input.tapLeafScript = [this.tapLeafScript];
804
+ }
795
805
 
796
- // If the first input and we have a global nonWitnessUtxo not yet set
797
- if (i === 0 && this.nonWitnessUtxo) {
798
- input.nonWitnessUtxo = this.nonWitnessUtxo;
806
+ if (this.nonWitnessUtxo) {
807
+ input.nonWitnessUtxo = this.nonWitnessUtxo;
808
+ }
799
809
  }
800
810
 
811
+ /*if (utxo.nonWitnessUtxo && extra) {
812
+ const witness = Buffer.isBuffer(utxo.nonWitnessUtxo)
813
+ ? utxo.nonWitnessUtxo
814
+ : typeof utxo.nonWitnessUtxo === 'string'
815
+ ? Buffer.from(utxo.nonWitnessUtxo, 'hex')
816
+ : (utxo.nonWitnessUtxo as unknown) instanceof Uint8Array
817
+ ? Buffer.from(utxo.nonWitnessUtxo)
818
+ : undefined;
819
+
820
+ input.nonWitnessUtxo = witness;
821
+ }*/
822
+
801
823
  return input;
802
824
  }
803
825
 
@@ -861,7 +883,16 @@ export abstract class TweakedTransaction extends Logger {
861
883
  }
862
884
 
863
885
  if (tweakedSigner) {
864
- await this.signTaprootInput(tweakedSigner, transaction, i);
886
+ try {
887
+ await this.signTaprootInput(tweakedSigner, transaction, i);
888
+ } catch (e) {
889
+ tweakedSigner = this.getTweakedSigner(false, this.signer);
890
+ if (!tweakedSigner) {
891
+ throw new Error(`Failed to obtain tweaked signer for input ${i}.`);
892
+ }
893
+
894
+ await this.signTaprootInput(tweakedSigner, transaction, i);
895
+ }
865
896
  } else {
866
897
  this.error(`Failed to obtain tweaked signer for input ${i}.`);
867
898
  }