@btc-vision/transaction 1.6.6 → 1.6.7

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.
@@ -36,6 +36,11 @@ export interface BitcoinTransferResponse extends BitcoinTransferBase {
36
36
  readonly original: FundingTransaction;
37
37
  }
38
38
  export declare class TransactionFactory {
39
+ debug: boolean;
40
+ private readonly DUMMY_PUBKEY;
41
+ private readonly P2TR_SCRIPT;
42
+ private readonly INITIAL_FUNDING_ESTIMATE;
43
+ private readonly MAX_ITERATIONS;
39
44
  createCustomScriptTransaction(interactionParameters: ICustomTransactionParameters): Promise<[string, string, UTXO[]]>;
40
45
  signInteraction(interactionParameters: IInteractionParameters | InteractionParametersWithoutSigner): Promise<InteractionResponse>;
41
46
  signDeployment(deploymentParameters: IDeploymentParameters): Promise<DeploymentResult>;
@@ -49,5 +54,6 @@ export declare class TransactionFactory {
49
54
  private writePSBTHeader;
50
55
  private signP2WDAInteraction;
51
56
  private getPriorityFee;
57
+ private iterateFundingAmount;
52
58
  private getUTXOAsTransaction;
53
59
  }
@@ -6,6 +6,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
6
6
  import { ECPairInterface } from 'ecpair';
7
7
  import { TweakedTransaction } from '../shared/TweakedTransaction.js';
8
8
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
9
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
9
10
  export declare const MINIMUM_AMOUNT_REWARD: bigint;
10
11
  export declare const MINIMUM_AMOUNT_CA: bigint;
11
12
  export declare const ANCHOR_SCRIPT: Buffer<ArrayBuffer>;
@@ -14,6 +15,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
14
15
  static readonly MINIMUM_DUST: bigint;
15
16
  abstract readonly type: T;
16
17
  readonly logColor: string;
18
+ debugFees: boolean;
17
19
  overflowFees: bigint;
18
20
  transactionFee: bigint;
19
21
  estimatedFees: bigint;
@@ -36,6 +38,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
36
38
  protected isPubKeyDestination: boolean;
37
39
  protected anchor: boolean;
38
40
  protected note?: Buffer;
41
+ private optionalOutputsAdded;
39
42
  protected constructor(parameters: ITransactionParameters);
40
43
  static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): string;
41
44
  static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
@@ -48,7 +51,8 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
48
51
  generateTransactionMinimalSignatures(checkPartialSigs?: boolean): Promise<void>;
49
52
  signPSBT(): Promise<Psbt>;
50
53
  addInput(input: PsbtInputExtended): void;
51
- addOutput(output: PsbtOutputExtended): void;
54
+ addOutput(output: PsbtOutputExtended, bypassMinCheck?: boolean): void;
55
+ getTotalOutputValue(): bigint;
52
56
  toAddress(): string | undefined;
53
57
  address(): string | undefined;
54
58
  estimateTransactionFees(): Promise<bigint>;
@@ -67,6 +71,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
67
71
  protected internalInit(): void;
68
72
  protected abstract buildTransaction(): Promise<void>;
69
73
  protected updateInput(input: UpdateInput): void;
74
+ protected addFeeToOutput(amountSpent: bigint, contractAddress: string, epochChallenge: IP2WSHAddress, addContractOutput: boolean): void;
70
75
  protected getWitness(): Buffer;
71
76
  protected getTapOutput(): Buffer;
72
77
  protected verifyUTXOValidity(): void;
