@btc-vision/transaction 1.0.105 → 1.0.107

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 (52) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/keypair/EcKeyPair.d.ts +4 -4
  4. package/browser/transaction/builders/MultiSignTransaction.d.ts +1 -1
  5. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -0
  6. package/browser/transaction/builders/TransactionBuilder.d.ts +4 -2
  7. package/browser/transaction/builders/UnwrapSegwitTransaction.d.ts +1 -1
  8. package/browser/transaction/builders/UnwrapTransaction.d.ts +1 -1
  9. package/browser/transaction/interfaces/ITransactionParameters.d.ts +9 -8
  10. package/browser/transaction/processor/PsbtTransaction.d.ts +1 -1
  11. package/browser/transaction/shared/TweakedTransaction.d.ts +2 -1
  12. package/browser/utxo/OPNetLimitedProvider.d.ts +11 -1
  13. package/browser/utxo/interfaces/IUTXO.d.ts +3 -1
  14. package/build/_version.d.ts +1 -1
  15. package/build/_version.js +1 -1
  16. package/build/keypair/EcKeyPair.d.ts +4 -4
  17. package/build/transaction/TransactionFactory.js +2 -1
  18. package/build/transaction/builders/CustomScriptTransaction.js +1 -4
  19. package/build/transaction/builders/MultiSignTransaction.d.ts +1 -1
  20. package/build/transaction/builders/MultiSignTransaction.js +2 -2
  21. package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -0
  22. package/build/transaction/builders/SharedInteractionTransaction.js +43 -31
  23. package/build/transaction/builders/TransactionBuilder.d.ts +4 -2
  24. package/build/transaction/builders/TransactionBuilder.js +17 -5
  25. package/build/transaction/builders/UnwrapSegwitTransaction.d.ts +1 -1
  26. package/build/transaction/builders/UnwrapSegwitTransaction.js +2 -3
  27. package/build/transaction/builders/UnwrapTransaction.d.ts +1 -1
  28. package/build/transaction/builders/UnwrapTransaction.js +2 -2
  29. package/build/transaction/interfaces/ITransactionParameters.d.ts +9 -8
  30. package/build/transaction/processor/PsbtTransaction.d.ts +1 -1
  31. package/build/transaction/processor/PsbtTransaction.js +2 -2
  32. package/build/transaction/shared/TweakedTransaction.d.ts +2 -1
  33. package/build/transaction/shared/TweakedTransaction.js +30 -10
  34. package/build/utxo/OPNetLimitedProvider.d.ts +11 -1
  35. package/build/utxo/OPNetLimitedProvider.js +49 -2
  36. package/build/utxo/interfaces/IUTXO.d.ts +3 -1
  37. package/eslint.config.js +0 -1
  38. package/package.json +2 -2
  39. package/src/_version.ts +1 -1
  40. package/src/keypair/EcKeyPair.ts +4 -4
  41. package/src/transaction/TransactionFactory.ts +3 -3
  42. package/src/transaction/builders/CustomScriptTransaction.ts +3 -7
  43. package/src/transaction/builders/MultiSignTransaction.ts +3 -2
  44. package/src/transaction/builders/SharedInteractionTransaction.ts +61 -34
  45. package/src/transaction/builders/TransactionBuilder.ts +38 -11
  46. package/src/transaction/builders/UnwrapSegwitTransaction.ts +5 -5
  47. package/src/transaction/builders/UnwrapTransaction.ts +6 -2
  48. package/src/transaction/interfaces/ITransactionParameters.ts +11 -9
  49. package/src/transaction/processor/PsbtTransaction.ts +2 -2
  50. package/src/transaction/shared/TweakedTransaction.ts +38 -10
  51. package/src/utxo/OPNetLimitedProvider.ts +86 -4
  52. package/src/utxo/interfaces/IUTXO.ts +3 -1
@@ -33,7 +33,7 @@ export declare abstract class TweakedTransaction extends Logger {
33
33
  protected regenerated: boolean;
34
34
  protected ignoreSignatureErrors: boolean;
35
35
  protected constructor(data: ITweakedTransactionData);
36
- static readScriptWitnessToWitnessStack(buffer: Buffer): Buffer[];
36
+ static readScriptWitnessToWitnessStack(Buffer: Buffer): Buffer[];
37
37
  static preEstimateTaprootTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numWitnessElements: bigint, witnessElementSize: bigint, emptyWitness: bigint, taprootControlWitnessSize?: bigint, taprootScriptSize?: bigint): bigint;
