@btc-vision/transaction 1.1.2 → 1.1.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.
Files changed (34) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
  4. package/browser/transaction/browser/types/Unisat.d.ts +8 -4
  5. package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -0
  6. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
  7. package/browser/transaction/builders/TransactionBuilder.d.ts +2 -1
  8. package/browser/transaction/shared/TweakedTransaction.d.ts +6 -3
  9. package/browser/utils/BitcoinUtils.d.ts +1 -1
  10. package/build/_version.d.ts +1 -1
  11. package/build/_version.js +1 -1
  12. package/build/buffer/BinaryWriter.js +0 -1
  13. package/build/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
  14. package/build/transaction/browser/extensions/UnisatSigner.js +103 -20
  15. package/build/transaction/browser/types/Unisat.d.ts +8 -4
  16. package/build/transaction/builders/DeploymentTransaction.d.ts +1 -0
  17. package/build/transaction/builders/DeploymentTransaction.js +17 -0
  18. package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
  19. package/build/transaction/builders/SharedInteractionTransaction.js +31 -10
  20. package/build/transaction/builders/TransactionBuilder.d.ts +2 -1
  21. package/build/transaction/shared/TweakedTransaction.d.ts +6 -3
  22. package/build/transaction/shared/TweakedTransaction.js +61 -43
  23. package/build/utils/BitcoinUtils.d.ts +1 -1
  24. package/build/utils/BitcoinUtils.js +6 -3
  25. package/package.json +1 -1
  26. package/src/_version.ts +1 -1
  27. package/src/buffer/BinaryWriter.ts +0 -2
  28. package/src/transaction/browser/extensions/UnisatSigner.ts +139 -28
  29. package/src/transaction/browser/types/Unisat.ts +12 -4
  30. package/src/transaction/builders/DeploymentTransaction.ts +25 -0
  31. package/src/transaction/builders/SharedInteractionTransaction.ts +51 -21
  32. package/src/transaction/builders/TransactionBuilder.ts +2 -1
  33. package/src/transaction/shared/TweakedTransaction.ts +90 -49
  34. package/src/utils/BitcoinUtils.ts +12 -3
