@btc-vision/transaction 1.1.8 → 1.1.10
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/MessageSigner.d.ts +11 -0
- package/browser/opnet.d.ts +2 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/keypair/Address.js +0 -3
- package/build/keypair/EcKeyPair.js +1 -1
- package/build/keypair/MessageSigner.d.ts +11 -0
- package/build/keypair/MessageSigner.js +41 -0
- package/build/opnet.d.ts +2 -1
- package/build/opnet.js +2 -1
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/keypair/Address.ts +1 -5
- package/src/keypair/EcKeyPair.ts +1 -1
- package/src/keypair/MessageSigner.ts +99 -0
- package/src/opnet.ts +2 -9
- package/src/transaction/builders/UnwrapSegwitTransaction.ts.disabled +0 -371
- package/src/transaction/builders/UnwrapTransaction.ts.disabled +0 -503
- package/src/transaction/builders/WrapTransaction.ts.disabled +0 -338
|
@@ -1,503 +0,0 @@
|
|
|
1
|
-
import { Taptree } from '@btc-vision/bitcoin/src/types.js';
|
|
2
|
-
import { TransactionType } from '../enums/TransactionType.js';
|
|
3
|
-
import { IUnwrapParameters } from '../interfaces/ITransactionParameters.js';
|
|
4
|
-
import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
|
|
5
|
-
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
6
|
-
import { wBTC } from '../../metadata/contracts/wBTC.js';
|
|
7
|
-
import {
|
|
8
|
-
Network,
|
|
9
|
-
Payment,
|
|
10
|
-
payments,
|
|
11
|
-
Psbt,
|
|
12
|
-
PsbtInput,
|
|
13
|
-
PsbtInputExtended,
|
|
14
|
-
PsbtOutputExtended,
|
|
15
|
-
} from '@btc-vision/bitcoin';
|
|
16
|
-
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
17
|
-
import { IWBTCUTXODocument, PsbtTransaction, VaultUTXOs } from '../processor/PsbtTransaction.js';
|
|
18
|
-
import { MultiSignGenerator } from '../../generators/builders/MultiSignGenerator.js';
|
|
19
|
-
import { MultiSignTransaction } from './MultiSignTransaction.js';
|
|
20
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
21
|
-
import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
|
|
22
|
-
import { currentConsensusConfig } from '../../consensus/ConsensusConfig.js';
|
|
23
|
-
import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
|
|
24
|
-
import { Features } from '../../generators/Features.js';
|
|
25
|
-
import { ABICoder } from '../../abi/ABICoder.js';
|
|
26
|
-
import { Selector } from '../../utils/types.js';
|
|
27
|
-
import { BinaryWriter } from '../../buffer/BinaryWriter.js';
|
|
28
|
-
|
|
29
|
-
const abiCoder: ABICoder = new ABICoder();
|
|
30
|
-
const numsPoint: Buffer = Buffer.from(
|
|
31
|
-
'50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
|
|
32
|
-
'hex',
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Unwrap transaction
|
|
37
|
-
* @class UnwrapTransaction
|
|
38
|
-
*/
|
|
39
|
-
export class UnwrapTransaction extends SharedInteractionTransaction<TransactionType.WBTC_UNWRAP> {
|
|
40
|
-
private static readonly UNWRAP_SELECTOR: Selector = Number(
|
|
41
|
-
'0x' + abiCoder.encodeSelector('burn'),
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
public type: TransactionType.WBTC_UNWRAP = TransactionType.WBTC_UNWRAP;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* The amount to wrap
|
|
48
|
-
* @private
|
|
49
|
-
*/
|
|
50
|
-
public readonly amount: bigint;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* The compiled target script
|
|
54
|
-
* @protected
|
|
55
|
-
*/
|
|
56
|
-
protected readonly compiledTargetScript: Buffer;
|
|
57
|
-
/**
|
|
58
|
-
* The script tree
|
|
59
|
-
* @protected
|
|
60
|
-
*/
|
|
61
|
-
protected readonly scriptTree: Taptree;
|
|
62
|
-
/**
|
|
63
|
-
* The sighash types for the transaction
|
|
64
|
-
* @protected
|
|
65
|
-
*/
|
|
66
|
-
protected sighashTypes: number[] = [];
|
|
67
|
-
/**
|
|
68
|
-
* Contract secret for the interaction
|
|
69
|
-
* @protected
|
|
70
|
-
*/
|
|
71
|
-
protected readonly contractSecret: Buffer;
|
|
72
|
-
/**
|
|
73
|
-
* The vault UTXOs
|
|
74
|
-
* @protected
|
|
75
|
-
*/
|
|
76
|
-
protected readonly vaultUTXOs: VaultUTXOs[];
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Estimated unwrap loss due to bitcoin fees in satoshis.
|
|
80
|
-
* @protected
|
|
81
|
-
*/
|
|
82
|
-
protected readonly estimatedFeeLoss: bigint = 0n;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* The wBTC contract
|
|
86
|
-
* @private
|
|
87
|
-
*/
|
|
88
|
-
private readonly wbtc: wBTC;
|
|
89
|
-
private readonly calculatedSignHash: number = PsbtTransaction.calculateSignHash(
|
|
90
|
-
this.sighashTypes,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
public constructor(parameters: IUnwrapParameters) {
|
|
94
|
-
if (parameters.amount < TransactionBuilder.MINIMUM_DUST) {
|
|
95
|
-
throw new Error('Amount is below dust limit');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
parameters.disableAutoRefund = true; // we have to disable auto refund for this transaction, so it does not create an unwanted output.
|
|
99
|
-
parameters.calldata = UnwrapTransaction.generateBurnCalldata(parameters.amount);
|
|
100
|
-
|
|
101
|
-
super(parameters);
|
|
102
|
-
|
|
103
|
-
this.wbtc = new wBTC(parameters.network, parameters.chainId);
|
|
104
|
-
this.to = this.wbtc.getAddress();
|
|
105
|
-
|
|
106
|
-
this.vaultUTXOs = parameters.unwrapUTXOs;
|
|
107
|
-
this.estimatedFeeLoss = UnwrapTransaction.preEstimateTaprootTransactionFees(
|
|
108
|
-
BigInt(this.feeRate),
|
|
109
|
-
this.calculateNumInputs(this.vaultUTXOs),
|
|
110
|
-
2n,
|
|
111
|
-
this.calculateNumSignatures(this.vaultUTXOs),
|
|
112
|
-
65n,
|
|
113
|
-
this.calculateNumEmptyWitnesses(this.vaultUTXOs),
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
this.amount = parameters.amount;
|
|
117
|
-
this.contractSecret = this.generateSecret();
|
|
118
|
-
|
|
119
|
-
this.calldataGenerator = new CalldataGenerator(
|
|
120
|
-
Buffer.from(this.signer.publicKey),
|
|
121
|
-
this.scriptSignerXOnlyPubKey(),
|
|
122
|
-
this.network,
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
this.compiledTargetScript = this.calldataGenerator.compile(
|
|
126
|
-
this.calldata,
|
|
127
|
-
this.contractSecret,
|
|
128
|
-
[Features.UNWRAP],
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
this.scriptTree = this.getScriptTree();
|
|
132
|
-
this.internalInit();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Generate a valid wBTC calldata
|
|
137
|
-
* @param {bigint} amount - The amount to wrap
|
|
138
|
-
* @private
|
|
139
|
-
* @returns {Buffer} - The calldata
|
|
140
|
-
*/
|
|
141
|
-
public static generateBurnCalldata(amount: bigint): Buffer {
|
|
142
|
-
if (!amount) throw new Error('Amount is required');
|
|
143
|
-
|
|
144
|
-
const bufWriter: BinaryWriter = new BinaryWriter();
|
|
145
|
-
bufWriter.writeSelector(UnwrapTransaction.UNWRAP_SELECTOR);
|
|
146
|
-
bufWriter.writeU256(amount);
|
|
147
|
-
|
|
148
|
-
return Buffer.from(bufWriter.getBuffer());
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* @description Signs the transaction
|
|
153
|
-
* @public
|
|
154
|
-
* @returns {Promise<Psbt>} - The signed transaction in hex format
|
|
155
|
-
* @throws {Error} - If something went wrong
|
|
156
|
-
*/
|
|
157
|
-
public async signPSBT(): Promise<Psbt> {
|
|
158
|
-
if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
159
|
-
throw new Error(
|
|
160
|
-
'Invalid contract address. The contract address must be a taproot address.',
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!this.vaultUTXOs.length) {
|
|
165
|
-
throw new Error('No vault UTXOs provided');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
await this.buildTransaction();
|
|
169
|
-
this.ignoreSignatureError();
|
|
170
|
-
this.mergeVaults();
|
|
171
|
-
|
|
172
|
-
const builtTx = await this.internalBuildTransaction(this.transaction);
|
|
173
|
-
if (builtTx) {
|
|
174
|
-
return this.transaction;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
throw new Error('Could not sign transaction');
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
public getRefund(): bigint {
|
|
181
|
-
let losses: bigint = -currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT;
|
|
182
|
-
|
|
183
|
-
for (const vault of this.vaultUTXOs) {
|
|
184
|
-
for (let i = 0; i < vault.utxos.length; i++) {
|
|
185
|
-
losses += currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Since we are creating one output when consolidating, we need to add the fee for that output.
|
|
190
|
-
return losses;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* @description Get the estimated unwrap loss due to bitcoin fees in satoshis.
|
|
195
|
-
* @description If the number is negative, it means the user will get a refund.
|
|
196
|
-
* @description If the number is positive, it means the user will lose that amount.
|
|
197
|
-
* @public
|
|
198
|
-
* @returns {bigint} - The estimated fee loss or refund
|
|
199
|
-
*/
|
|
200
|
-
public getFeeLossOrRefund(): bigint {
|
|
201
|
-
const refund: bigint = this.getRefund();
|
|
202
|
-
|
|
203
|
-
return refund - this.estimatedFeeLoss;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* @description Merge vault UTXOs into the transaction
|
|
208
|
-
* @protected
|
|
209
|
-
*/
|
|
210
|
-
protected mergeVaults(): void {
|
|
211
|
-
const totalInputAmount: bigint = this.getVaultTotalOutputAmount(this.vaultUTXOs);
|
|
212
|
-
|
|
213
|
-
let refund: bigint = this.getRefund();
|
|
214
|
-
const outputLeftAmount = totalInputAmount - refund - this.amount;
|
|
215
|
-
|
|
216
|
-
if (outputLeftAmount === currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT) {
|
|
217
|
-
refund += currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT;
|
|
218
|
-
} else if (outputLeftAmount < currentConsensusConfig.VAULT_MINIMUM_AMOUNT) {
|
|
219
|
-
throw new Error(
|
|
220
|
-
`Output left amount is below the minimum amount: ${outputLeftAmount} below ${currentConsensusConfig.VAULT_MINIMUM_AMOUNT}`,
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const outAmount: bigint = this.amount + refund - this.estimatedFeeLoss;
|
|
225
|
-
const bestVault = BitcoinUtils.findVaultWithMostPublicKeys(this.vaultUTXOs);
|
|
226
|
-
if (!bestVault) {
|
|
227
|
-
throw new Error('No vaults provided');
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const hasConsolidation: boolean =
|
|
231
|
-
outputLeftAmount > currentConsensusConfig.VAULT_MINIMUM_AMOUNT &&
|
|
232
|
-
outputLeftAmount - currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT !== 0n;
|
|
233
|
-
|
|
234
|
-
if (hasConsolidation) {
|
|
235
|
-
this.success(`Consolidating output with ${outputLeftAmount} sat.`);
|
|
236
|
-
} else {
|
|
237
|
-
this.warn(`No consolidation in this transaction.`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
outputLeftAmount - currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT !==
|
|
242
|
-
0n
|
|
243
|
-
) {
|
|
244
|
-
// If the amount left is 0, we don't consolidate the output.
|
|
245
|
-
this.addOutput({
|
|
246
|
-
address: bestVault.vault,
|
|
247
|
-
value: Number(outputLeftAmount),
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (outAmount < TransactionBuilder.MINIMUM_DUST) {
|
|
252
|
-
throw new Error(
|
|
253
|
-
`Amount is below dust limit. The requested amount can not be unwrapped since, after fees, it is below the dust limit. Dust: ${outAmount} sat. Are your bitcoin fees too high?`,
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const percentageLossOverInitialAmount = (outAmount * 100n) / this.amount;
|
|
258
|
-
if (percentageLossOverInitialAmount <= 60n) {
|
|
259
|
-
// For user safety, we don't allow more than 60% loss over the initial amount.
|
|
260
|
-
throw new Error(
|
|
261
|
-
`For user safety, OPNet will decline this transaction since you will lose ${100n - percentageLossOverInitialAmount}% of your btc by doing this transaction due to bitcoin fees. Are your bitcoin fees too high?`,
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
this.addOutput({
|
|
266
|
-
address: this.from,
|
|
267
|
-
value: Number(outAmount),
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
for (const vault of this.vaultUTXOs) {
|
|
271
|
-
this.addVaultInputs(vault);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
protected calculateNumEmptyWitnesses(vault: VaultUTXOs[]): bigint {
|
|
276
|
-
let numSignatures = 0n;
|
|
277
|
-
for (const v of vault) {
|
|
278
|
-
numSignatures += BigInt(v.publicKeys.length - v.minimum) * BigInt(v.utxos.length);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return numSignatures;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
protected calculateNumSignatures(vault: VaultUTXOs[]): bigint {
|
|
285
|
-
let numSignatures = 0n;
|
|
286
|
-
for (const v of vault) {
|
|
287
|
-
numSignatures += BigInt(v.minimum * v.utxos.length);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return numSignatures;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
protected calculateNumInputs(vault: VaultUTXOs[]): bigint {
|
|
294
|
-
let numSignatures = 0n;
|
|
295
|
-
for (const v of vault) {
|
|
296
|
-
numSignatures += BigInt(v.utxos.length);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return numSignatures;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Converts the public key to x-only.
|
|
304
|
-
* @protected
|
|
305
|
-
* @returns {Buffer}
|
|
306
|
-
*/
|
|
307
|
-
protected internalPubKeyToXOnly(): Buffer {
|
|
308
|
-
return toXOnly(numsPoint);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Generate an input for a vault UTXO
|
|
313
|
-
* @param {Buffer[]} pubkeys The public keys
|
|
314
|
-
* @param {number} minimumSignatures The minimum number of signatures
|
|
315
|
-
* @protected
|
|
316
|
-
* @returns {Taptree} The tap tree
|
|
317
|
-
* @throws {Error} If something went wrong
|
|
318
|
-
*/
|
|
319
|
-
protected generateTapDataForInput(
|
|
320
|
-
pubkeys: Buffer[],
|
|
321
|
-
minimumSignatures: number,
|
|
322
|
-
): {
|
|
323
|
-
internalPubkey: Buffer;
|
|
324
|
-
network: Network;
|
|
325
|
-
scriptTree: Taptree;
|
|
326
|
-
redeem: Payment;
|
|
327
|
-
} {
|
|
328
|
-
const compiledTargetScript = MultiSignGenerator.compile(pubkeys, minimumSignatures);
|
|
329
|
-
const scriptTree: Taptree = [
|
|
330
|
-
{
|
|
331
|
-
output: compiledTargetScript,
|
|
332
|
-
version: 192,
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
output: MultiSignTransaction.LOCK_LEAF_SCRIPT,
|
|
336
|
-
version: 192,
|
|
337
|
-
},
|
|
338
|
-
];
|
|
339
|
-
|
|
340
|
-
const redeem: Payment = {
|
|
341
|
-
output: compiledTargetScript,
|
|
342
|
-
redeemVersion: 192,
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
return {
|
|
346
|
-
internalPubkey: this.internalPubKeyToXOnly(),
|
|
347
|
-
network: this.network,
|
|
348
|
-
scriptTree: scriptTree,
|
|
349
|
-
redeem: redeem,
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Generate the script solution
|
|
355
|
-
* @param {PsbtInput} input The input
|
|
356
|
-
* @protected
|
|
357
|
-
*
|
|
358
|
-
* @returns {Buffer[]} The script solution
|
|
359
|
-
*/
|
|
360
|
-
protected getScriptSolution(input: PsbtInput): Buffer[] {
|
|
361
|
-
if (!input.tapScriptSig) {
|
|
362
|
-
throw new Error('Tap script signature is required');
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return [
|
|
366
|
-
this.contractSecret,
|
|
367
|
-
toXOnly(Buffer.from(this.signer.publicKey)),
|
|
368
|
-
input.tapScriptSig[0].signature,
|
|
369
|
-
input.tapScriptSig[1].signature,
|
|
370
|
-
];
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Builds the transaction.
|
|
375
|
-
* @param {Psbt} transaction - The transaction to build
|
|
376
|
-
* @param checkPartialSigs
|
|
377
|
-
* @protected
|
|
378
|
-
* @returns {Promise<boolean>}
|
|
379
|
-
* @throws {Error} - If something went wrong while building the transaction
|
|
380
|
-
*/
|
|
381
|
-
protected async internalBuildTransaction(
|
|
382
|
-
transaction: Psbt,
|
|
383
|
-
checkPartialSigs: boolean = false,
|
|
384
|
-
): Promise<boolean> {
|
|
385
|
-
if (transaction.data.inputs.length === 0) {
|
|
386
|
-
const inputs: PsbtInputExtended[] = this.getInputs();
|
|
387
|
-
const outputs: PsbtOutputExtended[] = this.getOutputs();
|
|
388
|
-
|
|
389
|
-
transaction.setMaximumFeeRate(this._maximumFeeRate);
|
|
390
|
-
transaction.addInputs(inputs, checkPartialSigs);
|
|
391
|
-
|
|
392
|
-
for (let i = 0; i < this.updateInputs.length; i++) {
|
|
393
|
-
transaction.updateInput(i, this.updateInputs[i]);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
transaction.addOutputs(outputs);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
try {
|
|
400
|
-
try {
|
|
401
|
-
await this.signInputs(transaction);
|
|
402
|
-
} catch (e) {
|
|
403
|
-
console.log(e);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (this.finalized) {
|
|
407
|
-
this.transactionFee = BigInt(transaction.getFee());
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return true;
|
|
411
|
-
} catch (e) {
|
|
412
|
-
const err: Error = e as Error;
|
|
413
|
-
|
|
414
|
-
this.error(
|
|
415
|
-
`[internalBuildTransaction] Something went wrong while getting building the transaction: ${err.stack}`,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* @description Add a vault UTXO to the transaction
|
|
424
|
-
* @private
|
|
425
|
-
*/
|
|
426
|
-
private addVaultUTXO(
|
|
427
|
-
utxo: IWBTCUTXODocument,
|
|
428
|
-
pubkeys: Buffer[],
|
|
429
|
-
minimumSignatures: number,
|
|
430
|
-
): void {
|
|
431
|
-
const tapInput = this.generateTapDataForInput(pubkeys, minimumSignatures);
|
|
432
|
-
const tap = payments.p2tr(tapInput);
|
|
433
|
-
|
|
434
|
-
if (!tap.witness) throw new Error('Failed to generate taproot witness');
|
|
435
|
-
|
|
436
|
-
const controlBlock = tap.witness[tap.witness.length - 1];
|
|
437
|
-
const input: PsbtInputExtended = {
|
|
438
|
-
hash: utxo.hash,
|
|
439
|
-
index: utxo.outputIndex,
|
|
440
|
-
witnessUtxo: {
|
|
441
|
-
script: Buffer.from(utxo.output, 'base64'),
|
|
442
|
-
value: Number(utxo.value),
|
|
443
|
-
},
|
|
444
|
-
sequence: this.sequence,
|
|
445
|
-
tapLeafScript: [
|
|
446
|
-
{
|
|
447
|
-
leafVersion: tapInput.redeem.redeemVersion as number,
|
|
448
|
-
script: tapInput.redeem.output as Buffer,
|
|
449
|
-
controlBlock: controlBlock,
|
|
450
|
-
},
|
|
451
|
-
],
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
if (this.calculatedSignHash) {
|
|
455
|
-
input.sighashType = this.calculatedSignHash;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
this.addInput(input);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* @description Add vault inputs to the transaction
|
|
463
|
-
* @param {VaultUTXOs} vault The vault UTXOs
|
|
464
|
-
* @private
|
|
465
|
-
*/
|
|
466
|
-
private addVaultInputs(vault: VaultUTXOs): void {
|
|
467
|
-
const pubKeys = vault.publicKeys.map((key) => Buffer.from(key, 'base64'));
|
|
468
|
-
|
|
469
|
-
for (const utxo of vault.utxos) {
|
|
470
|
-
this.addVaultUTXO(utxo, pubKeys, vault.minimum);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* @description Calculate the amount left to refund to the first vault.
|
|
476
|
-
* @param {VaultUTXOs[]} vaults The vaults
|
|
477
|
-
* @private
|
|
478
|
-
* @returns {bigint} The amount left
|
|
479
|
-
*/
|
|
480
|
-
private calculateOutputLeftAmountFromVaults(vaults: VaultUTXOs[]): bigint {
|
|
481
|
-
const total = this.getVaultTotalOutputAmount(vaults);
|
|
482
|
-
|
|
483
|
-
return total - this.amount;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Get the total output amount from the vaults
|
|
488
|
-
* @description Get the total output amount from the vaults
|
|
489
|
-
* @param {VaultUTXOs[]} vaults The vaults
|
|
490
|
-
* @private
|
|
491
|
-
* @returns {bigint} The total output amount
|
|
492
|
-
*/
|
|
493
|
-
private getVaultTotalOutputAmount(vaults: VaultUTXOs[]): bigint {
|
|
494
|
-
let total = BigInt(0);
|
|
495
|
-
for (const vault of vaults) {
|
|
496
|
-
for (const utxo of vault.utxos) {
|
|
497
|
-
total += BigInt(utxo.value);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return total;
|
|
502
|
-
}
|
|
503
|
-
}
|