38
38
  protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer, sighashTypes: number[]): void;
39
39
  protected static calculateSignHash(sighashTypes: number[]): number;
@@ -48,6 +48,7 @@ export declare abstract class TweakedTransaction extends Logger {
48
48
  protected generateScriptAddress(): Payment;
49
49
  protected getSignerKey(): Signer;
50
50
  protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer): Promise<void>;
51
+ protected splitArray<T>(arr: T[], chunkSize: number): T[][];
51
52
  protected signInputs(transaction: Psbt): Promise<void>;
52
53
  protected internalPubKeyToXOnly(): Buffer;
53
54
  protected internalInit(): void;
@@ -28,15 +28,15 @@ export class TweakedTransaction extends Logger {
28
28
  this.nonWitnessUtxo = data.nonWitnessUtxo;
29
29
  this.isBrowser = typeof window !== 'undefined';
30
30
  }
31
- static readScriptWitnessToWitnessStack(buffer) {
31
+ static readScriptWitnessToWitnessStack(Buffer) {
32
32
  let offset = 0;
33
33
  function readSlice(n) {
34
- const slice = buffer.subarray(offset, offset + n);
34
+ const slice = Buffer.subarray(offset, offset + n);
35
35
  offset += n;
36
36
  return slice;
37
37
  }
38
38
  function readVarInt() {
39
- const varint = varuint.decode(buffer, offset);
39
+ const varint = varuint.decode(Buffer, offset);
40
40
  offset += varuint.decode.bytes;
41
41
  return varint;
42
42
  }
@@ -193,15 +193,35 @@ export class TweakedTransaction extends Logger {
193
193
  }
194
194
  }
195
195
  }