@@ -154,47 +154,24 @@ export class TweakedTransaction extends Logger {
154
154
  getSignerKey() {
155
155
  return this.signer;
156
156
  }
157
- async signInput(transaction, input, i, signer) {
157
+ async signInput(transaction, input, i, signer, reverse = false) {
158
158
  const publicKey = signer.publicKey;
159
- const isTaproot = this.isTaprootInput(input);
159
+ let isTaproot = this.isTaprootInput(input);
160
+ if (reverse) {
161
+ isTaproot = !isTaproot;
162
+ }
160
163
  let signed = false;
161
164
  if (isTaproot) {
162
- const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
163
- if (isScriptSpend) {
164
- try {
165
- await this.signTaprootInput(signer, transaction, i);
166
- signed = true;
167
- }
168
- catch (e) {
169
- this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
170
- }
165
+ try {
166
+ await this.attemptSignTaproot(transaction, input, i, signer, publicKey);
167
+ signed = true;
171
168
  }
172
- else {
173
- let tweakedSigner;
174
- if (signer !== this.signer) {
175
- tweakedSigner = this.getTweakedSigner(true, signer);
176
- }
177
- else {
178
- if (!this.tweakedSigner)
179
- this.tweakSigner();
180
- tweakedSigner = this.tweakedSigner;
181
- }
182
- if (tweakedSigner) {
183
- try {
184
- await this.signTaprootInput(tweakedSigner, transaction, i);
185
- signed = true;
186
- }
187
- catch (e) {
188
- this.error(`Failed to sign Taproot key path input ${i}: ${e}`);
189
- }
190
- }
191
- else {
192
- this.error(`Failed to obtain tweaked signer for input ${i}.`);
193
- }
169
+ catch (e) {
170
+ this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
194
171
  }
195
172
  }
196
173
  else {
197
- if (this.canSignNonTaprootInput(input, publicKey)) {
174
+ if (!reverse ? this.canSignNonTaprootInput(input, publicKey) : true) {
198
175
  try {
199
176
  await this.signNonTaprootInput(signer, transaction, i);
200
177
  signed = true;
@@ -205,7 +182,12 @@ export class TweakedTransaction extends Logger {
205
182
  }
206
183
  }
207
184
  if (!signed) {
208
- throw new Error(`Cannot sign input ${i} with the provided signer.`);
185
+ try {
186
+ await this.signInput(transaction, input, i, signer, true);
187
+ }
188
+ catch {
189
+ throw new Error(`Cannot sign input ${i} with the provided signer.`);
190
+ }
209
191
  }
210
192
  }
211
193
  splitArray(arr, chunkSize) {
@@ -219,6 +201,10 @@ export class TweakedTransaction extends Logger {
219
201
  return result;
220
202
  }
221
203
  async signInputs(transaction) {
204
+ if ('multiSignPsbt' in this.signer) {
205
+ await this.signInputsWalletBased(transaction);
206
+ return;
207
+ }
222
208
  const txs = transaction.data.inputs;
223
209
  const batchSize = 20;
224
210
  const batches = this.splitArray(txs, batchSize);
@@ -238,14 +224,10 @@ export class TweakedTransaction extends Logger {
238
224
  }
239
225
  await Promise.all(promises);
240
226
  }
241
- transaction.finalizeInput(0, this.customFinalizerP2SH);
242
- try {
243
- transaction.finalizeAllInputs();
244
- this.finalized = true;
245
- }
246
- catch (e) {
247
- this.finalized = false;
227
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
228
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
248
229
  }
230
+ this.finalized = true;
249
231
  }
250
232
  internalPubKeyToXOnly() {
251
233
  return toXOnly(Buffer.from(this.signer.publicKey));
@@ -373,6 +355,37 @@ export class TweakedTransaction extends Logger {
373
355
  }
374
356
  return input;
375
357
  }
358
+ async signInputsWalletBased(transaction) {
359
+ const signer = this.signer;
360
+ await signer.multiSignPsbt([transaction]);
361
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
362
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
363
+ }
364
+ this.finalized = true;
365
+ }
366
+ async attemptSignTaproot(transaction, input, i, signer, publicKey) {
367
+ const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
368
+ if (isScriptSpend) {
369
+ await this.signTaprootInput(signer, transaction, i);
370
+ }
371
+ else {
372
+ let tweakedSigner;
373
+ if (signer !== this.signer) {
374
+ tweakedSigner = this.getTweakedSigner(true, signer);
375
+ }
376
+ else {
377
+ if (!this.tweakedSigner)
378
+ this.tweakSigner();
379
+ tweakedSigner = this.tweakedSigner;
380
+ }
381
+ if (tweakedSigner) {
382
+ await this.signTaprootInput(tweakedSigner, transaction, i);
383
+ }
384
+ else {
385
+ this.error(`Failed to obtain tweaked signer for input ${i}.`);
386
+ }
387
+ }
388
+ }
376
389
  isTaprootScriptSpend(input, publicKey) {
377
390
  if (input.tapLeafScript && input.tapLeafScript.length > 0) {
378
391
  for (const tapLeafScript of input.tapLeafScript) {
@@ -432,7 +445,12 @@ export class TweakedTransaction extends Logger {
432
445
  }
433
446
  async signTaprootInput(signer, transaction, i, tapLeafHash) {
434
447
  if ('signTaprootInput' in signer) {
435
- await signer.signTaprootInput(transaction, i, tapLeafHash);
448
+ try {
449
+ await signer.signTaprootInput(transaction, i, tapLeafHash);
450
+ }
451
+ catch {
452
+ throw new Error('Failed to sign Taproot input with provided signer.');
453
+ }
436
454
  }
437
455
  else {
438
456
  transaction.signTaprootInput(i, signer);
@@ -1,6 +1,6 @@
1
1
  export declare class BitcoinUtils {
2
2
  static btcToSatoshi(btc: number): bigint;
3
3
  static rndBytes(): Buffer;
4
- static getUnsafeRandomValues(length: number): Buffer;
4
+ static getSafeRandomValues(length: number): Buffer;
5
5
  static opnetHash(data: Buffer): string;
6
6
  }
@@ -4,11 +4,13 @@ export class BitcoinUtils {
4
4
  return BigInt(btc * 100000000);
5
5
  }
6
6
  static rndBytes() {
7
- const buf = BitcoinUtils.getUnsafeRandomValues(64);
7
+ const buf = BitcoinUtils.getSafeRandomValues(64);
8
8
  return Buffer.from(buf);
9
9
  }
10
- static getUnsafeRandomValues(length) {
11
- if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
10
+ static getSafeRandomValues(length) {
11
+ if (typeof globalThis.window !== 'undefined' &&
12
+ globalThis.window.crypto &&
13
+ typeof globalThis.window.crypto.getRandomValues === 'function') {
12
14
  const array = new Uint8Array(length);
13
15
  window.crypto.getRandomValues(array);
14
16
  return Buffer.from(array);
@@ -19,6 +21,7 @@ export class BitcoinUtils {
19
21
  return Buffer.from(array);
20
22
  }
21
23
  else {
24
+ console.log(`No secure random number generator available. Please upgrade your environment.`, globalThis.window.crypto, crypto);
22
25
  throw new Error('No secure random number generator available. Please upgrade your environment.');
23
26
  }
24
27
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.1.2",
4
+ "version": "1.1.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": {
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.1.2';
1
+ export const version = '1.1.4';
@@ -63,8 +63,6 @@ export class BinaryWriter {
63
63
 
64
64
  const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue);
65
65
  if (bytesToHex.byteLength !== 32) {
66
- console.log('Invalid u256 value:', bytesToHex);
67
-
68
66
  throw new Error(`Invalid u256 value: ${bigIntValue}`);
69
67
  }
70
68
 
@@ -1,9 +1,19 @@
1
- import { Network, networks, Psbt, TapScriptSig } from '@btc-vision/bitcoin';
1
+ import {
2
+ crypto as bitCrypto,
3
+ Network,
4
+ networks,
5
+ opcodes,
6
+ Psbt,
7
+ PsbtInput,
8
+ script as bitScript,
9
+ TapScriptSig,
10
+ } from '@btc-vision/bitcoin';
2
11
  import { ECPairInterface } from 'ecpair';
3
12
  import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
4
13
  import { CustomKeypair } from '../BrowserSignerBase.js';
5
14
  import { PsbtSignatureOptions, Unisat, UnisatNetwork } from '../types/Unisat.js';
6
15
  import { PartialSig } from 'bip174/src/lib/interfaces.js';
16
+ import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
7
17
 
8
18
  declare global {
9
19
  interface Window {
@@ -122,15 +132,15 @@ export class UnisatSigner extends CustomKeypair {
122
132
  return this.publicKey;
123
133
  }
124
134
 
125
- public sign(hash: Buffer, lowR?: boolean): Buffer {
135
+ public sign(_hash: Buffer, _lowR?: boolean): Buffer {
126
136
  throw new Error('Not implemented: sign');
127
137
  }
128
138
 
129
- public signSchnorr(hash: Buffer): Buffer {
139
+ public signSchnorr(_hash: Buffer): Buffer {
130
140
  throw new Error('Not implemented: signSchnorr');
131
141
  }
132
142
 
133
- public verify(hash: Buffer, signature: Buffer): boolean {
143
+ public verify(_hash: Buffer, _signature: Buffer): boolean {
134
144
  throw new Error('Not implemented: verify');
135
145
  }
136
146
 
@@ -176,6 +186,80 @@ export class UnisatSigner extends CustomKeypair {
176
186
  this.combine(transaction, firstSignature, i);
177
187
  }
178
188
 
189
+ public async multiSignPsbt(transactions: Psbt[]): Promise<void> {
190
+ const toSignPsbts: string[] = [];
191
+ const options: PsbtSignatureOptions[] = [];
192
+
193
+ for (const psbt of transactions) {
194
+ const hex = psbt.toHex();
195
+ toSignPsbts.push(hex);
196
+
197
+ const toSignInputs = psbt.data.inputs
198
+ .map((input, i) => {
199
+ let needsToSign = false;
200
+ let viaTaproot = false;
201
+
202
+ if (isTaprootInput(input)) {
203
+ if (input.tapLeafScript && input.tapLeafScript.length > 0) {
204
+ for (const tapLeafScript of input.tapLeafScript) {
205
+ if (pubkeyInScript(this.publicKey, tapLeafScript.script)) {
206
+ needsToSign = true;
207
+ viaTaproot = false; // for opnet, we use original keys.
208
+ break;
209
+ }
210
+ }
211
+ }
212
+
213
+ if (!needsToSign && input.tapInternalKey) {
214
+ const tapInternalKey = input.tapInternalKey;
215
+ const xOnlyPubKey = toXOnly(this.publicKey);
216
+
217
+ if (tapInternalKey.equals(xOnlyPubKey)) {
218
+ needsToSign = true;
219
+ viaTaproot = true;
220
+ }
221
+ }
222
+ } else {
223
+ // Non-Taproot input
224
+ const script = getInputRelevantScript(input);
225
+
226
+ if (script && pubkeyInScript(this.publicKey, script)) {
227
+ needsToSign = true;
228
+ viaTaproot = false;
229
+ }
230
+ }
231
+
232
+ if (needsToSign) {
233
+ return {
234
+ index: i,
235
+ publicKey: this.publicKey.toString('hex'),
236
+ disableTweakSigner: !viaTaproot,
237
+ };
238
+ } else {
239
+ return null;
240
+ }
241
+ })
242
+ .filter((v) => v !== null);
243
+
244
+ options.push({
245
+ autoFinalized: false,
246
+ toSignInputs: toSignInputs,
247
+ });
248
+ }
249
+
250
+ const signed = await this.unisat.signPsbt(toSignPsbts[0], options[0]);
251
+ const signedPsbts = Psbt.fromHex(signed); //signed.map((hex) => Psbt.fromHex(hex));
252
+
253
+ /*for (let i = 0; i < signedPsbts.length; i++) {
254
+ const psbtOriginal = transactions[i];
255
+ const psbtSigned = signedPsbts[i];
256
+
257
+ psbtOriginal.combine(psbtSigned);
258
+ }*/
259
+
260
+ transactions[0].combine(signedPsbts);
261
+ }
262
+
179
263
  private hasAlreadySignedTapScriptSig(input: TapScriptSig[]): boolean {
180
264
  for (let i = 0; i < input.length; i++) {
181
265
  const item = input[i];
@@ -257,30 +341,6 @@ export class UnisatSigner extends CustomKeypair {
257
341
  return Psbt.fromHex(signed);
258
342
  }
259
343
 
260
- private async signTweaked(
261
- transaction: Psbt,
262
- i: number,
263
- sighashTypes: number[],
264
- disableTweakSigner: boolean = false,
265
- ): Promise<Psbt> {
266
- const opts: PsbtSignatureOptions = {
267
- autoFinalized: false,
268
- toSignInputs: [
269
- {
270
- index: i,
271
- publicKey: this.publicKey.toString('hex'),
272
- sighashTypes,
273
- disableTweakSigner: disableTweakSigner,
274
- },
275
- ],
276
- };
277
-
278
- const psbt = transaction.toHex();
279
- const signed = await this.unisat.signPsbt(psbt, opts);
280
-
281
- return Psbt.fromHex(signed);
282
- }
283
-
284
344
  private getNonDuplicateScriptSig(
285
345
  scriptSig1: TapScriptSig[],
286
346
  scriptSig2: TapScriptSig[],
@@ -296,3 +356,54 @@ export class UnisatSigner extends CustomKeypair {
296
356
  return nonDuplicate;
297
357
  }
298
358
  }
359
+
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
+ function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
392
+ return pubkeyPositionInScript(pubkey, script) !== -1;
393
+ }
394
+
395
+ function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
396
+ const pubkeyHash = bitCrypto.hash160(pubkey);
397
+ const pubkeyXOnly = toXOnly(pubkey);
398
+
399
+ const decompiled = bitScript.decompile(script);
400
+ if (decompiled === null) throw new Error('Unknown script error');
401
+
402
+ return decompiled.findIndex((element) => {
403
+ if (typeof element === 'number') return false;
404
+ return (
405
+ Buffer.isBuffer(element) &&
406
+ (element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly))
407
+ );
408
+ });
409
+ }
@@ -67,7 +67,7 @@ export interface Unisat {
67
67
 
68
68
  getNetwork(): Promise<UnisatNetwork>;
69
69
 
70
- getChain(): Promise<UnisatChainType>;
70
+ getChain(): Promise<UnisatChainInfo>;
71
71
 
72
72
  getAccounts(): Promise<string[]>;
73
73
 
@@ -83,15 +83,23 @@ export interface Unisat {
83
83
 
84
84
  signPsbt(psbtHex: string, psbtOptions: PsbtSignatureOptions): Promise<string>;
85
85
 
86
- signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions): Promise<string[]>;
86
+ signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions[]): Promise<string[]>;
87
87
 
88
88
  pushPsbt(psbtHex: string): Promise<string>;
89
89
 
90
90
  on(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
91
91
 
92
- on(event: 'chainChanged' | 'networkChanged', listener: (network: UnisatNetwork) => void): void;
92
+ on(event: 'chainChanged', listener: (chain: UnisatChainInfo) => void): void;
93
+
94
+ on(event: 'networkChanged', listener: (network: UnisatChainInfo) => void): void;
95
+
96
+ on(event: 'disconnect', listener: () => void): void;
93
97
 
94
98
  removeListener(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
95
99
 
96
- removeListener(event: 'networkChanged', listener: (network: UnisatNetwork) => void): void;
100
+ removeListener(event: 'chainChanged', listener: (chain: UnisatChainInfo) => void): void;
101
+
102
+ removeListener(event: 'networkChanged', listener: (network: UnisatChainInfo) => void): void;
103
+
104
+ removeListener(event: 'disconnect', listener: () => void): void;
97
105
  }
@@ -12,6 +12,7 @@ import { Compressor } from '../../bytecode/Compressor.js';
12
12
  import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
13
13
  import { ECPairInterface } from 'ecpair';
14
14
  import { Address } from '../../keypair/Address.js';
15
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
15
16
 
16
17
  export class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
17
18
  public static readonly MAXIMUM_CONTRACT_SIZE = 128 * 1024;
@@ -203,6 +204,25 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
203
204
  await this.addRefundOutput(amountSpent);
204
205
  }
205
206
 
207
+ protected override async signInputsWalletBased(transaction: Psbt): Promise<void> {
208
+ const signer: UnisatSigner = this.signer as UnisatSigner;
209
+
210
+ // first, we sign the first input with the script signer.
211
+ await this.signInput(transaction, transaction.data.inputs[0], 0, this.contractSigner);
212
+
213
+ // then, we sign all the remaining inputs with the wallet signer.
214
+ await signer.multiSignPsbt([transaction]);
215
+
216
+ // Then, we finalize every input.
217
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
218
+ if (i === 0) {
219
+ transaction.finalizeInput(i, this.customFinalizer);
220
+ } else {
221
+ transaction.finalizeInput(i);
222
+ }
223
+ }
224
+ }
225
+
206
226
  /**
207
227
  * Sign the inputs
208
228
  * @param {Psbt} transaction The transaction to sign
@@ -215,6 +235,11 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
215
235
  return;
216
236
  }
217
237
 
238
+ if ('multiSignPsbt' in this.signer) {
239
+ await this.signInputsWalletBased(transaction);
240
+ return;
241
+ }
242
+
218
243
  for (let i = 0; i < transaction.data.inputs.length; i++) {
219
244
  if (i === 0) {
220
245
  // multi sig input
@@ -9,6 +9,7 @@ import { Compressor } from '../../bytecode/Compressor.js';
9
9
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
10
10
  import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
11
11
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
12
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
12
13
 
13
14
  /**
14
15
  * Shared interaction transaction
@@ -186,27 +187,10 @@ export abstract class SharedInteractionTransaction<
186
187
  return;
187
188
  }
188
189
 
189
- for (let i = 0; i < transaction.data.inputs.length; i++) {
190
- if (i === 0) {
191
- await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
192
- await this.signInput(
193
- transaction,
194
- transaction.data.inputs[i],
195
- i,
196
- this.getSignerKey(),
197
- );
198
-
199
- transaction.finalizeInput(i, this.customFinalizer);
200
- } else {
201
- await this.signInput(
202
- transaction,
203
- transaction.data.inputs[i],
204
- i,
205
- this.getSignerKey(),
206
- );
207
-
208
- transaction.finalizeInput(i);
209
- }
190
+ if ('multiSignPsbt' in this.signer) {
191
+ await this.signInputsWalletBased(transaction);
192
+ } else {
193
+ await this.signInputsNonWalletBased(transaction);
210
194
  }
211
195
  }
212
196
 
@@ -308,6 +292,52 @@ export abstract class SharedInteractionTransaction<
308
292
  };
309
293
  };
310
294
 
295
+ // custom for interactions
296
+ protected override async signInputsWalletBased(transaction: Psbt): Promise<void> {
297
+ const signer: UnisatSigner = this.signer as UnisatSigner;
298
+
299
+ // first, we sign the first input with the script signer.
300
+ await this.signInput(transaction, transaction.data.inputs[0], 0, this.scriptSigner);
301
+
302
+ // then, we sign all the remaining inputs with the wallet signer.
303
+ await signer.multiSignPsbt([transaction]);
304
+
305
+ // Then, we finalize every input.
306
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
307
+ if (i === 0) {
308
+ transaction.finalizeInput(i, this.customFinalizer);
309
+ } else {
310
+ transaction.finalizeInput(i);
311
+ }
312
+ }
313
+ }
314
+
315
+ private async signInputsNonWalletBased(transaction: Psbt): Promise<void> {
316
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
317
+ if (i === 0) {
318
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
319
+
320
+ await this.signInput(
321
+ transaction,
322
+ transaction.data.inputs[i],
323
+ i,
324
+ this.getSignerKey(),
325
+ );
326
+
327
+ transaction.finalizeInput(i, this.customFinalizer);
328
+ } else {
329
+ await this.signInput(
330
+ transaction,
331
+ transaction.data.inputs[i],
332
+ i,
333
+ this.getSignerKey(),
334
+ );
335
+
336
+ transaction.finalizeInput(i);
337
+ }
338
+ }
339
+ }
340
+
311
341
  /**
312
342
  * Get the public keys
313
343
  * @private
@@ -22,6 +22,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
22
22
  import { ECPairInterface } from 'ecpair';
23
23
  import { AddressVerificator } from '../../keypair/AddressVerificator.js';
24
24
  import { TweakedTransaction } from '../shared/TweakedTransaction.js';
25
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
25
26
 
26
27
  initEccLib(ecc);
27
28
 
@@ -91,7 +92,7 @@ export abstract class TransactionBuilder<T extends TransactionType> extends Twea
91
92
  /**
92
93
  * @description The signer of the transaction
93
94
  */
94
- protected readonly signer: Signer | ECPairInterface;
95
+ protected readonly signer: Signer | ECPairInterface | UnisatSigner;
95
96
 
96
97
  /**
97
98
  * @description The network where the transaction will be broadcasted