@btc-vision/transaction 1.1.2 → 1.1.3

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 (30) 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 +1 -1
  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/build/_version.d.ts +1 -1
  10. package/build/_version.js +1 -1
  11. package/build/buffer/BinaryWriter.js +0 -1
  12. package/build/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
  13. package/build/transaction/browser/extensions/UnisatSigner.js +103 -20
  14. package/build/transaction/browser/types/Unisat.d.ts +1 -1
  15. package/build/transaction/builders/DeploymentTransaction.d.ts +1 -0
  16. package/build/transaction/builders/DeploymentTransaction.js +17 -0
  17. package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
  18. package/build/transaction/builders/SharedInteractionTransaction.js +31 -10
  19. package/build/transaction/builders/TransactionBuilder.d.ts +2 -1
  20. package/build/transaction/shared/TweakedTransaction.d.ts +6 -3
  21. package/build/transaction/shared/TweakedTransaction.js +61 -43
  22. package/package.json +1 -1
  23. package/src/_version.ts +1 -1
  24. package/src/buffer/BinaryWriter.ts +0 -2
  25. package/src/transaction/browser/extensions/UnisatSigner.ts +139 -28
  26. package/src/transaction/browser/types/Unisat.ts +1 -1
  27. package/src/transaction/builders/DeploymentTransaction.ts +25 -0
  28. package/src/transaction/builders/SharedInteractionTransaction.ts +51 -21
  29. package/src/transaction/builders/TransactionBuilder.ts +2 -1
  30. package/src/transaction/shared/TweakedTransaction.ts +90 -49
@@ -23,15 +23,15 @@ export declare class UnisatSigner extends CustomKeypair {
23
23
  get unisat(): Unisat;
24
24
  init(): Promise<void>;
25
25
  getPublicKey(): Buffer;
26
- sign(hash: Buffer, lowR?: boolean): Buffer;
27
- signSchnorr(hash: Buffer): Buffer;
28
- verify(hash: Buffer, signature: Buffer): boolean;
26
+ sign(_hash: Buffer, _lowR?: boolean): Buffer;
27
+ signSchnorr(_hash: Buffer): Buffer;
28
+ verify(_hash: Buffer, _signature: Buffer): boolean;
29
29
  signTaprootInput(transaction: Psbt, i: number, sighashTypes: number[]): Promise<void>;
30
30
  signInput(transaction: Psbt, i: number, sighashTypes: number[]): Promise<void>;
31
+ multiSignPsbt(transactions: Psbt[]): Promise<void>;
31
32
  private hasAlreadySignedTapScriptSig;
32
33
  private hasAlreadyPartialSig;
33
34
  private combine;
34
35
  private signAllTweaked;
35
- private signTweaked;
36
36
  private getNonDuplicateScriptSig;
37
37
  }
@@ -62,7 +62,7 @@ export interface Unisat {
62
62
  rawtx: string;
63
63
  }): Promise<string>;
64
64
  signPsbt(psbtHex: string, psbtOptions: PsbtSignatureOptions): Promise<string>;
65
- signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions): Promise<string[]>;
65
+ signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions[]): Promise<string[]>;
66
66
  pushPsbt(psbtHex: string): Promise<string>;
67
67
  on(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
68
68
  on(event: 'chainChanged' | 'networkChanged', listener: (network: UnisatNetwork) => void): void;
@@ -27,6 +27,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
27
27
  getRndBytes(): Buffer;
28
28
  protected contractSignerXOnlyPubKey(): Buffer;
29
29
  protected buildTransaction(): Promise<void>;
30
+ protected signInputsWalletBased(transaction: Psbt): Promise<void>;
30
31
  protected signInputs(transaction: Psbt): Promise<void>;
31
32
  protected generateScriptAddress(): Payment;
32
33
  protected generateTapData(): Payment;
@@ -32,6 +32,8 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
32
32
  protected customFinalizer: (_inputIndex: number, input: PsbtInput) => {
33
33
  finalScriptWitness: Buffer;
34
34
  };
35
+ protected signInputsWalletBased(transaction: Psbt): Promise<void>;
36
+ private signInputsNonWalletBased;
35
37
  private getPubKeys;
36
38
  private generateRedeemScripts;
37
39
  }
