@btc-vision/transaction 1.6.4 → 1.6.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 (60) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/generators/builders/P2WDAGenerator.d.ts +13 -0
  3. package/browser/index.js +1 -1
  4. package/browser/keypair/Address.d.ts +3 -2
  5. package/browser/keypair/AddressVerificator.d.ts +12 -1
  6. package/browser/keypair/Wallet.d.ts +3 -0
  7. package/browser/opnet.d.ts +4 -0
  8. package/browser/p2wda/P2WDADetector.d.ts +16 -0
  9. package/browser/transaction/TransactionFactory.d.ts +3 -1
  10. package/browser/transaction/builders/DeploymentTransaction.d.ts +3 -3
  11. package/browser/transaction/builders/InteractionTransactionP2WDA.d.ts +37 -0
  12. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +4 -4
  13. package/browser/transaction/mineable/IP2WSHAddress.d.ts +4 -0
  14. package/browser/transaction/mineable/TimelockGenerator.d.ts +2 -5
  15. package/browser/transaction/shared/TweakedTransaction.d.ts +5 -0
  16. package/build/_version.d.ts +1 -1
  17. package/build/_version.js +1 -1
  18. package/build/generators/builders/P2WDAGenerator.d.ts +13 -0
  19. package/build/generators/builders/P2WDAGenerator.js +62 -0
  20. package/build/keypair/Address.d.ts +3 -2
  21. package/build/keypair/Address.js +28 -2
  22. package/build/keypair/AddressVerificator.d.ts +12 -1
  23. package/build/keypair/AddressVerificator.js +78 -1
  24. package/build/keypair/Wallet.d.ts +3 -0
  25. package/build/keypair/Wallet.js +4 -0
  26. package/build/opnet.d.ts +4 -0
  27. package/build/opnet.js +4 -0
  28. package/build/p2wda/P2WDADetector.d.ts +16 -0
  29. package/build/p2wda/P2WDADetector.js +97 -0
  30. package/build/transaction/TransactionFactory.d.ts +3 -1
  31. package/build/transaction/TransactionFactory.js +35 -4
  32. package/build/transaction/builders/DeploymentTransaction.d.ts +3 -3
  33. package/build/transaction/builders/DeploymentTransaction.js +1 -1
  34. package/build/transaction/builders/InteractionTransactionP2WDA.d.ts +37 -0
  35. package/build/transaction/builders/InteractionTransactionP2WDA.js +205 -0
  36. package/build/transaction/builders/SharedInteractionTransaction.d.ts +4 -4
  37. package/build/transaction/builders/SharedInteractionTransaction.js +3 -3
  38. package/build/transaction/mineable/IP2WSHAddress.d.ts +4 -0
  39. package/build/transaction/mineable/IP2WSHAddress.js +1 -0
  40. package/build/transaction/mineable/TimelockGenerator.d.ts +2 -5
  41. package/build/transaction/shared/TweakedTransaction.d.ts +5 -0
  42. package/build/transaction/shared/TweakedTransaction.js +19 -0
  43. package/doc/README.md +0 -0
  44. package/doc/addresses/P2OP.md +1 -0
  45. package/doc/addresses/P2WDA.md +240 -0
  46. package/package.json +1 -1
  47. package/src/_version.ts +1 -1
  48. package/src/generators/builders/P2WDAGenerator.ts +174 -0
  49. package/src/keypair/Address.ts +58 -3
  50. package/src/keypair/AddressVerificator.ts +140 -1
  51. package/src/keypair/Wallet.ts +16 -0
  52. package/src/opnet.ts +4 -0
  53. package/src/p2wda/P2WDADetector.ts +218 -0
  54. package/src/transaction/TransactionFactory.ts +79 -5
  55. package/src/transaction/builders/DeploymentTransaction.ts +4 -3
  56. package/src/transaction/builders/InteractionTransactionP2WDA.ts +376 -0
  57. package/src/transaction/builders/SharedInteractionTransaction.ts +7 -6
  58. package/src/transaction/mineable/IP2WSHAddress.ts +4 -0
  59. package/src/transaction/mineable/TimelockGenerator.ts +2 -6
  60. package/src/transaction/shared/TweakedTransaction.ts +36 -0