@@ -9,6 +9,7 @@ export interface LoadedStorage {
9
9
  export interface ITransactionParameters extends ITweakedTransactionData {
10
10
  readonly from?: string;
11
11
  readonly to?: string;
12
+ readonly debugFees?: boolean;
12
13
  utxos: UTXO[];
13
14
  nonWitnessUtxo?: Buffer;
14
15
  estimatedFees?: bigint;
@@ -1 +1 @@
1
- export declare const version = "1.6.6";
1
+ export declare const version = "1.6.7";
package/build/_version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.6.6';
1
+ export const version = '1.6.7';
@@ -36,6 +36,11 @@ export interface BitcoinTransferResponse extends BitcoinTransferBase {
36
36
  readonly original: FundingTransaction;
37
37
  }
38
38
  export declare class TransactionFactory {
39
+ debug: boolean;
40
+ private readonly DUMMY_PUBKEY;
41
+ private readonly P2TR_SCRIPT;
42
+ private readonly INITIAL_FUNDING_ESTIMATE;
43
+ private readonly MAX_ITERATIONS;
39
44
  createCustomScriptTransaction(interactionParameters: ICustomTransactionParameters): Promise<[string, string, UTXO[]]>;
40
45
  signInteraction(interactionParameters: IInteractionParameters | InteractionParametersWithoutSigner): Promise<InteractionResponse>;
41
46
  signDeployment(deploymentParameters: IDeploymentParameters): Promise<DeploymentResult>;
@@ -49,5 +54,6 @@ export declare class TransactionFactory {
49
54
  private writePSBTHeader;
50
55
  private signP2WDAInteraction;
51
56
  private getPriorityFee;
57
+ private iterateFundingAmount;
52
58
  private getUTXOAsTransaction;
53
59
  }
@@ -1,3 +1,4 @@
1
+ import { Transaction } from '@btc-vision/bitcoin';
1
2
  import { currentConsensus } from '../consensus/ConsensusConfig.js';
2
3
  import { CustomScriptTransaction, } from './builders/CustomScriptTransaction.js';
3
4
  import { DeploymentTransaction } from './builders/DeploymentTransaction.js';
@@ -6,7 +7,20 @@ import { InteractionTransaction } from './builders/InteractionTransaction.js';
6
7
  import { TransactionBuilder } from './builders/TransactionBuilder.js';
7
8
  import { P2WDADetector } from '../p2wda/P2WDADetector.js';
8
9
  import { InteractionTransactionP2WDA } from './builders/InteractionTransactionP2WDA.js';
10
+ import { ChallengeSolution } from '../epoch/ChallengeSolution.js';
11
+ import { Address } from '../keypair/Address.js';
12
+ import { BitcoinUtils } from '../utils/BitcoinUtils.js';
9
13
  export class TransactionFactory {
14
+ constructor() {
15
+ this.debug = false;
16
+ this.DUMMY_PUBKEY = Buffer.alloc(32, 1);
17
+ this.P2TR_SCRIPT = Buffer.concat([
18
+ Buffer.from([0x51, 0x20]),
19
+ this.DUMMY_PUBKEY,
20
+ ]);
21
+ this.INITIAL_FUNDING_ESTIMATE = 2000n;
22
+ this.MAX_ITERATIONS = 10;
23
+ }
10
24
  async createCustomScriptTransaction(interactionParameters) {
11
25
  if (!interactionParameters.to) {
12
26
  throw new Error('Field "to" not provided.');
@@ -21,27 +35,24 @@ export class TransactionFactory {
21
35
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
22
36
  }
23
37
  const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
24
- const preTransaction = new CustomScriptTransaction({
25
- ...interactionParameters,
26
- utxos: [interactionParameters.utxos[0]],
27
- optionalInputs: inputs,
28
- });
29
- await preTransaction.generateTransactionMinimalSignatures();
30
- const parameters = await preTransaction.getFundingTransactionParameters();
38
+ const { finalTransaction, estimatedAmount, challenge } = await this.iterateFundingAmount({ ...interactionParameters, optionalInputs: inputs }, CustomScriptTransaction, async (tx) => {
39
+ const fee = await tx.estimateTransactionFees();
40
+ const priorityFee = this.getPriorityFee(interactionParameters);
41
+ const optionalValue = tx.getOptionalOutputValue();
42
+ return fee + priorityFee + optionalValue;
43
+ }, 'CustomScript');
44
+ const parameters = await finalTransaction.getFundingTransactionParameters();
31
45
  parameters.utxos = interactionParameters.utxos;
32
- parameters.amount =
33
- (await preTransaction.estimateTransactionFees()) +
34
- this.getPriorityFee(interactionParameters) +
35
- preTransaction.getOptionalOutputValue();
36
- const feeEstimationFundingTransaction = await this.createFundTransaction({
46
+ parameters.amount = estimatedAmount;
47
+ const feeEstimationFunding = await this.createFundTransaction({
37
48
  ...parameters,
38
49
  optionalOutputs: [],
39
50
  optionalInputs: [],
40
51
  });
41
- if (!feeEstimationFundingTransaction) {
52
+ if (!feeEstimationFunding) {
42
53
  throw new Error('Could not sign funding transaction.');
43
54
  }
44
- parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
55
+ parameters.estimatedFees = feeEstimationFunding.estimatedFees;
45
56
  const signedTransaction = await this.createFundTransaction({
46
57
  ...parameters,
47
58
  optionalOutputs: [],
@@ -50,19 +61,16 @@ export class TransactionFactory {
50
61
  if (!signedTransaction) {
51
62
  throw new Error('Could not sign funding transaction.');
52
63
  }
53
- interactionParameters.utxos = this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0);
54
64
  const newParams = {
55
65
  ...interactionParameters,
56
- utxos: [
57
- ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
58
- ],
59
- randomBytes: preTransaction.getRndBytes(),
66
+ utxos: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
67
+ randomBytes: finalTransaction.getRndBytes(),
60
68
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
61
- estimatedFees: preTransaction.estimatedFees,
69
+ estimatedFees: finalTransaction.estimatedFees,
62
70
  optionalInputs: inputs,
63
71
  };
64
- const finalTransaction = new CustomScriptTransaction(newParams);
65
- const outTx = await finalTransaction.signTransaction();
72
+ const customTransaction = new CustomScriptTransaction(newParams);
73
+ const outTx = await customTransaction.signTransaction();
66
74
  return [
67
75
  signedTransaction.tx.toHex(),
68
76
  outTx.toHex(),
@@ -91,27 +99,26 @@ export class TransactionFactory {
91
99
  return this.signP2WDAInteraction(interactionParameters);
92
100
  }
93
101
  const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
94
- const preTransaction = new InteractionTransaction({
95
- ...interactionParameters,
96
- utxos: [interactionParameters.utxos[0]],
97
- optionalInputs: inputs,
98
- });
99
- await preTransaction.generateTransactionMinimalSignatures();
100
- const parameters = await preTransaction.getFundingTransactionParameters();
102
+ const { finalTransaction, estimatedAmount, challenge } = await this.iterateFundingAmount({ ...interactionParameters, optionalInputs: inputs }, InteractionTransaction, async (tx) => {
103
+ const fee = await tx.estimateTransactionFees();
104
+ const outputsValue = tx.getTotalOutputValue();
105
+ return fee + outputsValue;
106
+ }, 'Interaction');
107
+ if (!challenge) {
108
+ throw new Error('Failed to get challenge from interaction transaction');
109
+ }
110
+ const parameters = await finalTransaction.getFundingTransactionParameters();
101
111
  parameters.utxos = interactionParameters.utxos;
102
- parameters.amount =
103
- (await preTransaction.estimateTransactionFees()) +
104
- this.getPriorityFee(interactionParameters) +
105
- preTransaction.getOptionalOutputValue();
106
- const feeEstimationFundingTransaction = await this.createFundTransaction({
112
+ parameters.amount = estimatedAmount;
113
+ const feeEstimationFunding = await this.createFundTransaction({
107
114
  ...parameters,
108
115
  optionalOutputs: [],
109
116
  optionalInputs: [],
110
117
  });
111
- if (!feeEstimationFundingTransaction) {
118
+ if (!feeEstimationFunding) {
112
119
  throw new Error('Could not sign funding transaction.');
113
120
  }
114
- parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
121
+ parameters.estimatedFees = feeEstimationFunding.estimatedFees;
115
122
  const signedTransaction = await this.createFundTransaction({
116
123
  ...parameters,
117
124
  optionalOutputs: [],
@@ -120,26 +127,23 @@ export class TransactionFactory {
120
127
  if (!signedTransaction) {
121
128
  throw new Error('Could not sign funding transaction.');
122
129
  }
123
- interactionParameters.utxos = this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0);
124
130
  const newParams = {
125
131
  ...interactionParameters,
126
- utxos: [
127
- ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
128
- ],
129
- randomBytes: preTransaction.getRndBytes(),
130
- challenge: preTransaction.getChallenge(),
132
+ utxos: this.getUTXOAsTransaction(signedTransaction.tx, finalTransaction.getScriptAddress(), 0),
133
+ randomBytes: finalTransaction.getRndBytes(),
134
+ challenge: challenge,
131
135
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
132
- estimatedFees: preTransaction.estimatedFees,
136
+ estimatedFees: finalTransaction.estimatedFees,
133
137
  optionalInputs: inputs,
134
138
  };
135
- const finalTransaction = new InteractionTransaction(newParams);
136
- const outTx = await finalTransaction.signTransaction();
139
+ const interactionTx = new InteractionTransaction(newParams);
140
+ const outTx = await interactionTx.signTransaction();
137
141
  return {
138
142
  fundingTransaction: signedTransaction.tx.toHex(),
139
143
  interactionTransaction: outTx.toHex(),
140
- estimatedFees: preTransaction.estimatedFees,
144
+ estimatedFees: interactionTx.transactionFee,
141
145
  nextUTXOs: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.from, 1),
142
- challenge: preTransaction.getChallenge().toRaw(),
146
+ challenge: challenge.toRaw(),
143
147
  };
144
148
  }
145
149
  async signDeployment(deploymentParameters) {
@@ -151,27 +155,27 @@ export class TransactionFactory {
151
155
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
152
156
  }
153
157
  const inputs = this.parseOptionalInputs(deploymentParameters.optionalInputs);
154
- const preTransaction = new DeploymentTransaction({
155
- ...deploymentParameters,
156
- utxos: [deploymentParameters.utxos[0]],
157
- optionalInputs: inputs,
158
- });
159
- await preTransaction.generateTransactionMinimalSignatures();
160
- const parameters = await preTransaction.getFundingTransactionParameters();
158
+ const { finalTransaction, estimatedAmount, challenge } = await this.iterateFundingAmount({ ...deploymentParameters, optionalInputs: inputs }, DeploymentTransaction, async (tx) => {
159
+ const fee = await tx.estimateTransactionFees();
160
+ const priorityFee = this.getPriorityFee(deploymentParameters);
161
+ const optionalValue = tx.getOptionalOutputValue();
162
+ return fee + priorityFee + optionalValue;
163
+ }, 'Deployment');
164
+ if (!challenge) {
165
+ throw new Error('Failed to get challenge from deployment transaction');
166
+ }
167
+ const parameters = await finalTransaction.getFundingTransactionParameters();
161
168
  parameters.utxos = deploymentParameters.utxos;
162
- parameters.amount =
163
- (await preTransaction.estimateTransactionFees()) +
164
- this.getPriorityFee(deploymentParameters) +
165
- preTransaction.getOptionalOutputValue();
166
- const feeEstimationFundingTransaction = await this.createFundTransaction({
169
+ parameters.amount = estimatedAmount;
170
+ const feeEstimationFunding = await this.createFundTransaction({
167
171
  ...parameters,
168
172
  optionalOutputs: [],
169
173
  optionalInputs: [],
170
174
  });
171
- if (!feeEstimationFundingTransaction) {
175
+ if (!feeEstimationFunding) {
172
176
  throw new Error('Could not sign funding transaction.');
173
177
  }
174
- parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
178
+ parameters.estimatedFees = feeEstimationFunding.estimatedFees;
175
179
  const fundingTransaction = new FundingTransaction({
176
180
  ...parameters,
177
181
  optionalInputs: [],
@@ -187,21 +191,21 @@ export class TransactionFactory {
187
191
  outputIndex: 0,
188
192
  scriptPubKey: {
189
193
  hex: out.script.toString('hex'),
190
- address: preTransaction.getScriptAddress(),
194
+ address: finalTransaction.getScriptAddress(),
191
195
  },
192
196
  value: BigInt(out.value),
193
197
  };
194
198
  const newParams = {
195
199
  ...deploymentParameters,
196
200
  utxos: [newUtxo],
197
- randomBytes: preTransaction.getRndBytes(),
198
- challenge: preTransaction.getChallenge(),
201
+ randomBytes: finalTransaction.getRndBytes(),
202
+ challenge: challenge,
199
203
  nonWitnessUtxo: signedTransaction.toBuffer(),
200
- estimatedFees: preTransaction.estimatedFees,
204
+ estimatedFees: finalTransaction.estimatedFees,
201
205
  optionalInputs: inputs,
202
206
  };
203
- const finalTransaction = new DeploymentTransaction(newParams);
204
- const outTx = await finalTransaction.signTransaction();
207
+ const deploymentTx = new DeploymentTransaction(newParams);
208
+ const outTx = await deploymentTx.signTransaction();
205
209
  const out2 = signedTransaction.outs[1];
206
210
  const refundUTXO = {
207
211
  transactionId: signedTransaction.getId(),
@@ -214,10 +218,10 @@ export class TransactionFactory {
214
218
  };
215
219
  return {
216
220
  transaction: [signedTransaction.toHex(), outTx.toHex()],
217
- contractAddress: finalTransaction.getContractAddress(),
218
- contractPubKey: finalTransaction.contractPubKey,
221
+ contractAddress: deploymentTx.getContractAddress(),
222
+ contractPubKey: deploymentTx.contractPubKey,
219
223
  utxos: [refundUTXO],
220
- challenge: preTransaction.getChallenge().toRaw(),
224
+ challenge: challenge.toRaw(),
221
225
  };
222
226
  }
223
227
  async createBTCTransfer(parameters) {
@@ -352,6 +356,92 @@ export class TransactionFactory {
352
356
  }
353
357
  return totalFee;
354
358
  }
359
+ async iterateFundingAmount(params, TransactionClass, calculateAmount, debugPrefix) {
360
+ const randomBytes = 'randomBytes' in params
361
+ ? (params.randomBytes ?? BitcoinUtils.rndBytes())
362
+ : BitcoinUtils.rndBytes();
363
+ const dummyAddress = Address.dead().p2tr(params.network);
364
+ let estimatedFundingAmount = this.INITIAL_FUNDING_ESTIMATE;
365
+ let previousAmount = 0n;
366
+ let iterations = 0;
367
+ let finalPreTransaction = null;
368
+ let challenge = null;
369
+ while (iterations < this.MAX_ITERATIONS && estimatedFundingAmount !== previousAmount) {
370
+ previousAmount = estimatedFundingAmount;
371
+ const dummyTx = new Transaction();
372
+ dummyTx.addOutput(this.P2TR_SCRIPT, Number(estimatedFundingAmount));
373
+ const simulatedFundedUtxo = {
374
+ transactionId: Buffer.alloc(32, 0).toString('hex'),
375
+ outputIndex: 0,
376
+ scriptPubKey: {
377
+ hex: this.P2TR_SCRIPT.toString('hex'),
378
+ address: dummyAddress,
379
+ },
380
+ value: estimatedFundingAmount,
381
+ nonWitnessUtxo: dummyTx.toBuffer(),
382
+ };
383
+ let txParams;
384
+ if ('challenge' in params && params.challenge) {
385
+ const withChallenge = {
386
+ ...params,
387
+ utxos: [simulatedFundedUtxo],
388
+ randomBytes: randomBytes,
389
+ challenge: challenge ?? params.challenge,
390
+ };
391
+ txParams = withChallenge;
392
+ }
393
+ else {
394
+ const withoutChallenge = {
395
+ ...params,
396
+ utxos: [simulatedFundedUtxo],
397
+ randomBytes: randomBytes,
398
+ };
399
+ txParams = withoutChallenge;
400
+ }
401
+ const preTransaction = new TransactionClass(txParams);
402
+ try {
403
+ await preTransaction.generateTransactionMinimalSignatures();
404
+ estimatedFundingAmount = await calculateAmount(preTransaction);
405
+ }
406
+ catch (error) {
407
+ if (error instanceof Error) {
408
+ const match = error.message.match(/need (\d+) sats but only have (\d+) sats/);
409
+ if (match) {
410
+ estimatedFundingAmount = BigInt(match[1]);
411
+ if (this.debug) {
412
+ console.log(`${debugPrefix}: Caught insufficient funds, updating to ${estimatedFundingAmount}`);
413
+ }
414
+ }
415
+ else {
416
+ throw error;
417
+ }
418
+ }
419
+ else {
420
+ throw new Error('Unknown error during fee estimation');
421
+ }
422
+ }
423
+ finalPreTransaction = preTransaction;
424
+ if ('getChallenge' in preTransaction &&
425
+ typeof preTransaction.getChallenge === 'function') {
426
+ const result = preTransaction.getChallenge();
427
+ if (result instanceof ChallengeSolution) {
428
+ challenge = result;
429
+ }
430
+ }
431
+ iterations++;
432
+ if (this.debug) {
433
+ console.log(`${debugPrefix} Iteration ${iterations}: Previous=${previousAmount}, New=${estimatedFundingAmount}`);
434
+ }
435
+ }
436
+ if (!finalPreTransaction) {
437
+ throw new Error(`Failed to converge on ${debugPrefix} funding amount`);
438
+ }
439
+ return {
440
+ finalTransaction: finalPreTransaction,
441
+ estimatedAmount: estimatedFundingAmount,
442
+ challenge,
443
+ };
444
+ }
355
445
  getUTXOAsTransaction(tx, to, index) {
356
446
  if (!tx.outs[index])
357
447
  return [];
@@ -1,6 +1,6 @@
1
1
  import { TransactionType } from '../enums/TransactionType.js';
2
2
  import { crypto as bitCrypto, PaymentType, toXOnly, } from '@btc-vision/bitcoin';
3
- import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
3
+ import { TransactionBuilder } from './TransactionBuilder.js';
4
4
  import { DeploymentGenerator, versionBuffer, } from '../../generators/builders/DeploymentGenerator.js';
5
5
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
6
6
  import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
@@ -104,24 +104,7 @@ export class DeploymentTransaction extends TransactionBuilder {
104
104
  };
105
105
  this.addInputsFromUTXO();
106
106
  const amountSpent = this.getTransactionOPNetFee();
107
- let amountToCA;
108
- if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
109
- amountToCA = MINIMUM_AMOUNT_CA;
110
- }
111
- else {
112
- amountToCA = amountSpent;
113
- }
114
- this.addOutput({
115
- value: Number(amountToCA),
116
- address: this.getContractAddress(),
117
- });
118
- if (amountToCA === MINIMUM_AMOUNT_CA &&
119
- amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
120
- this.addOutput({
121
- value: Number(amountSpent - amountToCA),
122
- address: this.epochChallenge.address,
123
- });
124
- }
107
+ this.addFeeToOutput(amountSpent, this.getContractAddress(), this.epochChallenge, true);
125
108
  await this.addRefundOutput(amountSpent + this.addOptionalOutputsAndGetAmount());
126
109
  }
127
110
  async signInputsWalletBased(transaction) {
@@ -33,7 +33,8 @@ export class FundingTransaction extends TransactionBuilder {
33
33
  address: this.to,
34
34
  });
35
35
  }
36
- await this.addRefundOutput(this.amount + this.addOptionalOutputsAndGetAmount());
36
+ const totalOutputAmount = this.amount + this.addOptionalOutputsAndGetAmount();
37
+ await this.addRefundOutput(totalOutputAmount);
37
38
  }
38
39
  splitInputs(amountSpent) {
39
40
  if (!this.to) {
@@ -1,7 +1,7 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import { toXOnly } from '@btc-vision/bitcoin';
3
3
  import { TransactionType } from '../enums/TransactionType.js';
4
- import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
4
+ import { TransactionBuilder } from './TransactionBuilder.js';
5
5
  import { MessageSigner } from '../../keypair/MessageSigner.js';
6
6
  import { Compressor } from '../../bytecode/Compressor.js';
7
7
  import { P2WDAGenerator } from '../../generators/builders/P2WDAGenerator.js';
@@ -64,24 +64,7 @@ export class InteractionTransactionP2WDA extends TransactionBuilder {
64
64
  if (!this.to)
65
65
  throw new Error('To address is required');
66
66
  const amountSpent = this.getTransactionOPNetFee();
67
- let amountToCA;
68
- if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
69
- amountToCA = MINIMUM_AMOUNT_CA;
70
- }
71
- else {
72
- amountToCA = amountSpent;
73
- }
74
- this.addOutput({
75
- value: Number(amountToCA),
76
- address: this.to,
77
- });
78
- if (amountToCA === MINIMUM_AMOUNT_CA &&
79
- amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
80
- this.addOutput({
81
- value: Number(amountSpent - amountToCA),
82
- address: this.epochChallenge.address,
83
- });
84
- }
67
+ this.addFeeToOutput(amountSpent, this.to, this.epochChallenge, false);
85
68
  const amount = this.addOptionalOutputsAndGetAmount();
86
69
  if (!this.disableAutoRefund) {
87
70
  await this.addRefundOutput(amountSpent + amount);
@@ -1,5 +1,5 @@
1
1
  import { PaymentType, toXOnly, } from '@btc-vision/bitcoin';
2
- import { MINIMUM_AMOUNT_CA, MINIMUM_AMOUNT_REWARD, TransactionBuilder, } from './TransactionBuilder.js';
2
+ import { MINIMUM_AMOUNT_REWARD, TransactionBuilder } from './TransactionBuilder.js';
3
3
  import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
4
4
  import { Compressor } from '../../bytecode/Compressor.js';
5
5
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
@@ -180,28 +180,12 @@ export class SharedInteractionTransaction extends TransactionBuilder {
180
180
  async createMineableRewardOutputs() {
181
181
  if (!this.to)
182
182
  throw new Error('To address is required');
183
- const amountSpent = this.getTransactionOPNetFee();
184
- let amountToCA;
185
- if (amountSpent > MINIMUM_AMOUNT_REWARD + MINIMUM_AMOUNT_CA) {
186
- amountToCA = MINIMUM_AMOUNT_CA;
187
- }
188
- else {
189
- amountToCA = amountSpent;
190
- }
191
- this.addOutput({
192
- value: Number(amountToCA),
193
- address: this.to,
194
- });
195
- if (amountToCA === MINIMUM_AMOUNT_CA &&
196
- amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
197
- this.addOutput({
198
- value: Number(amountSpent - amountToCA),
199
- address: this.epochChallenge.address,
200
- });
201
- }
202
- const amount = this.addOptionalOutputsAndGetAmount();
183
+ const opnetFee = this.getTransactionOPNetFee();
184
+ this.addFeeToOutput(opnetFee, this.to, this.epochChallenge, false);
185
+ const actualOutputAmount = opnetFee < MINIMUM_AMOUNT_REWARD ? MINIMUM_AMOUNT_REWARD : opnetFee;
186
+ const optionalAmount = this.addOptionalOutputsAndGetAmount();
203
187
  if (!this.disableAutoRefund) {
204
- await this.addRefundOutput(amountSpent + amount);
188
+ await this.addRefundOutput(actualOutputAmount + optionalAmount);
205
189
  }
206
190
  }
207
191
  getPubKeys() {
@@ -6,6 +6,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
6
6
  import { ECPairInterface } from 'ecpair';
7
7
  import { TweakedTransaction } from '../shared/TweakedTransaction.js';
8
8
  import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
9
+ import { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
9
10
  export declare const MINIMUM_AMOUNT_REWARD: bigint;
10
11
  export declare const MINIMUM_AMOUNT_CA: bigint;
11
12
  export declare const ANCHOR_SCRIPT: Buffer<ArrayBuffer>;
@@ -14,6 +15,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
14
15
  static readonly MINIMUM_DUST: bigint;
15
16
  abstract readonly type: T;
16
17
  readonly logColor: string;
18
+ debugFees: boolean;
17
19
  overflowFees: bigint;
18
20
  transactionFee: bigint;
19
21
  estimatedFees: bigint;
@@ -36,6 +38,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
36
38
  protected isPubKeyDestination: boolean;
37
39
  protected anchor: boolean;
38
40
  protected note?: Buffer;
41
+ private optionalOutputsAdded;
39
42
  protected constructor(parameters: ITransactionParameters);
40
43
  static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): string;
41
44
  static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
@@ -48,7 +51,8 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
48
51
  generateTransactionMinimalSignatures(checkPartialSigs?: boolean): Promise<void>;
49
52
  signPSBT(): Promise<Psbt>;
50
53
  addInput(input: PsbtInputExtended): void;
51
- addOutput(output: PsbtOutputExtended): void;
54
+ addOutput(output: PsbtOutputExtended, bypassMinCheck?: boolean): void;
55
+ getTotalOutputValue(): bigint;
52
56
  toAddress(): string | undefined;
53
57
  address(): string | undefined;
54
58
  estimateTransactionFees(): Promise<bigint>;
@@ -67,6 +71,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
67
71
  protected internalInit(): void;
68
72
  protected abstract buildTransaction(): Promise<void>;
69
73
  protected updateInput(input: UpdateInput): void;
74
+ protected addFeeToOutput(amountSpent: bigint, contractAddress: string, epochChallenge: IP2WSHAddress, addContractOutput: boolean): void;
70
75
  protected getWitness(): Buffer;
71
76
  protected getTapOutput(): Buffer;
72
77
  protected verifyUTXOValidity(): void;