@btc-vision/transaction 1.3.3 → 1.3.4

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.
@@ -36,8 +36,8 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
36
36
  finalScriptWitness: Buffer<ArrayBufferLike>;
37
37
  };
38
38
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
39
+ protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
39
40
  private createMineableRewardOutputs;
40
- private signInputsNonWalletBased;
41
41
  private getPubKeys;
42
42
  private generateRedeemScripts;
43
43
  }
@@ -51,6 +51,7 @@ export declare abstract class TweakedTransaction extends Logger {
51
51
  protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean, errored?: boolean): Promise<void>;
52
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
53
53
  protected signInputs(transaction: Psbt): Promise<void>;
54
+ protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
54
55
  protected internalPubKeyToXOnly(): Buffer;
55
56
  protected internalInit(): void;
56
57
  protected tweakSigner(): void;
@@ -64,7 +65,7 @@ export declare abstract class TweakedTransaction extends Logger {
64
65
  redeemScript: Buffer;
65
66
  outputScript: Buffer;
66
67
  } | undefined;
67
- protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
68
+ protected generatePsbtInputExtended(utxo: UTXO, i: number, extra?: boolean): PsbtInputExtended;
68
69
  protected customFinalizerP2SH: (inputIndex: number, input: PsbtInput, scriptA: Buffer, isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => {
69
70
  finalScriptSig: Buffer | undefined;
70
71
  finalScriptWitness: Buffer | undefined;
@@ -70,9 +70,22 @@ export class TransactionFactory {
70
70
  if (!('signer' in interactionParameters)) {
71
71
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
72
72
  }
73
+ const inputs = (interactionParameters.optionalInputs || []).map((input) => {
74
+ let nonWitness = input.nonWitnessUtxo;
75
+ if (nonWitness &&
76
+ !(nonWitness instanceof Uint8Array) &&
77
+ typeof nonWitness === 'object') {
78
+ nonWitness = Buffer.from(Uint8Array.from(Object.values(input.nonWitnessUtxo)));
79
+ }
80
+ return {
81
+ ...input,
82
+ nonWitnessUtxo: nonWitness,
83
+ };
84
+ });
73
85
  const preTransaction = new InteractionTransaction({
74
86
  ...interactionParameters,
75
87
  utxos: [interactionParameters.utxos[0]],
88
+ optionalInputs: inputs,
76
89
  });
77
90
  await preTransaction.generateTransactionMinimalSignatures();
78
91
  const parameters = await preTransaction.getFundingTransactionParameters();
@@ -101,11 +114,14 @@ export class TransactionFactory {
101
114
  interactionParameters.utxos = this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0);
102
115
  const newParams = {
103
116
  ...interactionParameters,
104
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
117
+ utxos: [
118
+ ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
119
+ ],
105
120
  randomBytes: preTransaction.getRndBytes(),
106
121
  preimage: preTransaction.getPreimage(),
107
122
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
108
123
  estimatedFees: preTransaction.estimatedFees,
124
+ optionalInputs: inputs,
109
125
  };
110
126
  const finalTransaction = new InteractionTransaction(newParams);
111
127
  const outTx = await finalTransaction.signTransaction();
@@ -215,7 +231,7 @@ export class TransactionFactory {
215
231
  return utxos;
216
232
  }
217
233
  async detectInteractionOPWallet(interactionParameters) {
218
- if (typeof window === 'undefined' || !window.opnet || !window.opnet.web3) {
234
+ if (typeof window === 'undefined' || !window || !window.opnet || !window.opnet.web3) {
219
235
  return null;
220
236
  }
221
237
  const opnet = window.opnet.web3;
@@ -42,6 +42,8 @@ export class UnisatSigner extends CustomKeypair {
42
42
  return this._network;
43
43
  }
44
44
  get unisat() {
45
+ if (!window)
46
+ throw new Error('Window not found');
45
47
  const module = window.unisat;
46
48
  if (!module) {
47
49
  throw new Error('Unisat extension not found');
@@ -36,8 +36,8 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
36
36
  finalScriptWitness: Buffer<ArrayBufferLike>;
37
37
  };
38
38
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
39
+ protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
39
40
  private createMineableRewardOutputs;
40
- private signInputsNonWalletBased;
41
41
  private getPubKeys;
42
42
  private generateRedeemScripts;
43
43
  }
@@ -156,7 +156,30 @@ export class SharedInteractionTransaction extends TransactionBuilder {
156
156
  transaction.finalizeInput(i, this.customFinalizer);
157
157
  }
158
158
  else {
159
- transaction.finalizeInput(i);
159
+ try {
160
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
161
+ }
162
+ catch (e) {
163
+ transaction.finalizeInput(i);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ async signInputsNonWalletBased(transaction) {
169
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
170
+ if (i === 0) {
171
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
172
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
173
+ transaction.finalizeInput(0, this.customFinalizer);
174
+ }
175
+ else {
176
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.signer);
177
+ try {
178
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
179
+ }
180
+ catch (e) {
181
+ transaction.finalizeInput(i);
182
+ }
160
183
  }
161
184
  }
162
185
  }
@@ -187,19 +210,6 @@ export class SharedInteractionTransaction extends TransactionBuilder {
187
210
  await this.addRefundOutput(amountSpent + amount);
188
211
  }
189
212
  }
190
- async signInputsNonWalletBased(transaction) {
191
- for (let i = 0; i < transaction.data.inputs.length; i++) {
192
- if (i === 0) {
193
- await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
194
- await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
195
- transaction.finalizeInput(i, this.customFinalizer);
196
- }
197
- else {
198
- await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
199
- transaction.finalizeInput(i);
200
- }
201
- }
202
- }
203
213
  getPubKeys() {
204
214
  const pubKeys = [Buffer.from(this.signer.publicKey)];
205
215
  if (this.scriptSigner) {
@@ -293,9 +293,9 @@ export class TransactionBuilder extends TweakedTransaction {
293
293
  }
294
294
  }
295
295
  if (this.optionalInputs) {
296
- for (let i = 0; i < this.optionalInputs.length; i++) {
297
- const utxo = this.optionalInputs[i];
298
- const input = this.generatePsbtInputExtended(utxo, i);
296
+ for (let i = this.utxos.length; i < this.optionalInputs.length + this.utxos.length; i++) {
297
+ const utxo = this.optionalInputs[i - this.utxos.length];
298
+ const input = this.generatePsbtInputExtended(utxo, i, true);
299
299
  this.addInput(input);
300
300
  }
301
301
  }
@@ -51,6 +51,7 @@ export declare abstract class TweakedTransaction extends Logger {
51
51
  protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean, errored?: boolean): Promise<void>;
52
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
53
53
  protected signInputs(transaction: Psbt): Promise<void>;
54
+ protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
54
55
  protected internalPubKeyToXOnly(): Buffer;
55
56
  protected internalInit(): void;
56
57
  protected tweakSigner(): void;
@@ -64,7 +65,7 @@ export declare abstract class TweakedTransaction extends Logger {
64
65
  redeemScript: Buffer;
65
66
  outputScript: Buffer;
66
67
  } | undefined;
67
- protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
68
+ protected generatePsbtInputExtended(utxo: UTXO, i: number, extra?: boolean): PsbtInputExtended;
68
69
  protected customFinalizerP2SH: (inputIndex: number, input: PsbtInput, scriptA: Buffer, isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => {
69
70
  finalScriptSig: Buffer | undefined;
70
71
  finalScriptWitness: Buffer | undefined;
@@ -208,6 +208,9 @@ export class TweakedTransaction extends Logger {
208
208
  await this.signInputsWalletBased(transaction);
209
209
  return;
210
210
  }
211
+ await this.signInputsNonWalletBased(transaction);
212
+ }
213
+ async signInputsNonWalletBased(transaction) {
211
214
  const txs = transaction.data.inputs;
212
215
  const batchSize = 20;
213
216
  const batches = this.splitArray(txs, batchSize);
@@ -324,7 +327,7 @@ export class TweakedTransaction extends Logger {
324
327
  }
325
328
  return;
326
329
  }
327
- generatePsbtInputExtended(utxo, i) {
330
+ generatePsbtInputExtended(utxo, i, extra = false) {
328
331
  const script = Buffer.from(utxo.scriptPubKey.hex, 'hex');
329
332
  const input = {
330
333
  hash: utxo.transactionId,
@@ -421,8 +424,10 @@ export class TweakedTransaction extends Logger {
421
424
  else {
422
425
  this.error(`Unknown or unsupported script type for output: ${utxo.scriptPubKey.hex}`);
423
426
  }
424
- if (this.tapLeafScript) {
425
- input.tapLeafScript = [this.tapLeafScript];
427
+ if (i === 0) {
428
+ if (this.tapLeafScript) {
429
+ input.tapLeafScript = [this.tapLeafScript];
430
+ }
426
431
  }
427
432
  if (i === 0 && this.nonWitnessUtxo) {
428
433
  input.nonWitnessUtxo = this.nonWitnessUtxo;
@@ -453,7 +458,16 @@ export class TweakedTransaction extends Logger {
453
458
  tweakedSigner = this.tweakedSigner;
454
459
  }
455
460
  if (tweakedSigner) {
456
- await this.signTaprootInput(tweakedSigner, transaction, i);
461
+ try {
462
+ await this.signTaprootInput(tweakedSigner, transaction, i);
463
+ }
464
+ catch (e) {
465
+ tweakedSigner = this.getTweakedSigner(false, this.signer);
466
+ if (!tweakedSigner) {
467
+ throw new Error(`Failed to obtain tweaked signer for input ${i}.`);
468
+ }
469
+ await this.signTaprootInput(tweakedSigner, transaction, i);
470
+ }
457
471
  }
458
472
  else {
459
473
  this.error(`Failed to obtain tweaked signer for input ${i}.`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.3.3",
4
+ "version": "1.3.4",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -174,9 +174,30 @@ export class TransactionFactory {
174
174
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
175
175
  }
176
176
 
177
+ const inputs = (interactionParameters.optionalInputs || []).map((input) => {
178
+ let nonWitness = input.nonWitnessUtxo;
179
+ if (
180
+ nonWitness &&
181
+ !(nonWitness instanceof Uint8Array) &&
182
+ typeof nonWitness === 'object'
183
+ ) {
184
+ nonWitness = Buffer.from(
185
+ Uint8Array.from(
186
+ Object.values(input.nonWitnessUtxo as unknown as Record<number, number>),
187
+ ),
188
+ );
189
+ }
190
+
191
+ return {
192
+ ...input,
193
+ nonWitnessUtxo: nonWitness,
194
+ };
195
+ });
196
+
177
197
  const preTransaction: InteractionTransaction = new InteractionTransaction({
178
198
  ...interactionParameters,
179
199
  utxos: [interactionParameters.utxos[0]], // we simulate one input here.
200
+ optionalInputs: inputs,
180
201
  });
181
202
 
182
203
  // we don't sign that transaction, we just need the parameters.
@@ -221,11 +242,14 @@ export class TransactionFactory {
221
242
 
222
243
  const newParams: IInteractionParameters = {
223
244
  ...interactionParameters,
224
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0), // always 0
245
+ utxos: [
246
+ ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
247
+ ], // always 0
225
248
  randomBytes: preTransaction.getRndBytes(),
226
249
  preimage: preTransaction.getPreimage(),
227
250
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
228
251
  estimatedFees: preTransaction.estimatedFees,
252
+ optionalInputs: inputs,
229
253
  };
230
254
 
231
255
  const finalTransaction: InteractionTransaction = new InteractionTransaction(newParams);
@@ -399,7 +423,7 @@ export class TransactionFactory {
399
423
  private async detectInteractionOPWallet(
400
424
  interactionParameters: IInteractionParameters | InteractionParametersWithoutSigner,
401
425
  ): Promise<InteractionResponse | null> {
402
- if (typeof window === 'undefined' || !window.opnet || !window.opnet.web3) {
426
+ if (typeof window === 'undefined' || !window || !window.opnet || !window.opnet.web3) {
403
427
  return null;
404
428
  }
405
429
 
@@ -426,6 +450,7 @@ export class TransactionFactory {
426
450
  const challengeTransaction: ChallengeSolutionTransaction = new ChallengeSolutionTransaction(
427
451
  parameters,
428
452
  );
453
+
429
454
  const signedTransaction: Transaction = await challengeTransaction.signTransaction();
430
455
  if (!signedTransaction) {
431
456
  throw new Error('Could not sign funding transaction.');
@@ -83,6 +83,8 @@ export class UnisatSigner extends CustomKeypair {
83
83
  }
84
84
 
85
85
  public get unisat(): Unisat {
86
+ if (!window) throw new Error('Window not found');
87
+
86
88
  const module = window.unisat;
87
89
  if (!module) {
88
90
  throw new Error('Unisat extension not found');
@@ -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,9 +797,11 @@ 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];
800
+ if (i === 0) {
801
+ // TapLeafScript if available
802
+ if (this.tapLeafScript) {
803
+ input.tapLeafScript = [this.tapLeafScript];
804
+ }
794
805
  }
795
806
 
796
807
  // If the first input and we have a global nonWitnessUtxo not yet set
@@ -798,6 +809,18 @@ export abstract class TweakedTransaction extends Logger {
798
809
  input.nonWitnessUtxo = this.nonWitnessUtxo;
799
810
  }
800
811
 
812
+ /*if (utxo.nonWitnessUtxo && extra) {
813
+ const witness = Buffer.isBuffer(utxo.nonWitnessUtxo)
814
+ ? utxo.nonWitnessUtxo
815
+ : typeof utxo.nonWitnessUtxo === 'string'
816
+ ? Buffer.from(utxo.nonWitnessUtxo, 'hex')
817
+ : (utxo.nonWitnessUtxo as unknown) instanceof Uint8Array
818
+ ? Buffer.from(utxo.nonWitnessUtxo)
819
+ : undefined;
820
+
821
+ input.nonWitnessUtxo = witness;
822
+ }*/
823
+
801
824
  return input;
802
825
  }
803
826
 
@@ -861,7 +884,16 @@ export abstract class TweakedTransaction extends Logger {
861
884
  }
862
885
 
863
886
  if (tweakedSigner) {
864
- await this.signTaprootInput(tweakedSigner, transaction, i);
887
+ try {
888
+ await this.signTaprootInput(tweakedSigner, transaction, i);
889
+ } catch (e) {
890
+ tweakedSigner = this.getTweakedSigner(false, this.signer);
891
+ if (!tweakedSigner) {
892
+ throw new Error(`Failed to obtain tweaked signer for input ${i}.`);
893
+ }
894
+
895
+ await this.signTaprootInput(tweakedSigner, transaction, i);
896
+ }
865
897
  } else {
866
898
  this.error(`Failed to obtain tweaked signer for input ${i}.`);
867
899
  }