@@ -0,0 +1,376 @@
1
+ import { Buffer } from 'buffer';
2
+ import { Psbt, PsbtInput, toXOnly } from '@btc-vision/bitcoin';
3
+ import { TransactionType } from '../enums/TransactionType.js';
4
+ import { IInteractionParameters } from '../interfaces/ITransactionParameters.js';
5
+ import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
6
+ import { MessageSigner } from '../../keypair/MessageSigner.js';
7
+ import { Compressor } from '../../bytecode/Compressor.js';
8
+ import { P2WDAGenerator } from '../../generators/builders/P2WDAGenerator.js';
9
+ import { Feature, Features } from '../../generators/Features.js';
10
+ import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
11
+ import { EcKeyPair } from '../../keypair/EcKeyPair.js';
12
+ import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
13
+ import { ECPairInterface } from 'ecpair';
14
+ import { P2WDADetector } from '../../p2wda/P2WDADetector.js';
15
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
16
+ import { TimeLockGenerator } from '../mineable/TimelockGenerator.js';
17
+
18
+ /**
19
+ * P2WDA Interaction Transaction
20
+ *
21
+ * This transaction type uses the exact same operation data as regular interactions
22
+ * (via CalldataGenerator), but embeds it in the witness field instead of a taproot script.
23
+ * This achieves 75% cost reduction through the witness discount.
24
+ */
25
+ export class InteractionTransactionP2WDA extends TransactionBuilder<TransactionType.INTERACTION> {
26
+ private static readonly MAX_WITNESS_FIELDS = 10;
27
+ private static readonly MAX_BYTES_PER_WITNESS = 80;
28
+
29
+ public readonly type: TransactionType.INTERACTION = TransactionType.INTERACTION;
30
+ protected readonly epochChallenge: IP2WSHAddress;
31
+ /**
32
+ * Disable auto refund
33
+ * @protected
34
+ */
35
+ protected readonly disableAutoRefund: boolean;
36
+ private readonly contractAddress: string;
37
+ private readonly contractSecret: Buffer;
38
+ private readonly calldata: Buffer;
39
+ private readonly challenge: ChallengeSolution;
40
+ private readonly randomBytes: Buffer;
41
+ private p2wdaGenerator: P2WDAGenerator;
42
+ private scriptSigner: ECPairInterface;
43
+ private p2wdaInputIndices: Set<number> = new Set();
44
+ /**
45
+ * The compiled operation data from CalldataGenerator
46
+ * This is exactly what would go in a taproot script, but we put it in witness instead
47
+ */
48
+ private readonly compiledOperationData: Buffer | null = null;
49
+
50
+ public constructor(parameters: IInteractionParameters) {
51
+ super(parameters);
52
+
53
+ if (!parameters.to) {
54
+ throw new Error('Contract address (to) is required');
55
+ }
56
+
57
+ if (!parameters.contract) {
58
+ throw new Error('Contract secret is required');
59
+ }
60
+
61
+ if (!parameters.calldata) {
62
+ throw new Error('Calldata is required');
63
+ }
64
+
65
+ if (!parameters.challenge) {
66
+ throw new Error('Challenge solution is required');
67
+ }
68
+
69
+ this.disableAutoRefund = parameters.disableAutoRefund || false;
70
+ this.contractAddress = parameters.to;
71
+ this.contractSecret = Buffer.from(parameters.contract.replace('0x', ''), 'hex');
72
+ this.calldata = Compressor.compress(parameters.calldata);
73
+ this.challenge = parameters.challenge;
74
+ this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
75
+
76
+ // Create the script signer (same as SharedInteractionTransaction does)
77
+ this.scriptSigner = this.generateKeyPairFromSeed();
78
+
79
+ // Create the P2WDA generator instead of CalldataGenerator
80
+ // P2WDA needs a different data format optimized for witness embedding
81
+ this.p2wdaGenerator = new P2WDAGenerator(
82
+ Buffer.from(this.signer.publicKey),
83
+ this.scriptSignerXOnlyPubKey(),
84
+ this.network,
85
+ );
86
+
87
+ // Validate contract secret
88
+ if (this.contractSecret.length !== 32) {
89
+ throw new Error('Invalid contract secret length. Expected 32 bytes.');
90
+ }
91
+
92
+ this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(
93
+ this.challenge.publicKey.originalPublicKeyBuffer(),
94
+ this.network,
95
+ );
96
+
97
+ // Validate P2WDA inputs
98
+ this.validateP2WDAInputs();
99
+
100
+ this.compiledOperationData = this.p2wdaGenerator.compile(
101
+ this.calldata,
102
+ this.contractSecret,
103
+ this.challenge,
104
+ this.priorityFee,
105
+ this.generateFeatures(parameters),
106
+ );
107
+
108
+ // Validate size early
109
+ this.validateOperationDataSize();
110
+
111
+ this.internalInit();
112
+ }
113
+
114
+ /**
115
+ * Get random bytes (for compatibility if needed elsewhere)
116
+ */
117
+ public getRndBytes(): Buffer {
118
+ return this.randomBytes;
119
+ }
120
+
121
+ /**
122
+ * Get the challenge (for compatibility if needed elsewhere)
123
+ */
124
+ public getChallenge(): ChallengeSolution {
125
+ return this.challenge;
126
+ }
127
+
128
+ /**
129
+ * Get contract secret (for compatibility if needed elsewhere)
130
+ */
131
+ public getContractSecret(): Buffer {
132
+ return this.contractSecret;
133
+ }
134
+
135
+ /**
136
+ * Build the transaction
137
+ */
138
+ protected async buildTransaction(): Promise<void> {
139
+ if (!this.regenerated) {
140
+ this.addInputsFromUTXO();
141
+ }
142
+
143
+ // Add refund
144
+ await this.createMineableRewardOutputs();
145
+ }
146
+
147
+ protected async createMineableRewardOutputs(): Promise<void> {
148
+ if (!this.to) throw new Error('To address is required');
149
+
150
+ const amountSpent: bigint = this.getTransactionOPNetFee();
151
+
152
+ let amountToCA: bigint;
153
+ if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
154
+ amountToCA = MINIMUM_AMOUNT_CA;
155
+ } else {
156
+ amountToCA = amountSpent;
157
+ }
158
+
159
+ // ALWAYS THE FIRST INPUT.
160
+ this.addOutput({
161
+ value: Number(amountToCA),
162
+ address: this.to,
163
+ });
164
+
165
+ // ALWAYS SECOND.
166
+ if (
167
+ amountToCA === MINIMUM_AMOUNT_CA &&
168
+ amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD
169
+ ) {
170
+ this.addOutput({
171
+ value: Number(amountSpent - amountToCA),
172
+ address: this.epochChallenge.address,
173
+ });
174
+ }
175
+
176
+ const amount = this.addOptionalOutputsAndGetAmount();
177
+ if (!this.disableAutoRefund) {
178
+ await this.addRefundOutput(amountSpent + amount);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Sign inputs with P2WDA-specific handling
184
+ */
185
+ protected override async signInputs(transaction: Psbt): Promise<void> {
186
+ // Sign all inputs
187
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
188
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.signer);
189
+ }
190
+
191
+ // Finalize with appropriate finalizers
192
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
193
+ if (this.p2wdaInputIndices.has(i)) {
194
+ if (i === 0) {
195
+ transaction.finalizeInput(i, this.finalizePrimaryP2WDA.bind(this));
196
+ } else {
197
+ transaction.finalizeInput(i, this.finalizeSecondaryP2WDA.bind(this));
198
+ }
199
+ } else {
200
+ transaction.finalizeInput(i, this.customFinalizerP2SH.bind(this));
201
+ }
202
+ }
203
+
204
+ this.finalized = true;
205
+ }
206
+
207
+ /**
208
+ * Generate features array (same as InteractionTransaction)
209
+ */
210
+ private generateFeatures(parameters: IInteractionParameters): Feature<Features>[] {
211
+ const features: Feature<Features>[] = [];
212
+
213
+ if (parameters.loadedStorage) {
214
+ features.push({
215
+ opcode: Features.ACCESS_LIST,
216
+ data: parameters.loadedStorage,
217
+ });
218
+ }
219
+
220
+ const submission = parameters.challenge.getSubmission();
221
+ if (submission) {
222
+ features.push({
223
+ opcode: Features.EPOCH_SUBMISSION,
224
+ data: submission,
225
+ });
226
+ }
227
+
228
+ return features;
229
+ }
230
+
231
+ /**
232
+ * Generate keypair from seed (same as SharedInteractionTransaction)
233
+ */
234
+ private generateKeyPairFromSeed(): ECPairInterface {
235
+ return EcKeyPair.fromSeedKeyPair(this.randomBytes, this.network);
236
+ }
237
+
238
+ /**
239
+ * Get script signer x-only pubkey (same as SharedInteractionTransaction)
240
+ */
241
+ private scriptSignerXOnlyPubKey(): Buffer {
242
+ return toXOnly(Buffer.from(this.scriptSigner.publicKey));
243
+ }
244
+
245
+ /**
246
+ * Validate that input 0 is P2WDA
247
+ */
248
+ private validateP2WDAInputs(): void {
249
+ if (this.utxos.length === 0 || !P2WDADetector.isP2WDAUTXO(this.utxos[0])) {
250
+ throw new Error('Input 0 must be a P2WDA UTXO');
251
+ }
252
+
253
+ // Track all P2WDA inputs
254
+ for (let i = 0; i < this.utxos.length; i++) {
255
+ if (P2WDADetector.isP2WDAUTXO(this.utxos[i])) {
256
+ this.p2wdaInputIndices.add(i);
257
+ }
258
+ }
259
+
260
+ for (let i = 0; i < this.optionalInputs.length; i++) {
261
+ const actualIndex = this.utxos.length + i;
262
+ if (P2WDADetector.isP2WDAUTXO(this.optionalInputs[i])) {
263
+ this.p2wdaInputIndices.add(actualIndex);
264
+ }
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Validate the compiled operation data will fit in witness fields
270
+ */
271
+ private validateOperationDataSize(): void {
272
+ if (!this.compiledOperationData) {
273
+ throw new Error('Operation data not compiled');
274
+ }
275
+
276
+ // The data that goes in witness: COMPRESS(signature + compiledOperationData)
277
+ // Signature is 64 bytes
278
+ const estimatedSize = this.compiledOperationData.length;
279
+
280
+ if (!P2WDAGenerator.validateWitnessSize(estimatedSize)) {
281
+ const signatureSize = 64;
282
+ const totalSize = estimatedSize + signatureSize;
283
+ const compressedEstimate = Math.ceil(totalSize * 0.7);
284
+ const requiredFields = Math.ceil(
285
+ compressedEstimate / InteractionTransactionP2WDA.MAX_BYTES_PER_WITNESS,
286
+ );
287
+
288
+ throw new Error(
289
+ `Please dont use P2WDA for this operation. Data too large. Raw size: ${estimatedSize} bytes, ` +
290
+ `estimated compressed: ${compressedEstimate} bytes, ` +
291
+ `needs ${requiredFields} witness fields, max is ${InteractionTransactionP2WDA.MAX_WITNESS_FIELDS}`,
292
+ );
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Finalize primary P2WDA input with the operation data
298
+ * This is where we create the signature and compress everything
299
+ */
300
+ private finalizePrimaryP2WDA(
301
+ inputIndex: number,
302
+ input: PsbtInput,
303
+ ): {
304
+ finalScriptSig: Buffer | undefined;
305
+ finalScriptWitness: Buffer | undefined;
306
+ } {
307
+ if (!input.partialSig || input.partialSig.length === 0) {
308
+ throw new Error(`No signature for P2WDA input #${inputIndex}`);
309
+ }
310
+
311
+ if (!input.witnessScript) {
312
+ throw new Error(`No witness script for P2WDA input #${inputIndex}`);
313
+ }
314
+
315
+ if (!this.compiledOperationData) {
316
+ throw new Error('Operation data not compiled');
317
+ }
318
+
319
+ const txSignature = input.partialSig[0].signature;
320
+ const messageToSign = Buffer.concat([txSignature, this.compiledOperationData]);
321
+ const signedMessage = MessageSigner.signMessage(
322
+ this.signer as ECPairInterface,
323
+ messageToSign,
324
+ );
325
+
326
+ const schnorrSignature = Buffer.from(signedMessage.signature);
327
+
328
+ // Combine and compress: COMPRESS(signature + compiledOperationData)
329
+ const fullData = Buffer.concat([schnorrSignature, this.compiledOperationData]);
330
+ const compressedData = Compressor.compress(fullData);
331
+
332
+ // Split into chunks
333
+ const chunks = this.splitIntoWitnessChunks(compressedData);
334
+
335
+ if (chunks.length > InteractionTransactionP2WDA.MAX_WITNESS_FIELDS) {
336
+ throw new Error(
337
+ `Compressed data needs ${chunks.length} witness fields, max is ${InteractionTransactionP2WDA.MAX_WITNESS_FIELDS}`,
338
+ );
339
+ }
340
+
341
+ // Build witness stack
342
+ const witnessStack: Buffer[] = [txSignature];
343
+
344
+ // Add exactly 10 data fields
345
+ // Bitcoin stack is reversed!
346
+ for (let i = 0; i < InteractionTransactionP2WDA.MAX_WITNESS_FIELDS; i++) {
347
+ witnessStack.push(i < chunks.length ? chunks[i] : Buffer.alloc(0));
348
+ }
349
+
350
+ witnessStack.push(input.witnessScript);
351
+
352
+ return {
353
+ finalScriptSig: undefined,
354
+ finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witnessStack),
355
+ };
356
+ }
357
+
358
+ /**
359
+ * Split data into 80-byte chunks
360
+ */
361
+ private splitIntoWitnessChunks(data: Buffer): Buffer[] {
362
+ const chunks: Buffer[] = [];
363
+ let offset = 0;
364
+
365
+ while (offset < data.length) {
366
+ const size = Math.min(
367
+ InteractionTransactionP2WDA.MAX_BYTES_PER_WITNESS,
368
+ data.length - offset,
369
+ );
370
+ chunks.push(data.subarray(offset, offset + size));
371
+ offset += size;
372
+ }
373
+
374
+ return chunks;
375
+ }
376
+ }
@@ -1,6 +1,6 @@
1
- import { P2TRPayment, PaymentType, Psbt, PsbtInput, Signer, Taptree, toXOnly } from '@btc-vision/bitcoin';
1
+ import { P2TRPayment, PaymentType, Psbt, PsbtInput, Signer, Taptree, toXOnly, } from '@btc-vision/bitcoin';
2
2
  import { ECPairInterface } from 'ecpair';
3
- import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder } from './TransactionBuilder.js';
3
+ import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
4
4
  import { TransactionType } from '../enums/TransactionType.js';
5
5
  import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
6
6
  import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
@@ -8,8 +8,9 @@ import { Compressor } from '../../bytecode/Compressor.js';
8
8
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
9
9
  import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
10
10
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
11
- import { ITimeLockOutput, TimeLockGenerator } from '../mineable/TimelockGenerator.js';
11
+ import { TimeLockGenerator } from '../mineable/TimelockGenerator.js';
12
12
  import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
13
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
13
14
 
14
15
  /**
15
16
  * Shared interaction transaction
@@ -33,7 +34,7 @@ export abstract class SharedInteractionTransaction<
33
34
  protected abstract readonly scriptTree: Taptree;
34
35
 
35
36
  protected readonly challenge: ChallengeSolution;
36
- protected readonly epochChallenge: ITimeLockOutput;
37
+ protected readonly epochChallenge: IP2WSHAddress;
37
38
 
38
39
  protected calldataGenerator: CalldataGenerator;
39
40
 
@@ -111,7 +112,7 @@ export abstract class SharedInteractionTransaction<
111
112
  /**
112
113
  * Get the preimage
113
114
  */
114
- public getPreimage(): ChallengeSolution {
115
+ public getChallenge(): ChallengeSolution {
115
116
  return this.challenge;
116
117
  }
117
118
 
@@ -340,7 +341,7 @@ export abstract class SharedInteractionTransaction<
340
341
  }
341
342
  }
