@btc-vision/transaction 1.1.8 → 1.1.9

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.
@@ -1,338 +0,0 @@
1
- import { Taptree } from '@btc-vision/bitcoin/src/types.js';
2
- import { TransactionType } from '../enums/TransactionType.js';
3
- import { TapLeafScript } from '../interfaces/Tap.js';
4
- import { IWrapParameters } from '../interfaces/ITransactionParameters.js';
5
- import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
6
- import { wBTC } from '../../metadata/contracts/wBTC.js';
7
- import { WrappedGeneration } from '../../../wbtc_disabled/WrappedGenerationParameters.js';
8
- import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
9
- import { Network, PsbtOutputExtendedAddress } from '@btc-vision/bitcoin';
10
- import { P2TR_MS } from '../shared/P2TR_MS.js';
11
- import { currentConsensusConfig } from '../../consensus/ConsensusConfig.js';
12
- import { Selector } from '../../utils/types.js';
13
- import { ABICoder } from '../../abi/ABICoder.js';
14
- import { BinaryWriter } from '../../buffer/BinaryWriter.js';
15
- import { Address } from '../../keypair/Address.js';
16
-
17
- const abiCoder: ABICoder = new ABICoder();
18
-
19
- /**
20
- * Wrapped Bitcoin transaction wrap interaction
21
- * @class InteractionTransaction
22
- */
23
- export class WrapTransaction extends SharedInteractionTransaction<TransactionType.WBTC_WRAP> {
24
- private static readonly WRAP_SELECTOR: Selector = Number(
25
- '0x' + abiCoder.encodeSelector('mint'),
26
- );
27
-
28
- public type: TransactionType.WBTC_WRAP = TransactionType.WBTC_WRAP;
29
-
30
- /**
31
- * The vault address
32
- * @private
33
- * @readonly
34
- */
35
- public readonly vault: string;
36
-
37
- /**
38
- * The amount to wrap
39
- * @private
40
- */
41
- public readonly amount: bigint;
42
-
43
- /**
44
- * The receiver of the wrapped tokens
45
- * @private
46
- */
47
- public readonly receiver: Address;
48
-
49
- /**
50
- * The compiled target script
51
- * @protected
52
- */
53
- protected readonly compiledTargetScript: Buffer;
54
-
55
- /**
56
- * Tap tree for the interaction
57
- * @protected
58
- */
59
- protected readonly scriptTree: Taptree;
60
-
61
- /**
62
- * Tap leaf script
63
- * @protected
64
- */
65
- protected tapLeafScript: TapLeafScript | null = null;
66
-
67
- /**
68
- * Contract secret for the interaction
69
- * @protected
70
- */
71
- protected readonly contractSecret: Buffer;
72
- /**
73
- * Public keys specified in the interaction
74
- * @protected
75
- */
76
- protected readonly interactionPubKeys: Buffer[] = [];
77
- /**
78
- * Minimum signatures required for the interaction
79
- * @protected
80
- */
81
- protected readonly minimumSignatures: number = 0;
82
- /**
83
- * The wBTC contract
84
- * @private
85
- */
86
- private readonly wbtc: wBTC;
87
-
88
- public constructor(parameters: IWrapParameters) {
89
- if (parameters.amount < currentConsensusConfig.VAULT_MINIMUM_AMOUNT) {
90
- throw new Error(
91
- `Amount is below the minimum required of ${currentConsensusConfig.VAULT_MINIMUM_AMOUNT} sat.`,
92
- );
93
- }
94
-
95
- const receiver: Address = parameters.receiver || new Address(parameters.signer.publicKey);
96
-
97
- parameters.calldata = WrapTransaction.generateMintCalldata(
98
- parameters.amount,
99
- receiver,
100
- parameters.network,
101
- );
102
-
103
- super(parameters);
104
-
105
- this.wbtc = new wBTC(parameters.network, parameters.chainId);
106
- this.vault = parameters.generationParameters.vault;
107
-
108
- this.to = this.wbtc.getAddress();
109
- this.receiver = receiver;
110
- this.amount = parameters.amount;
111
-
112
- this.verifyRequiredValue();
113
-
114
- this.interactionPubKeys = parameters.generationParameters.pubKeys;
115
- this.minimumSignatures = parameters.generationParameters.constraints.minimum;
116
- this.contractSecret = this.generateSecret();
117
-
118
- if (!this.verifyPublicKeysConstraints(parameters.generationParameters)) {
119
- throw new Error(
120
- 'Oops. Your wrapping request have been decline! It failed security checks!',
121
- );
122
- }
123
-
124
- this.compiledTargetScript = this.calldataGenerator.compile(
125
- this.calldata,
126
- this.contractSecret,
127
- [],
128
- this.interactionPubKeys,
129
- this.minimumSignatures,
130
- );
131
-
132
- this.scriptTree = this.getScriptTree();
133
- this.internalInit();
134
- }
135
-
136
- /**
137
- * Generate a valid wBTC calldata
138
- * @param {bigint} amount - The amount to wrap
139
- * @param {Address} to - The address to send the wrapped tokens to
140
- * @param {Network} network - The network to use
141
- * @private
142
- * @returns {Buffer} - The calldata
143
- */
144
- private static generateMintCalldata(amount: bigint, to: Address, network: Network): Buffer {
145
- if (!amount) throw new Error('Amount is required');
146
- if (!to) throw new Error('To address is required');
147
-
148
- if (!to.isValid(network)) {
149
- throw new Error(
150
- `Oops! The address ${to} is not a valid P2TR address! If you wrap at this address, your funds will be lost!`,
151
- );
152
- }
153
-
154
- const bufWriter: BinaryWriter = new BinaryWriter();
155
- bufWriter.writeSelector(WrapTransaction.WRAP_SELECTOR);
156
- bufWriter.writeAddress(to);
157
- bufWriter.writeU256(amount);
158
-
159
- return Buffer.from(bufWriter.getBuffer());
160
- }
161
-
162
- /**
163
- * Verify the data integrity received by the client.
164
- * @param {WrappedGeneration} generation - The generation parameters
165
- * @returns {boolean} - True if the data is valid
166
- * @throws {Error} - If the data is invalid
167
- */
168
- public verifyPublicKeysConstraints(generation: WrappedGeneration): boolean {
169
- if (generation.constraints.minimum < 2) {
170
- throw new Error('Minimum signatures must be at least 2');
171
- }
172
-
173
- if (
174
- generation.keys.length < generation.constraints.transactionMinimum ||
175
- generation.keys.length < generation.constraints.minimum
176
- ) {
177
- throw new Error('Not enough pub keys');
178
- }
179
-
180
- if (generation.keys.length > 255) {
181
- throw new Error('Too many pub keys');
182
- }
183
-
184
- const generatedVault: string = this.generateVaultAddress(
185
- generation.pubKeys,
186
- generation.constraints.minimum,
187
- );
188
- if (generatedVault !== generation.vault) {
189
- throw new Error(
190
- `Invalid vault address. Expected: ${generatedVault} Got: ${generation.vault}`,
191
- );
192
- }
193
-
194
- const passChecksum: Buffer = this.generateChecksumSalt(
195
- generation,
196
- this.amount,
197
- generation.vault,
198
- );
199
-
200
- const checksum: string = BitcoinUtils.opnetHash(passChecksum);
201
- if (checksum !== generation.signature) {
202
- throw new Error(`Invalid checksum. Expected: ${checksum} Got: ${generation.signature}`);
203
- }
204
-
205
- return true;
206
- }
207
-
208
- /**
209
- * Build the transaction
210
- * @protected
211
- *
212
- * @throws {Error} If the leftover funds script redeem is required
213
- * @throws {Error} If the leftover funds script redeem version is required
214
- * @throws {Error} If the leftover funds script redeem output is required
215
- * @throws {Error} If the to address is required
216
- */
217
- protected override async buildTransaction(): Promise<void> {
218
- if (!this.to) throw new Error('To address is required');
219
-
220
- const selectedRedeem = this.scriptSigner
221
- ? this.targetScriptRedeem
222
- : this.leftOverFundsScriptRedeem;
223
-
224
- if (!selectedRedeem) {
225
- throw new Error('Left over funds script redeem is required');
226
- }
227
-
228
- if (!selectedRedeem.redeemVersion) {
229
- throw new Error('Left over funds script redeem version is required');
230
- }
231
-
232
- if (!selectedRedeem.output) {
233
- throw new Error('Left over funds script redeem output is required');
234
- }
235
-
236
- this.tapLeafScript = {
237
- leafVersion: selectedRedeem.redeemVersion,
238
- script: selectedRedeem.output,
239
- controlBlock: this.getWitness(),
240
- };
241
-
242
- this.addInputsFromUTXO();
243
-
244
- const amountSpent: bigint = this.getTransactionOPNetFee();
245
- this.addOutput({
246
- value: Number(amountSpent),
247
- address: this.to,
248
- });
249
-
250
- this.addVaultOutput();
251
- await this.addRefundOutput(
252
- amountSpent +
253
- this.amount +
254
- currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT,
255
- );
256
- }
257
-
258
- /**
259
- * Verify if the required value is available
260
- * @private
261
- */
262
- private verifyRequiredValue(): void {
263
- if (this.totalInputAmount < this.amount) {
264
- throw new Error(
265
- `Not enough funds to wrap the amount specified. ${this.totalInputAmount} < ${this.amount}`,
266
- );
267
- }
268
-
269
- const valueToVault: bigint =
270
- this.amount + currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT; //this.priorityFee
271
-
272
- if (this.totalInputAmount < valueToVault) {
273
- throw new Error(
274
- `Not enough funds to wrap the amount specified. ${this.totalInputAmount} < ${valueToVault}. Make sure that your inputs cover the amount to wrap, the priority fee and the unwrap prepaid fees.`,
275
- );
276
- }
277
- }
278
-
279
- /**
280
- * Add the vault output
281
- * @private
282
- * @throws {Error} If no vault address is provided
283
- * @throws {Error} If the amount is not a number
284
- */
285
- private addVaultOutput(): void {
286
- if (!this.vault) {
287
- throw new Error(`No vault address provided`);
288
- }
289
-
290
- const valueToSend: bigint =
291
- this.amount + currentConsensusConfig.UNWRAP_CONSOLIDATION_PREPAID_FEES_SAT;
292
-
293
- const amountOutput: PsbtOutputExtendedAddress = {
294
- address: this.vault,
295
- value: Number(valueToSend),
296
- };
297
-
298
- this.addOutput(amountOutput);
299
- }
300
-
301
- /**
302
- * Generate a vault address
303
- * @param {Buffer[]} keys
304
- * @param {number} minimumSignatureRequired
305
- * @private
306
- * @returns {string}
307
- */
308
- private generateVaultAddress(keys: Buffer[], minimumSignatureRequired: number): string {
309
- return P2TR_MS.generateMultiSigAddress(keys, minimumSignatureRequired, this.network);
310
- }
311
-
312
- /**
313
- * Generate a wrapped checksum hash
314
- * @param {WrappedGeneration} param
315
- * @param {bigint} amount
316
- * @param {string} vault
317
- * @private
318
- * @returns {Buffer}
319
- */
320
- private generateChecksumSalt(param: WrappedGeneration, amount: bigint, vault: string): Buffer {
321
- const version: string = param.constraints.version;
322
- const timestamp: number = param.constraints.timestamp;
323
-
324
- const params: Buffer = Buffer.alloc(12 + version.length);
325
- params.writeBigInt64BE(BigInt(timestamp), 0);
326
- params.writeInt16BE(param.constraints.minimum, 8);
327
- params.writeInt16BE(param.constraints.transactionMinimum, 10);
328
- params.write(version, 12, version.length, 'utf-8');
329
-
330
- return Buffer.concat([
331
- ...param.pubKeys,
332
- ...param.entities.map((entity: string) => Buffer.from(entity, 'utf-8')),
333
- params,
334
- Buffer.from(amount.toString(), 'utf-8'),
335
- Buffer.from(vault, 'utf-8'),
336
- ]);
337
- }
338
- }