@@ -5,6 +5,7 @@ import { IFundingTransactionParameters, ITransactionParameters } from '../interf
5
5
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
6
6
  import { ECPairInterface } from 'ecpair';
7
7
  import { TweakedTransaction } from '../shared/TweakedTransaction.js';
8
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
8
9
  export declare abstract class TransactionBuilder<T extends TransactionType> extends TweakedTransaction {
9
10
  static readonly LOCK_LEAF_SCRIPT: Buffer;
10
11
  static readonly MINIMUM_DUST: bigint;
@@ -19,7 +20,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
19
20
  protected readonly outputs: PsbtOutputExtended[];
20
21
  protected feeOutput: PsbtOutputExtended | null;
21
22
  protected totalInputAmount: bigint;
22
- protected readonly signer: Signer | ECPairInterface;
23
+ protected readonly signer: Signer | ECPairInterface | UnisatSigner;
23
24
  protected readonly network: Network;
24
25
  protected readonly feeRate: number;
25
26
  protected priorityFee: bigint;
@@ -4,8 +4,9 @@ import { ECPairInterface } from 'ecpair';
4
4
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
5
5
  import { TapLeafScript } from '../interfaces/Tap.js';
6
6
  import { ChainId } from '../../network/ChainId.js';
7
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
7
8
  export interface ITweakedTransactionData {
8
- readonly signer: Signer | ECPairInterface;
9
+ readonly signer: Signer | ECPairInterface | UnisatSigner;
9
10
  readonly network: Network;
10
11
  readonly chainId?: ChainId;
11
12
  readonly nonWitnessUtxo?: Buffer;
@@ -17,7 +18,7 @@ export declare enum TransactionSequence {
17
18
  export declare abstract class TweakedTransaction extends Logger {
18
19
  readonly logColor: string;
19
20
  finalized: boolean;
20
- protected signer: Signer | ECPairInterface;
21
+ protected signer: Signer | ECPairInterface | UnisatSigner;
21
22
  protected tweakedSigner?: ECPairInterface;
22
23
  protected network: Network;
23
24
  protected signed: boolean;
@@ -47,7 +48,7 @@ export declare abstract class TweakedTransaction extends Logger {
47
48
  protected generateTapData(): Payment;
48
49
  protected generateScriptAddress(): Payment;
49
50
  protected getSignerKey(): Signer | ECPairInterface;
50
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface): Promise<void>;
51
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean): Promise<void>;
51
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
52
53
  protected signInputs(transaction: Psbt): Promise<void>;
53
54
  protected internalPubKeyToXOnly(): Buffer;
@@ -64,6 +65,8 @@ export declare abstract class TweakedTransaction extends Logger {
64
65
  finalScriptSig: Buffer | undefined;
65
66
  finalScriptWitness: Buffer | undefined;
66
67
  };
68
+ protected signInputsWalletBased(transaction: Psbt): Promise<void>;
69
+ private attemptSignTaproot;
67
70
  private isTaprootScriptSpend;
68
71
  private isTaprootInput;
69
72
  private canSignNonTaprootInput;
@@ -1 +1 @@
1
- export declare const version = "1.1.2";
1
+ export declare const version = "1.1.3";
package/build/_version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.1.2';
1
+ export const version = '1.1.3';
@@ -47,7 +47,6 @@ export class BinaryWriter {
47
47
  this.allocSafe(32);
48
48
  const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue);
49
49
  if (bytesToHex.byteLength !== 32) {
50
- console.log('Invalid u256 value:', bytesToHex);
51
50
  throw new Error(`Invalid u256 value: ${bigIntValue}`);
52
51
  }
53
52
  for (let i = 0; i < bytesToHex.byteLength; i++) {
@@ -23,15 +23,15 @@ export declare class UnisatSigner extends CustomKeypair {
23
23
  get unisat(): Unisat;
24
24
  init(): Promise<void>;
25
25
  getPublicKey(): Buffer;
26
- sign(hash: Buffer, lowR?: boolean): Buffer;
27
- signSchnorr(hash: Buffer): Buffer;
28
- verify(hash: Buffer, signature: Buffer): boolean;
26
+ sign(_hash: Buffer, _lowR?: boolean): Buffer;
27
+ signSchnorr(_hash: Buffer): Buffer;
28
+ verify(_hash: Buffer, _signature: Buffer): boolean;
29
29
  signTaprootInput(transaction: Psbt, i: number, sighashTypes: number[]): Promise<void>;
30
30
  signInput(transaction: Psbt, i: number, sighashTypes: number[]): Promise<void>;
31
+ multiSignPsbt(transactions: Psbt[]): Promise<void>;
31
32
  private hasAlreadySignedTapScriptSig;
32
33
  private hasAlreadyPartialSig;
33
34
  private combine;
34
35
  private signAllTweaked;
35
- private signTweaked;
36
36
  private getNonDuplicateScriptSig;
37
37
  }
@@ -1,7 +1,8 @@
1
- import { networks, Psbt } from '@btc-vision/bitcoin';
1
+ import { crypto as bitCrypto, networks, opcodes, Psbt, script as bitScript, } from '@btc-vision/bitcoin';
2
2
  import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
3
3
  import { CustomKeypair } from '../BrowserSignerBase.js';
4
4
  import { UnisatNetwork } from '../types/Unisat.js';
5
+ import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
5
6
  export class UnisatSigner extends CustomKeypair {
6
7
  constructor() {
7
8
  super();
@@ -78,13 +79,13 @@ export class UnisatSigner extends CustomKeypair {
78
79
  }
79
80
  return this.publicKey;
80
81
  }
81
- sign(hash, lowR) {
82
+ sign(_hash, _lowR) {
82
83
  throw new Error('Not implemented: sign');
83
84
  }
84
- signSchnorr(hash) {
85
+ signSchnorr(_hash) {
85
86
  throw new Error('Not implemented: signSchnorr');
86
87
  }
87
- verify(hash, signature) {
88
+ verify(_hash, _signature) {
88
89
  throw new Error('Not implemented: verify');
89
90
  }
90
91
  async signTaprootInput(transaction, i, sighashTypes) {
@@ -117,6 +118,63 @@ export class UnisatSigner extends CustomKeypair {
117
118
  const firstSignature = await this.signAllTweaked(transaction, sighashTypes, true);
118
119
  this.combine(transaction, firstSignature, i);
119
120
  }
121
+ async multiSignPsbt(transactions) {
122
+ const toSignPsbts = [];
123
+ const options = [];
124
+ for (const psbt of transactions) {
125
+ const hex = psbt.toHex();
126
+ toSignPsbts.push(hex);
127
+ const toSignInputs = psbt.data.inputs
128
+ .map((input, i) => {
129
+ let needsToSign = false;
130
+ let viaTaproot = false;
131
+ if (isTaprootInput(input)) {
132
+ if (input.tapLeafScript && input.tapLeafScript.length > 0) {
133
+ for (const tapLeafScript of input.tapLeafScript) {
134
+ if (pubkeyInScript(this.publicKey, tapLeafScript.script)) {
135
+ needsToSign = true;
136
+ viaTaproot = false;
137
+ break;
138
+ }
139
+ }
140
+ }
141
+ if (!needsToSign && input.tapInternalKey) {
142
+ const tapInternalKey = input.tapInternalKey;
143
+ const xOnlyPubKey = toXOnly(this.publicKey);
144
+ if (tapInternalKey.equals(xOnlyPubKey)) {
145
+ needsToSign = true;
146
+ viaTaproot = true;
147
+ }
148
+ }
149
+ }
150
+ else {
151
+ const script = getInputRelevantScript(input);
152
+ if (script && pubkeyInScript(this.publicKey, script)) {
153
+ needsToSign = true;
154
+ viaTaproot = false;
155
+ }
156
+ }
157
+ if (needsToSign) {
158
+ return {
159
+ index: i,
160
+ publicKey: this.publicKey.toString('hex'),
161
+ disableTweakSigner: !viaTaproot,
162
+ };
163
+ }
164
+ else {
165
+ return null;
166
+ }
167
+ })
168
+ .filter((v) => v !== null);
169
+ options.push({
170
+ autoFinalized: false,
171
+ toSignInputs: toSignInputs,
172
+ });
173
+ }
174
+ const signed = await this.unisat.signPsbt(toSignPsbts[0], options[0]);
175
+ const signedPsbts = Psbt.fromHex(signed);
176
+ transactions[0].combine(signedPsbts);
177
+ }
120
178
  hasAlreadySignedTapScriptSig(input) {
121
179
  for (let i = 0; i < input.length; i++) {
122
180
  const item = input[i];
@@ -179,22 +237,6 @@ export class UnisatSigner extends CustomKeypair {
179
237
  const signed = await this.unisat.signPsbt(psbt, opts);
180
238
  return Psbt.fromHex(signed);
181
239
  }
182
- async signTweaked(transaction, i, sighashTypes, disableTweakSigner = false) {
183
- const opts = {
184
- autoFinalized: false,
185
- toSignInputs: [
186
- {
187
- index: i,
188
- publicKey: this.publicKey.toString('hex'),
189
- sighashTypes,
190
- disableTweakSigner: disableTweakSigner,
191
- },
192
- ],
193
- };
194
- const psbt = transaction.toHex();
195
- const signed = await this.unisat.signPsbt(psbt, opts);
196
- return Psbt.fromHex(signed);
197
- }
198
240
  getNonDuplicateScriptSig(scriptSig1, scriptSig2) {
199
241
  const nonDuplicate = [];
200
242
  for (let i = 0; i < scriptSig2.length; i++) {
@@ -206,3 +248,44 @@ export class UnisatSigner extends CustomKeypair {
206
248
  return nonDuplicate;
207
249
  }
208
250
  }
251
+ function isTaprootInput(input) {
252
+ if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
253
+ return true;
254
+ }
255
+ if (input.witnessUtxo) {
256
+ const script = input.witnessUtxo.script;
257
+ return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
258
+ }
259
+ return false;
260
+ }
261
+ function getInputRelevantScript(input) {
262
+ if (input.redeemScript) {
263
+ return input.redeemScript;
264
+ }
265
+ if (input.witnessScript) {
266
+ return input.witnessScript;
267
+ }
268
+ if (input.witnessUtxo) {
269
+ return input.witnessUtxo.script;
270
+ }
271
+ if (input.nonWitnessUtxo) {
272
+ return null;
273
+ }
274
+ return null;
275
+ }
276
+ function pubkeyInScript(pubkey, script) {
277
+ return pubkeyPositionInScript(pubkey, script) !== -1;
278
+ }
279
+ function pubkeyPositionInScript(pubkey, script) {
280
+ const pubkeyHash = bitCrypto.hash160(pubkey);
281
+ const pubkeyXOnly = toXOnly(pubkey);
282
+ const decompiled = bitScript.decompile(script);
283
+ if (decompiled === null)
284
+ throw new Error('Unknown script error');
285
+ return decompiled.findIndex((element) => {
286
+ if (typeof element === 'number')
287
+ return false;
288
+ return (Buffer.isBuffer(element) &&
289
+ (element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly)));
290
+ });
291
+ }
@@ -62,7 +62,7 @@ export interface Unisat {
62
62
  rawtx: string;
63
63
  }): Promise<string>;
64
64
  signPsbt(psbtHex: string, psbtOptions: PsbtSignatureOptions): Promise<string>;
65
- signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions): Promise<string[]>;
65
+ signPsbts(psbtHex: string[], psbtOptions: PsbtSignatureOptions[]): Promise<string[]>;
66
66
  pushPsbt(psbtHex: string): Promise<string>;
67
67
  on(event: 'accountsChanged', listener: (accounts: string[]) => void): void;
68
68
  on(event: 'chainChanged' | 'networkChanged', listener: (network: UnisatNetwork) => void): void;
@@ -27,6 +27,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
27
27
  getRndBytes(): Buffer;
28
28
  protected contractSignerXOnlyPubKey(): Buffer;
29
29
  protected buildTransaction(): Promise<void>;
30
+ protected signInputsWalletBased(transaction: Psbt): Promise<void>;
30
31
  protected signInputs(transaction: Psbt): Promise<void>;
31
32
  protected generateScriptAddress(): Payment;
32
33
  protected generateTapData(): Payment;
@@ -95,11 +95,28 @@ export class DeploymentTransaction extends TransactionBuilder {
95
95
  });
96
96
  await this.addRefundOutput(amountSpent);
97
97
  }
98
+ async signInputsWalletBased(transaction) {
99
+ const signer = this.signer;
100
+ await this.signInput(transaction, transaction.data.inputs[0], 0, this.contractSigner);
101
+ await signer.multiSignPsbt([transaction]);
102
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
103
+ if (i === 0) {
104
+ transaction.finalizeInput(i, this.customFinalizer);
105
+ }
106
+ else {
107
+ transaction.finalizeInput(i);
108
+ }
109
+ }
110
+ }
98
111
  async signInputs(transaction) {
99
112
  if (!this.contractSigner) {
100
113
  await super.signInputs(transaction);
101
114
  return;
102
115
  }
116
+ if ('multiSignPsbt' in this.signer) {
117
+ await this.signInputsWalletBased(transaction);
118
+ return;
119
+ }
103
120
  for (let i = 0; i < transaction.data.inputs.length; i++) {
104
121
  if (i === 0) {
105
122
  transaction.signInput(0, this.contractSigner);
@@ -32,6 +32,8 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
32
32
  protected customFinalizer: (_inputIndex: number, input: PsbtInput) => {
33
33
  finalScriptWitness: Buffer;
34
34
  };
35
+ protected signInputsWalletBased(transaction: Psbt): Promise<void>;
36
+ private signInputsNonWalletBased;
35
37
  private getPubKeys;
36
38
  private generateRedeemScripts;
37
39
  }
@@ -88,16 +88,11 @@ export class SharedInteractionTransaction extends TransactionBuilder {
88
88
  await super.signInputs(transaction);
89
89
  return;
90
90
  }
91
- for (let i = 0; i < transaction.data.inputs.length; i++) {
92
- if (i === 0) {
93
- await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
94
- await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
95
- transaction.finalizeInput(i, this.customFinalizer);
96
- }
97
- else {
98
- await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
99
- transaction.finalizeInput(i);
100
- }
91
+ if ('multiSignPsbt' in this.signer) {
92
+ await this.signInputsWalletBased(transaction);
93
+ }
94
+ else {
95
+ await this.signInputsNonWalletBased(transaction);
101
96
  }
102
97
  }
103
98
  generateScriptAddress() {
@@ -151,6 +146,32 @@ export class SharedInteractionTransaction extends TransactionBuilder {
151
146
  },
152
147
  ];
153
148
  }
149
+ async signInputsWalletBased(transaction) {
150
+ const signer = this.signer;
151
+ await this.signInput(transaction, transaction.data.inputs[0], 0, this.scriptSigner);
152
+ await signer.multiSignPsbt([transaction]);
153
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
154
+ if (i === 0) {
155
+ transaction.finalizeInput(i, this.customFinalizer);
156
+ }
157
+ else {
158
+ transaction.finalizeInput(i);
159
+ }
160
+ }
161
+ }
162
+ async signInputsNonWalletBased(transaction) {
163
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
164
+ if (i === 0) {
165
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.scriptSigner);
166
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
167
+ transaction.finalizeInput(i, this.customFinalizer);
168
+ }
169
+ else {
170
+ await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
171
+ transaction.finalizeInput(i);
172
+ }
173
+ }
174
+ }
154
175
  getPubKeys() {
155
176
  const pubkeys = [Buffer.from(this.signer.publicKey)];
156
177
  if (this.scriptSigner) {
@@ -5,6 +5,7 @@ import { IFundingTransactionParameters, ITransactionParameters } from '../interf
5
5
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
6
6
  import { ECPairInterface } from 'ecpair';
7
7
  import { TweakedTransaction } from '../shared/TweakedTransaction.js';
8
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
8
9
  export declare abstract class TransactionBuilder<T extends TransactionType> extends TweakedTransaction {
9
10
  static readonly LOCK_LEAF_SCRIPT: Buffer;
10
11
  static readonly MINIMUM_DUST: bigint;
@@ -19,7 +20,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
19
20
  protected readonly outputs: PsbtOutputExtended[];
20
21
  protected feeOutput: PsbtOutputExtended | null;
21
22
  protected totalInputAmount: bigint;
22
- protected readonly signer: Signer | ECPairInterface;
23
+ protected readonly signer: Signer | ECPairInterface | UnisatSigner;
23
24
  protected readonly network: Network;
24
25
  protected readonly feeRate: number;
25
26
  protected priorityFee: bigint;
@@ -4,8 +4,9 @@ import { ECPairInterface } from 'ecpair';
4
4
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
5
5
  import { TapLeafScript } from '../interfaces/Tap.js';
6
6
  import { ChainId } from '../../network/ChainId.js';
7
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
7
8
  export interface ITweakedTransactionData {
8
- readonly signer: Signer | ECPairInterface;
9
+ readonly signer: Signer | ECPairInterface | UnisatSigner;
9
10
  readonly network: Network;
10
11
  readonly chainId?: ChainId;
11
12
  readonly nonWitnessUtxo?: Buffer;
@@ -17,7 +18,7 @@ export declare enum TransactionSequence {
17
18
  export declare abstract class TweakedTransaction extends Logger {
18
19
  readonly logColor: string;
19
20
  finalized: boolean;
20
- protected signer: Signer | ECPairInterface;
21
+ protected signer: Signer | ECPairInterface | UnisatSigner;
21
22
  protected tweakedSigner?: ECPairInterface;
22
23
  protected network: Network;
23
24
  protected signed: boolean;
@@ -47,7 +48,7 @@ export declare abstract class TweakedTransaction extends Logger {
47
48
  protected generateTapData(): Payment;
48
49
  protected generateScriptAddress(): Payment;
49
50
  protected getSignerKey(): Signer | ECPairInterface;
50
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface): Promise<void>;
51
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean): Promise<void>;
51
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
52
53
  protected signInputs(transaction: Psbt): Promise<void>;
53
54
  protected internalPubKeyToXOnly(): Buffer;
@@ -64,6 +65,8 @@ export declare abstract class TweakedTransaction extends Logger {
64
65
  finalScriptSig: Buffer | undefined;
65
66
  finalScriptWitness: Buffer | undefined;
66
67
  };
68
+ protected signInputsWalletBased(transaction: Psbt): Promise<void>;
69
+ private attemptSignTaproot;
67
70
  private isTaprootScriptSpend;
68
71
  private isTaprootInput;
69
72
  private canSignNonTaprootInput;
@@ -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);
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.3",
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.3';
@@ -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