342
343
 
343
- private async createMineableRewardOutputs(): Promise<void> {
344
+ protected async createMineableRewardOutputs(): Promise<void> {
344
345
  if (!this.to) throw new Error('To address is required');
345
346
 
346
347
  const amountSpent: bigint = this.getTransactionOPNetFee();
@@ -0,0 +1,4 @@
1
+ export interface IP2WSHAddress {
2
+ address: string;
3
+ witnessScript: Buffer;
4
+ }
@@ -1,9 +1,5 @@
1
1
  import bitcoin, { Network, networks, opcodes, script } from '@btc-vision/bitcoin';
2
-
3
- export interface ITimeLockOutput {
4
- address: string;
5
- witnessScript: Buffer;
6
- }
2
+ import { IP2WSHAddress } from './IP2WSHAddress.js';
7
3
 
8
4
  export class TimeLockGenerator {
9
5
  private static readonly CSV_BLOCKS = 75;
@@ -16,7 +12,7 @@ export class TimeLockGenerator {
16
12
  publicKey: Buffer,
17
13
  network: Network = networks.bitcoin,
18
14
  csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
19
- ): ITimeLockOutput {
15
+ ): IP2WSHAddress {
20
16
  const witnessScript = script.compile([
21
17
  script.number.encode(csvBlocks),
22
18
  opcodes.OP_CHECKSEQUENCEVERIFY,
@@ -39,6 +39,8 @@ import {
39
39
  pubkeyInScript,
40
40
  } from '../../signer/SignerUtils.js';
41
41
  import { TransactionBuilder } from '../builders/TransactionBuilder.js';
42
+ import { Buffer } from 'buffer';
43
+ import { P2WDADetector } from '../../p2wda/P2WDADetector.js';
42
44
 
43
45
  export type SupportedTransactionVersion = 1 | 2 | 3;
44
46
 
@@ -954,6 +956,11 @@ export abstract class TweakedTransaction extends Logger {
954
956
  throw new Error(`No signatures for P2WSH input #${inputIndex}`);
955
957
  }
956
958
 
959
+ const isP2WDA = P2WDADetector.isP2WDAWitnessScript(input.witnessScript);
960
+ if (isP2WDA) {
961
+ return this.finalizeSecondaryP2WDA(inputIndex, input);
962
+ }
963
+
957
964
  // Check if this is a CSV input
958
965
  const isCSVInput = this.csvInputIndices.has(inputIndex);
959
966
  if (isCSVInput) {
@@ -981,6 +988,35 @@ export abstract class TweakedTransaction extends Logger {
981
988
  );
982
989
  };
983
990
 
991
+ /**
992
+ * Finalize secondary P2WDA inputs with empty data
993
+ */
994
+ protected finalizeSecondaryP2WDA(
995
+ inputIndex: number,
996
+ input: PsbtInput,
997
+ ): {
998
+ finalScriptWitness: Buffer | undefined;
999
+ finalScriptSig: Buffer | undefined;
1000
+ } {
1001
+ if (!input.partialSig || input.partialSig.length === 0) {
1002
+ throw new Error(`No signature for P2WDA input #${inputIndex}`);
1003
+ }
1004
+
1005
+ if (!input.witnessScript) {
1006
+ throw new Error(`No witness script for P2WDA input #${inputIndex}`);
1007
+ }
1008
+
1009
+ const witnessStack = P2WDADetector.createSimpleP2WDAWitness(
1010
+ input.partialSig[0].signature,
1011
+ input.witnessScript,
1012
+ );
1013
+
1014
+ return {
1015
+ finalScriptSig: undefined,
1016
+ finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witnessStack),
1017
+ };
1018
+ }
1019
+
984
1020
  protected async signInputsWalletBased(transaction: Psbt): Promise<void> {
985
1021
  const signer: UnisatSigner = this.signer as UnisatSigner;
986
1022