@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.
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
- package/browser/transaction/browser/types/Unisat.d.ts +1 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +2 -1
- package/browser/transaction/shared/TweakedTransaction.d.ts +6 -3
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/buffer/BinaryWriter.js +0 -1
- package/build/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
- package/build/transaction/browser/extensions/UnisatSigner.js +103 -20
- package/build/transaction/browser/types/Unisat.d.ts +1 -1
- package/build/transaction/builders/DeploymentTransaction.d.ts +1 -0
- package/build/transaction/builders/DeploymentTransaction.js +17 -0
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
- package/build/transaction/builders/SharedInteractionTransaction.js +31 -10
- package/build/transaction/builders/TransactionBuilder.d.ts +2 -1
- package/build/transaction/shared/TweakedTransaction.d.ts +6 -3
- package/build/transaction/shared/TweakedTransaction.js +61 -43
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/buffer/BinaryWriter.ts +0 -2
- package/src/transaction/browser/extensions/UnisatSigner.ts +139 -28
- package/src/transaction/browser/types/Unisat.ts +1 -1
- package/src/transaction/builders/DeploymentTransaction.ts +25 -0
- package/src/transaction/builders/SharedInteractionTransaction.ts +51 -21
- package/src/transaction/builders/TransactionBuilder.ts +2 -1
- 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(
|
|
27
|
-
signSchnorr(
|
|
28
|
-
verify(
|
|
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;
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.1.
|
|
1
|
+
export declare const version = "1.1.3";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.1.
|
|
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(
|
|
27
|
-
signSchnorr(
|
|
28
|
-
verify(
|
|
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(
|
|
82
|
+
sign(_hash, _lowR) {
|
|
82
83
|
throw new Error('Not implemented: sign');
|
|
83
84
|
}
|
|
84
|
-
signSchnorr(
|
|
85
|
+
signSchnorr(_hash) {
|
|
85
86
|
throw new Error('Not implemented: signSchnorr');
|
|
86
87
|
}
|
|
87
|
-
verify(
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
159
|
+
let isTaproot = this.isTaprootInput(input);
|
|
160
|
+
if (reverse) {
|
|
161
|
+
isTaproot = !isTaproot;
|
|
162
|
+
}
|
|
160
163
|
let signed = false;
|
|
161
164
|
if (isTaproot) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
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
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.1.
|
|
1
|
+
export const version = '1.1.3';
|