196
+ splitArray(arr, chunkSize) {
197
+ if (chunkSize <= 0) {
198
+ throw new Error('Chunk size must be greater than 0.');
199
+ }
200
+ const result = [];
201
+ for (let i = 0; i < arr.length; i += chunkSize) {
202
+ result.push(arr.slice(i, i + chunkSize));
203
+ }
204
+ return result;
205
+ }
196
206
  async signInputs(transaction) {
197
- for (let i = 0; i < transaction.data.inputs.length; i++) {
198
- const input = transaction.data.inputs[i];
199
- try {
200
- await this.signInput(transaction, input, i);
201
- }
202
- catch (e) {
203
- this.log(`Failed to sign input ${i}: ${e.stack}`);
207
+ const txs = transaction.data.inputs;
208
+ const batchSize = 20;
209
+ const batches = this.splitArray(txs, batchSize);
210
+ for (let i = 0; i < batches.length; i++) {
211
+ const batch = batches[i];
212
+ const promises = [];
213
+ const offset = i * batchSize;
214
+ for (let j = 0; j < batch.length; j++) {
215
+ const index = offset + j;
216
+ const input = batch[j];
217
+ try {
218
+ promises.push(this.signInput(transaction, input, index));
219
+ }
220
+ catch (e) {
221
+ this.log(`Failed to sign input ${index}: ${e.stack}`);
222
+ }
204
223
  }
224
+ await Promise.all(promises);
205
225
  }
206
226
  try {
207
227
  transaction.finalizeAllInputs();
@@ -1,8 +1,15 @@
1
1
  import { Address } from '@btc-vision/bsi-binary';
2
+ import { Network } from 'bitcoinjs-lib';
3
+ import { Wallet } from '../opnet.js';
2
4
  import { UnwrapGeneration } from '../wbtc/UnwrapGeneration.js';
3
5
  import { WrappedGeneration } from '../wbtc/WrappedGenerationParameters.js';
4
6
  import { BroadcastResponse } from './interfaces/BroadcastResponse.js';
5
- import { FetchUTXOParams, FetchUTXOParamsMultiAddress, UTXO } from './interfaces/IUTXO.js';
7
+ import { FetchUTXOParams, FetchUTXOParamsMultiAddress, RawUTXOResponse, UTXO } from './interfaces/IUTXO.js';
8
+ export interface WalletUTXOs {
9
+ readonly confirmed: RawUTXOResponse[];
10
+ readonly pending: RawUTXOResponse[];
11
+ readonly spentTransactions: RawUTXOResponse[];
12
+ }
6
13
  export declare class OPNetLimitedProvider {
7
14
  private readonly opnetAPIUrl;
8
15
  private readonly utxoPath;
@@ -11,6 +18,9 @@ export declare class OPNetLimitedProvider {
11
18
  fetchUTXO(settings: FetchUTXOParams): Promise<UTXO[]>;
12
19
  fetchUTXOMultiAddr(settings: FetchUTXOParamsMultiAddress): Promise<UTXO[]>;
13
20
  broadcastTransaction(transaction: string, psbt: boolean): Promise<BroadcastResponse | undefined>;
21
+ splitUTXOs(wallet: Wallet, network: Network, splitInputsInto: number, amountPerUTXO: bigint): Promise<BroadcastResponse | {
22
+ error: string;
23
+ }>;
14
24
  rpcMethod(method: string, paramsMethod: unknown[]): Promise<unknown>;
15
25
  fetchWrapParameters(amount: bigint): Promise<WrappedGeneration | undefined>;
16
26
  fetchUnWrapParameters(amount: bigint, receiver: Address): Promise<UnwrapGeneration | undefined>;
@@ -1,4 +1,5 @@
1
1
  import { currentConsensusConfig } from '../consensus/ConsensusConfig.js';
2
+ import { TransactionFactory } from '../opnet.js';
2
3
  import { UnwrapGeneration } from '../wbtc/UnwrapGeneration.js';
3
4
  import { WrappedGeneration } from '../wbtc/WrappedGenerationParameters.js';
4
5
  export class OPNetLimitedProvider {
@@ -8,6 +9,12 @@ export class OPNetLimitedProvider {
8
9
  this.rpc = 'json-rpc';
9
10
  }
10
11
  async fetchUTXO(settings) {
12
+ if (settings.usePendingUTXO === undefined) {
13
+ settings.usePendingUTXO = true;
14
+ }
15
+ if (settings.optimized === undefined) {
16
+ settings.optimized = true;
17
+ }
11
18
  const params = {
12
19
  method: 'GET',
13
20
  headers: {
@@ -20,10 +27,21 @@ export class OPNetLimitedProvider {
20
27
  throw new Error(`Failed to fetch UTXO data: ${resp.statusText}`);
21
28
  }
22
29
  const fetchedData = (await resp.json());
23
- if (fetchedData.length === 0) {
30
+ const allUtxos = settings.usePendingUTXO
31
+ ? [...fetchedData.confirmed, ...fetchedData.pending]
32
+ : fetchedData.confirmed;
33
+ const unspentUTXOs = [];
34
+ for (const utxo of allUtxos) {
35
+ if (fetchedData.spentTransactions.some((spent) => spent.transactionId === utxo.transactionId &&
36
+ spent.outputIndex === utxo.outputIndex)) {
37
+ continue;
38
+ }
39
+ unspentUTXOs.push(utxo);
40
+ }
41
+ if (unspentUTXOs.length === 0) {
24
42
  throw new Error('No UTXO found');
25
43
  }
26
- const meetCriteria = fetchedData.filter((utxo) => {
44
+ const meetCriteria = unspentUTXOs.filter((utxo) => {
27
45
  return BigInt(utxo.value) >= settings.minAmount;
28
46
  });
29
47
  if (meetCriteria.length === 0) {
@@ -58,6 +76,7 @@ export class OPNetLimitedProvider {
58
76
  minAmount: settings.minAmount,
59
77
  requestedAmount: settings.requestedAmount,
60
78
  optimized: settings.optimized,
79
+ usePendingUTXO: settings.usePendingUTXO,
61
80
  };
62
81
  const promise = this.fetchUTXO(params).catch(() => {
63
82
  return [];
@@ -86,6 +105,34 @@ export class OPNetLimitedProvider {
86
105
  }
87
106
  return result;
88
107
  }
108
+ async splitUTXOs(wallet, network, splitInputsInto, amountPerUTXO) {
109
+ const utxoSetting = {
110
+ addresses: [wallet.p2wpkh, wallet.p2tr],
111
+ minAmount: 330n,
112
+ requestedAmount: 1000000000000000n,
113
+ };
114
+ const utxos = await this.fetchUTXOMultiAddr(utxoSetting);
115
+ if (!utxos || !utxos.length)
116
+ return { error: 'No UTXOs found' };
117
+ const amount = BigInt(splitInputsInto) * amountPerUTXO;
118
+ const fundingTransactionParameters = {
119
+ amount: amount,
120
+ feeRate: 500,
121
+ from: wallet.p2tr,
122
+ utxos: utxos,
123
+ signer: wallet.keypair,
124
+ network,
125
+ to: wallet.p2tr,
126
+ splitInputsInto,
127
+ priorityFee: 330n,
128
+ };
129
+ const transactionFactory = new TransactionFactory();
130
+ const fundingTx = await transactionFactory.createBTCTransfer(fundingTransactionParameters);
131
+ const broadcastResponse = await this.broadcastTransaction(fundingTx.tx, false);
132
+ if (!broadcastResponse)
133
+ return { error: 'Could not broadcast transaction' };
134
+ return broadcastResponse;
135
+ }
89
136
  async rpcMethod(method, paramsMethod) {
90
137
  const params = {
91
138
  method: 'POST',
@@ -9,13 +9,15 @@ export interface FetchUTXOParams {
9
9
  readonly address: string;
10
10
  readonly minAmount: bigint;
11
11
  readonly requestedAmount: bigint;
12
- readonly optimized?: boolean;
12
+ optimized?: boolean;
13
+ usePendingUTXO?: boolean;
13
14
  }
14
15
  export interface FetchUTXOParamsMultiAddress {
15
16
  readonly addresses: string[];
16
17
  readonly minAmount: bigint;
17
18
  readonly requestedAmount: bigint;
18
19
  readonly optimized?: boolean;
20
+ readonly usePendingUTXO?: boolean;
19
21
  }
20
22
  export interface RawUTXOResponse {
21
23
  readonly transactionId: string;
package/eslint.config.js CHANGED
@@ -28,7 +28,6 @@ export default tseslint.config(
28
28
  '@typescript-eslint/no-unnecessary-type-parameters': 'off',
29
29
  '@typescript-eslint/no-duplicate-enum-values': 'off',
30
30
  'prefer-spread': 'off',
31
- '@typescript-eslint/no-empty-object-type': 'off',
32
31
  },
33
32
  },
34
33
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.0.105",
4
+ "version": "1.0.107",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -92,7 +92,7 @@
92
92
  "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
93
93
  "@bitcoinerlab/secp256k1": "^1.1.1",
94
94
  "@btc-vision/bsi-binary": "^1.0.42",
95
- "@btc-vision/bsi-bitcoin-rpc": "^1.0.28",
95
+ "@btc-vision/bsi-bitcoin-rpc": "^1.0.29",
96
96
  "@btc-vision/logger": "^1.0.6",
97
97
  "@eslint/js": "^9.10.0",
98
98
  "assert": "^2.1.0",
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.0.101';
1
+ export const version = '1.0.107';
@@ -1,9 +1,9 @@
1
1
  import * as ecc from '@bitcoinerlab/secp256k1';
2
2
  import { Address } from '@btc-vision/bsi-binary';
3
- import bip32, { BIP32Factory, BIP32Interface } from 'bip32';
3
+ import bip32, { BIP32API, BIP32Factory, BIP32Interface } from 'bip32';
4
4
  import { address, initEccLib, Network, networks, payments } from 'bitcoinjs-lib';
5
5
  import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
6
- import { ECPairFactory, ECPairInterface } from 'ecpair';
6
+ import { ECPairAPI, ECPairFactory, ECPairInterface } from 'ecpair';
7
7
  import { IWallet } from './interfaces/IWallet.js';
8
8
 
9
9
  initEccLib(ecc);
@@ -22,8 +22,8 @@ if (!BIP32factory) {
22
22
  * @example import { EcKeyPair } from '@btc-vision/transaction';
23
23
  */
24
24
  export class EcKeyPair {
25
- public static BIP32 = BIP32factory(ecc);
26
- public static ECPair = ECPairFactory(ecc);
25
+ public static BIP32: BIP32API = BIP32factory(ecc);
26
+ public static ECPair: ECPairAPI = ECPairFactory(ecc);
27
27
 
28
28
  /**
29
29
  * Generate a keypair from a WIF
@@ -224,8 +224,7 @@ export class TransactionFactory {
224
224
  // Initial generation
225
225
  await preTransaction.signTransaction();
226
226
 
227
- const parameters: IFundingTransactionParameters =
228
- await preTransaction.getFundingTransactionParameters();
227
+ const parameters: IFundingTransactionParameters =await preTransaction.getFundingTransactionParameters();
229
228
 
230
229
  const fundingTransaction: FundingTransaction = new FundingTransaction(parameters);
231
230
  const signedTransaction: Transaction = await fundingTransaction.signTransaction();
@@ -249,6 +248,7 @@ export class TransactionFactory {
249
248
  utxos: [newUtxo],
250
249
  randomBytes: preTransaction.getRndBytes(),
251
250
  nonWitnessUtxo: signedTransaction.toBuffer(),
251
+ optionalOutputs: []
252
252
  };
253
253
 
254
254
  const finalTransaction: DeploymentTransaction = new DeploymentTransaction(newParams);
@@ -291,7 +291,7 @@ export class TransactionFactory {
291
291
  const childTransactionRequiredValue: bigint =
292
292
  wrapParameters.amount +
293
293
  currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT +
294
- (wrapParameters.priorityFee || 300n);
294
+ (wrapParameters.priorityFee || 330n);
295
295
 
296
296
  const wbtc: wBTC = new wBTC(wrapParameters.network, wrapParameters.chainId);
297
297
  const to = wbtc.getAddress();
@@ -255,17 +255,13 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
255
255
  /**
256
256
  * Finalize the transaction
257
257
  * @param _inputIndex
258
- * @param input
258
+ * @param _input
259
259
  */
260
- private customFinalizer = (_inputIndex: number, input: PsbtInput) => {
260
+ private customFinalizer = (_inputIndex: number, _input: PsbtInput) => {
261
261
  if (!this.tapLeafScript) {
262
262
  throw new Error('Tap leaf script is required');
263
263
  }
264
-
265
- if (!input.tapScriptSig) {
266
- throw new Error('Tap script signature is required');
267
- }
268
-
264
+
269
265
  const scriptSolution = this.witnesses;
270
266
  const witness = scriptSolution
271
267
  .concat(this.tapLeafScript.script)
@@ -493,16 +493,17 @@ export class MultiSignTransaction extends TransactionBuilder<TransactionType.MUL
493
493
  /**
494
494
  * Builds the transaction.
495
495
  * @param {Psbt} transaction - The transaction to build
496
+ * @param checkPartialSigs
496
497
  * @protected
497
498
  * @returns {Promise<boolean>}
498
499
  * @throws {Error} - If something went wrong while building the transaction
499
500
  */
500
- protected override async internalBuildTransaction(transaction: Psbt): Promise<boolean> {
501
+ protected override async internalBuildTransaction(transaction: Psbt, checkPartialSigs: boolean = false): Promise<boolean> {
501
502
  const inputs: PsbtInputExtended[] = this.getInputs();
502
503
  const outputs: PsbtOutputExtended[] = this.getOutputs();
503
504
 
504
505
  transaction.setMaximumFeeRate(this._maximumFeeRate);
505
- transaction.addInputs(inputs);
506
+ transaction.addInputs(inputs, checkPartialSigs);
506
507
 
507
508
  for (let i = 0; i < this.updateInputs.length; i++) {
508
509
  transaction.updateInput(i, this.updateInputs[i]);
@@ -185,44 +185,37 @@ export abstract class SharedInteractionTransaction<
185
185
  return;
186
186
  }
187
187
 
188
- for (let i = 0; i < transaction.data.inputs.length; i++) {
189
- const input: PsbtInput = transaction.data.inputs[i];
190
- let finalized: boolean = false;
191
- let signed: boolean = false;
188
+ const txs: PsbtInput[] = transaction.data.inputs;
189
+ for (let i = 0; i < txs.length; i += 20) {
190
+ const batch = txs.slice(i, i + 20);
192
191
 
193
- try {
194
- await this.signInput(transaction, input, i, this.scriptSigner);
195
- signed = true;
196
- } catch (e) {}
197
-
198
- try {
199
- await this.signInput(transaction, input, i);
200
- signed = true;
201
- } catch (e) {}
192
+ const promises: Promise<void>[] = [];
193
+ for (let y = 0; y < batch.length; y++) {
194
+ const offset = i * 20 + y;
195
+ const input = batch[y];
202
196
 
203
- try {
204
- transaction.finalizeInput(0, this.customFinalizer);
205
- finalized = true;
206
- } catch (e) {}
197
+ promises.push(this.signIndividualInputs(transaction, input, offset));
198
+ }
207
199
 
208
- try {
209
- transaction.finalizeInput(i);
210
- finalized = true;
211
- } catch (e) {}
200
+ await Promise.all(promises).catch((e: unknown) => {
201
+ throw e;
202
+ });
203
+ }
212
204
 
213
- if (signed || finalized) {
214
- this.log(
215
- `Signed input or finalized input #${i} out of ${transaction.data.inputs.length}! {Signed: ${signed}, Finalized: ${finalized}}`,
216
- );
205
+ try {
206
+ transaction.finalizeInput(0, this.customFinalizer);
217
207
 
218
- continue;
219
- }
208
+ this.log(`Finalized input 0!`);
209
+ } catch (e) {
210
+ console.log(`Failed to finalize input 0: ${(e as Error).stack}`);
211
+ }
220
212
 
221
- if (this.regenerated || this.ignoreSignatureErrors) {
222
- continue;
223
- }
213
+ for (let i = 0; i < txs.length; i++) {
214
+ try {
215
+ transaction.finalizeInput(i);
224
216
 
225
- throw new Error('Failed to sign input');
217
+ this.log(`Finalized input ${i}!`);
218
+ } catch {}
226
219
  }
227
220
  }
228
221
 
@@ -310,9 +303,11 @@ export abstract class SharedInteractionTransaction<
310
303
  throw new Error('Tap leaf script is required');
311
304
  }
312
305
 
313
- if (!input.tapScriptSig) {
314
- throw new Error('Tap script signature is required');
315
- }
306
+ //console.log('finalizing input', input);
307
+
308
+ //if (!input.tapLeafScript) {
309
+ // throw new Error('Tap script signature is required');
310
+ //}
316
311
 
317
312
  if (!this.contractSecret) {
318
313
  throw new Error('Contract secret is required');
@@ -328,6 +323,38 @@ export abstract class SharedInteractionTransaction<
328
323
  };
329
324
  };
330
325
 
326
+ private async signIndividualInputs(
327
+ transaction: Psbt,
328
+ input: PsbtInput,
329
+ i: number,
330
+ ): Promise<void> {
331
+ let signed: boolean = false;
332
+
333
+ try {
334
+ await this.signInput(transaction, input, i, this.scriptSigner);
335
+ signed = true;
336
+ } catch {}
337
+
338
+ try {
339
+ await this.signInput(transaction, input, i);
340
+ signed = true;
341
+ } catch {}
342
+
343
+ if (signed) {
344
+ this.log(
345
+ `Signed input #${i} out of ${transaction.data.inputs.length}! {Signed: ${signed}}`,
346
+ );
347
+
348
+ return;
349
+ }
350
+
351
+ if (this.regenerated || this.ignoreSignatureErrors) {
352
+ return;
353
+ }
354
+
355
+ throw new Error('Failed to sign input');
356
+ }
357
+
331
358
  /**
332
359
  * Get the public keys
333
360
  * @private
@@ -47,6 +47,11 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
47
47
  * @description The estimated fees of the transaction
48
48
  */
49
49
  public estimatedFees: bigint = 0n;
50
+ /**
51
+ * @param {ITransactionParameters} parameters - The transaction parameters
52
+ */
53
+
54
+ public optionalOutputs: PsbtOutputExtended[] | undefined;
50
55
  /**
51
56
  * @description The transaction itself.
52
57
  */
@@ -102,9 +107,6 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
102
107
  */
103
108
  protected _maximumFeeRate: number = 100000000;
104
109
 
105
- /**
106
- * @param {ITransactionParameters} parameters - The transaction parameters
107
- */
108
110
  protected constructor(parameters: ITransactionParameters) {
109
111
  super(parameters);
110
112
 
@@ -119,6 +121,8 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
119
121
  this.utxos = parameters.utxos;
120
122
  this.to = parameters.to || undefined;
121
123
 
124
+ this.optionalOutputs = parameters.optionalOutputs;
125
+
122
126
  this.from = TransactionBuilder.getFrom(
123
127
  parameters.from,
124
128
  this.signer as ECPairInterface,
@@ -195,6 +199,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
195
199
  priorityFee: this.priorityFee ?? 0n,
196
200
  from: this.from,
197
201
  amount: this.estimatedFees,
202
+ optionalOutputs: this.optionalOutputs,
198
203
  };
199
204
  }
200
205
 
@@ -253,7 +258,9 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
253
258
  * @description Generates the transaction minimal signatures
254
259
  * @public
255
260
  */
256
- public async generateTransactionMinimalSignatures(): Promise<void> {
261
+ public async generateTransactionMinimalSignatures(
262
+ checkPartialSigs: boolean = false,
263
+ ): Promise<void> {
257
264
  if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
258
265
  throw new Error(
259
266
  'Invalid contract address. The contract address must be a taproot address.',
@@ -267,7 +274,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
267
274
  const outputs: PsbtOutputExtended[] = this.getOutputs();
268
275
 
269
276
  this.transaction.setMaximumFeeRate(this._maximumFeeRate);
270
- this.transaction.addInputs(inputs);
277
+ this.transaction.addInputs(inputs, checkPartialSigs);
271
278
 
272
279
  for (let i = 0; i < this.updateInputs.length; i++) {
273
280
  this.transaction.updateInput(i, this.updateInputs[i]);
@@ -358,7 +365,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
358
365
  const fee: number = this.feeRate * size;
359
366
 
360
367
  this.estimatedFees = BigInt(Math.ceil(fee) + 1);
361
-
368
+
362
369
  return this.estimatedFees;
363
370
  } else {
364
371
  throw new Error(
@@ -409,8 +416,8 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
409
416
  */
410
417
  protected async addRefundOutput(amountSpent: bigint): Promise<void> {
411
418
  /** Add the refund output */
412
- const sendBackAmount: bigint = this.totalInputAmount - amountSpent;
413
-
419
+ const sendBackAmount: bigint =
420
+ this.totalInputAmount - amountSpent - this.addOptionalOutputsAndGetAmount();
414
421
  if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
415
422
  if (AddressVerificator.isValidP2TRAddress(this.from, this.network)) {
416
423
  await this.setFeeOutput({
@@ -479,7 +486,6 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
479
486
  for (const utxo of this.utxos) {
480
487
  total += utxo.value;
481
488
  }
482
-
483
489
  return total;
484
490
  }
485
491
 
@@ -497,6 +503,23 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
497
503
  return total;
498
504
  }
499
505
 
506
+ /**
507
+ * @description Adds optional outputs to transaction and returns their total value in satoshi to calculate refund transaction
508
+ * @protected
509
+ * @returns {bigint}
510
+ */
511
+ protected addOptionalOutputsAndGetAmount(): bigint {
512
+ if (!this.optionalOutputs) return 0n;
513
+
514
+ let refundedFromOptionalOutputs = 0n;
515
+
516
+ for (let i = 0; i < this.optionalOutputs.length; i++) {
517
+ this.addOutput(this.optionalOutputs[i]);
518
+ refundedFromOptionalOutputs += BigInt(this.optionalOutputs[i].value);
519
+ }
520
+ return refundedFromOptionalOutputs;
521
+ }
522
+
500
523
  /**
501
524
  * @description Adds the inputs from the utxos
502
525
  * @protected
@@ -634,17 +657,21 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
634
657
  /**
635
658
  * Builds the transaction.
636
659
  * @param {Psbt} transaction - The transaction to build
660
+ * @param checkPartialSigs
637
661
  * @protected
638
662
  * @returns {Promise<boolean>}
639
663
  * @throws {Error} - If something went wrong while building the transaction
640
664
  */
641
- protected async internalBuildTransaction(transaction: Psbt): Promise<boolean> {
665
+ protected async internalBuildTransaction(
666
+ transaction: Psbt,
667
+ checkPartialSigs: boolean = false,
668
+ ): Promise<boolean> {
642
669
  if (transaction.data.inputs.length === 0) {
643
670
  const inputs: PsbtInputExtended[] = this.getInputs();
644
671
  const outputs: PsbtOutputExtended[] = this.getOutputs();
645
672
 
646
673
  transaction.setMaximumFeeRate(this._maximumFeeRate);
647
- transaction.addInputs(inputs);
674
+ transaction.addInputs(inputs, checkPartialSigs);
648
675
 
649
676
  for (let i = 0; i < this.updateInputs.length; i++) {
650
677
  transaction.updateInput(i, this.updateInputs[i]);
@@ -196,13 +196,13 @@ export class UnwrapSegwitTransaction extends SharedInteractionTransaction<Transa
196
196
  * @returns {Promise<boolean>}
197
197
  * @throws {Error} - If something went wrong while building the transaction
198
198
  */
199
- protected async internalBuildTransaction(transaction: Psbt): Promise<boolean> {
199
+ protected async internalBuildTransaction(transaction: Psbt, checkPartialSigs: boolean = false): Promise<boolean> {
200
200
  if (transaction.data.inputs.length === 0) {
201
201
  const inputs: PsbtInputExtended[] = this.getInputs();
202
202
  const outputs: PsbtOutputExtended[] = this.getOutputs();
203
203
 
204
204
  transaction.setMaximumFeeRate(this._maximumFeeRate);
205
- transaction.addInputs(inputs);
205
+ transaction.addInputs(inputs, checkPartialSigs);
206
206
 
207
207
  for (let i = 0; i < this.updateInputs.length; i++) {
208
208
  transaction.updateInput(i, this.updateInputs[i]);
@@ -313,9 +313,9 @@ export class UnwrapSegwitTransaction extends SharedInteractionTransaction<Transa
313
313
  this.addVaultUTXO(utxo, p2wshOutput);
314
314
 
315
315
  if (firstSigner) {
316
- this.log(
317
- `Signing input ${inputIndex} with ${firstSigner.publicKey.toString('hex')}`,
318
- );
316
+ //this.log(
317
+ // `Signing input ${inputIndex} with ${firstSigner.publicKey.toString('hex')}`,
318
+ //);
319
319
 
320
320
  // we don't care if we fail to sign the input
321
321
  try {
@@ -381,17 +381,21 @@ export class UnwrapTransaction extends SharedInteractionTransaction<TransactionT
381
381
  /**
382
382
  * Builds the transaction.
383
383
  * @param {Psbt} transaction - The transaction to build
384
+ * @param checkPartialSigs
384
385
  * @protected
385
386
  * @returns {Promise<boolean>}
386
387
  * @throws {Error} - If something went wrong while building the transaction
387
388
  */
388
- protected async internalBuildTransaction(transaction: Psbt): Promise<boolean> {
389
+ protected async internalBuildTransaction(
390
+ transaction: Psbt,
391
+ checkPartialSigs: boolean = false,
392
+ ): Promise<boolean> {
389
393
  if (transaction.data.inputs.length === 0) {
390
394
  const inputs: PsbtInputExtended[] = this.getInputs();
391
395
  const outputs: PsbtOutputExtended[] = this.getOutputs();
392
396
 
393
397
  transaction.setMaximumFeeRate(this._maximumFeeRate);
394
- transaction.addInputs(inputs);
398
+ transaction.addInputs(inputs, checkPartialSigs);
395
399
 
396
400
  for (let i = 0; i < this.updateInputs.length; i++) {
397
401
  transaction.updateInput(i, this.updateInputs[i]);