@btc-vision/transaction 1.6.5 → 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.
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/transaction/TransactionFactory.d.ts +6 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +6 -1
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/transaction/TransactionFactory.d.ts +6 -0
- package/build/transaction/TransactionFactory.js +160 -70
- package/build/transaction/builders/DeploymentTransaction.js +2 -19
- package/build/transaction/builders/FundingTransaction.js +2 -1
- package/build/transaction/builders/InteractionTransactionP2WDA.js +2 -19
- package/build/transaction/builders/MultiSignTransaction.js +2 -2
- package/build/transaction/builders/SharedInteractionTransaction.js +6 -22
- package/build/transaction/builders/TransactionBuilder.d.ts +6 -1
- package/build/transaction/builders/TransactionBuilder.js +290 -63
- package/build/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/package.json +9 -9
- package/src/_version.ts +1 -1
- package/src/transaction/TransactionFactory.ts +232 -102
- package/src/transaction/builders/DeploymentTransaction.ts +2 -29
- package/src/transaction/builders/FundingTransaction.ts +6 -1
- package/src/transaction/builders/InteractionTransactionP2WDA.ts +2 -24
- package/src/transaction/builders/MultiSignTransaction.ts +2 -2
- package/src/transaction/builders/SharedInteractionTransaction.ts +10 -26
- package/src/transaction/builders/TransactionBuilder.ts +432 -64
- package/src/transaction/interfaces/ITransactionParameters.ts +1 -0
|
@@ -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;
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.6.
|
|
1
|
+
export declare const version = "1.6.7";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.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
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const parameters = await
|
|
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
|
-
|
|
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 (!
|
|
52
|
+
if (!feeEstimationFunding) {
|
|
42
53
|
throw new Error('Could not sign funding transaction.');
|
|
43
54
|
}
|
|
44
|
-
parameters.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
|
-
|
|
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:
|
|
69
|
+
estimatedFees: finalTransaction.estimatedFees,
|
|
62
70
|
optionalInputs: inputs,
|
|
63
71
|
};
|
|
64
|
-
const
|
|
65
|
-
const outTx = await
|
|
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
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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 (!
|
|
118
|
+
if (!feeEstimationFunding) {
|
|
112
119
|
throw new Error('Could not sign funding transaction.');
|
|
113
120
|
}
|
|
114
|
-
parameters.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
|
-
|
|
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:
|
|
136
|
+
estimatedFees: finalTransaction.estimatedFees,
|
|
133
137
|
optionalInputs: inputs,
|
|
134
138
|
};
|
|
135
|
-
const
|
|
136
|
-
const outTx = await
|
|
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:
|
|
144
|
+
estimatedFees: interactionTx.transactionFee,
|
|
141
145
|
nextUTXOs: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.from, 1),
|
|
142
|
-
challenge:
|
|
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
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
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 (!
|
|
175
|
+
if (!feeEstimationFunding) {
|
|
172
176
|
throw new Error('Could not sign funding transaction.');
|
|
173
177
|
}
|
|
174
|
-
parameters.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:
|
|
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:
|
|
198
|
-
challenge:
|
|
201
|
+
randomBytes: finalTransaction.getRndBytes(),
|
|
202
|
+
challenge: challenge,
|
|
199
203
|
nonWitnessUtxo: signedTransaction.toBuffer(),
|
|
200
|
-
estimatedFees:
|
|
204
|
+
estimatedFees: finalTransaction.estimatedFees,
|
|
201
205
|
optionalInputs: inputs,
|
|
202
206
|
};
|
|
203
|
-
const
|
|
204
|
-
const outTx = await
|
|
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:
|
|
218
|
-
contractPubKey:
|
|
221
|
+
contractAddress: deploymentTx.getContractAddress(),
|
|
222
|
+
contractPubKey: deploymentTx.contractPubKey,
|
|
219
223
|
utxos: [refundUTXO],
|
|
220
|
-
challenge:
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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);
|
|
@@ -297,9 +297,9 @@ export class MultiSignTransaction extends TransactionBuilder {
|
|
|
297
297
|
];
|
|
298
298
|
}
|
|
299
299
|
getTotalOutputAmount(utxos) {
|
|
300
|
-
let total =
|
|
300
|
+
let total = 0n;
|
|
301
301
|
for (const utxo of utxos) {
|
|
302
|
-
total +=
|
|
302
|
+
total += utxo.value;
|
|
303
303
|
}
|
|
304
304
|
return total;
|
|
305
305
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PaymentType, toXOnly, } from '@btc-vision/bitcoin';
|
|
2
|
-
import {
|
|
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
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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(
|
|
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;
|