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