@btc-vision/transaction 1.6.11 → 1.6.13
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/opnet.d.ts +1 -0
- package/browser/transaction/TransactionFactory.d.ts +3 -2
- package/browser/transaction/builders/CancelTransaction.d.ts +28 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +3 -2
- package/browser/transaction/enums/TransactionType.d.ts +1 -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/opnet.d.ts +1 -0
- package/build/opnet.js +1 -0
- package/build/transaction/TransactionFactory.d.ts +3 -2
- package/build/transaction/TransactionFactory.js +8 -4
- package/build/transaction/builders/CancelTransaction.d.ts +28 -0
- package/build/transaction/builders/CancelTransaction.js +154 -0
- package/build/transaction/builders/CustomScriptTransaction.js +2 -1
- package/build/transaction/builders/DeploymentTransaction.js +2 -1
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -0
- package/build/transaction/builders/SharedInteractionTransaction.js +6 -2
- package/build/transaction/builders/TransactionBuilder.d.ts +3 -2
- package/build/transaction/builders/TransactionBuilder.js +12 -8
- package/build/transaction/enums/TransactionType.d.ts +1 -1
- package/build/transaction/enums/TransactionType.js +1 -1
- package/build/transaction/interfaces/ITransactionParameters.d.ts +1 -0
- package/build/verification/TapscriptVerificator.js +11 -4
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/opnet.ts +1 -0
- package/src/transaction/TransactionFactory.ts +15 -10
- package/src/transaction/builders/CancelTransaction.ts +269 -0
- package/src/transaction/builders/CustomScriptTransaction.ts +2 -1
- package/src/transaction/builders/DeploymentTransaction.ts +2 -1
- package/src/transaction/builders/SharedInteractionTransaction.ts +10 -3
- package/src/transaction/builders/TransactionBuilder.ts +27 -10
- package/src/transaction/enums/TransactionType.ts +1 -1
- package/src/transaction/interfaces/ITransactionParameters.ts +1 -0
- package/src/verification/TapscriptVerificator.ts +14 -3
package/browser/opnet.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export * from './transaction/builders/InteractionTransactionP2WDA.js';
|
|
|
34
34
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
35
35
|
export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
36
36
|
export * from './transaction/builders/TransactionBuilder.js';
|
|
37
|
+
export * from './transaction/builders/CancelTransaction.js';
|
|
37
38
|
export * from './epoch/interfaces/IChallengeSolution.js';
|
|
38
39
|
export * from './epoch/validator/EpochValidator.js';
|
|
39
40
|
export * from './epoch/ChallengeSolution.js';
|
|
@@ -12,7 +12,6 @@ export interface DeploymentResult {
|
|
|
12
12
|
readonly contractAddress: string;
|
|
13
13
|
readonly contractPubKey: string;
|
|
14
14
|
readonly challenge: RawChallenge;
|
|
15
|
-
readonly rawTransaction: Transaction;
|
|
16
15
|
readonly utxos: UTXO[];
|
|
17
16
|
}
|
|
18
17
|
export interface FundingTransactionResponse {
|
|
@@ -31,8 +30,10 @@ export interface InteractionResponse {
|
|
|
31
30
|
readonly interactionTransaction: string;
|
|
32
31
|
readonly estimatedFees: bigint;
|
|
33
32
|
readonly nextUTXOs: UTXO[];
|
|
33
|
+
readonly fundingUTXOs: UTXO[];
|
|
34
34
|
readonly challenge: RawChallenge;
|
|
35
|
-
readonly
|
|
35
|
+
readonly interactionAddress: string | null;
|
|
36
|
+
readonly compiledTargetScript: string | null;
|
|
36
37
|
}
|
|
37
38
|
export interface BitcoinTransferResponse extends BitcoinTransferBase {
|
|
38
39
|
readonly original: FundingTransaction;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
2
|
+
import { P2TRPayment, Psbt, PsbtInput, Taptree } from '@btc-vision/bitcoin';
|
|
3
|
+
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
4
|
+
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
5
|
+
import { ITransactionParameters } from '../interfaces/ITransactionParameters.js';
|
|
6
|
+
export interface ICancelTransactionParameters extends Omit<ITransactionParameters, 'priorityFee' | 'gasSatFee'> {
|
|
7
|
+
readonly compiledTargetScript: string | Buffer;
|
|
8
|
+
}
|
|
9
|
+
export declare class CancelTransaction extends TransactionBuilder<TransactionType.CANCEL> {
|
|
10
|
+
type: TransactionType.CANCEL;
|
|
11
|
+
protected tapLeafScript: TapLeafScript | null;
|
|
12
|
+
protected readonly compiledTargetScript: Buffer;
|
|
13
|
+
protected readonly scriptTree: Taptree;
|
|
14
|
+
protected readonly contractSecret: Buffer;
|
|
15
|
+
protected leftOverFundsScriptRedeem: P2TRPayment | null;
|
|
16
|
+
constructor(parameters: ICancelTransactionParameters);
|
|
17
|
+
protected buildTransaction(): Promise<void>;
|
|
18
|
+
protected generateScriptAddress(): P2TRPayment;
|
|
19
|
+
protected generateTapData(): P2TRPayment;
|
|
20
|
+
protected customFinalizer: (_inputIndex: number, input: PsbtInput) => {
|
|
21
|
+
finalScriptWitness: Buffer<ArrayBufferLike>;
|
|
22
|
+
};
|
|
23
|
+
protected signInputs(transaction: Psbt): Promise<void>;
|
|
24
|
+
protected signInputsWalletBased(transaction: Psbt): Promise<void>;
|
|
25
|
+
protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
|
|
26
|
+
private getMinimalScriptTree;
|
|
27
|
+
private generateLeftoverFundsRedeem;
|
|
28
|
+
}
|
|
@@ -21,6 +21,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
|
|
|
21
21
|
protected readonly scriptSigner: Signer | ECPairInterface;
|
|
22
22
|
protected readonly disableAutoRefund: boolean;
|
|
23
23
|
protected constructor(parameters: SharedInteractionParameters);
|
|
24
|
+
exportCompiledTargetScript(): Buffer;
|
|
24
25
|
getContractSecret(): Buffer;
|
|
25
26
|
getRndBytes(): Buffer;
|
|
26
27
|
getChallenge(): ChallengeSolution;
|
|
@@ -11,11 +11,11 @@ export declare const MINIMUM_AMOUNT_REWARD: bigint;
|
|
|
11
11
|
export declare const MINIMUM_AMOUNT_CA: bigint;
|
|
12
12
|
export declare const ANCHOR_SCRIPT: Buffer<ArrayBuffer>;
|
|
13
13
|
export declare abstract class TransactionBuilder<T extends TransactionType> extends TweakedTransaction {
|
|
14
|
-
static readonly LOCK_LEAF_SCRIPT: Buffer;
|
|
15
14
|
static readonly MINIMUM_DUST: bigint;
|
|
16
15
|
abstract readonly type: T;
|
|
17
16
|
readonly logColor: string;
|
|
18
17
|
debugFees: boolean;
|
|
18
|
+
LOCK_LEAF_SCRIPT: Buffer;
|
|
19
19
|
overflowFees: bigint;
|
|
20
20
|
transactionFee: bigint;
|
|
21
21
|
estimatedFees: bigint;
|
|
@@ -61,7 +61,8 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
|
|
|
61
61
|
getInputs(): PsbtInputExtended[];
|
|
62
62
|
getOutputs(): PsbtOutputExtended[];
|
|
63
63
|
getOptionalOutputValue(): bigint;
|
|
64
|
-
protected addRefundOutput(amountSpent: bigint): Promise<void>;
|
|
64
|
+
protected addRefundOutput(amountSpent: bigint, expectRefund?: boolean): Promise<void>;
|
|
65
|
+
protected defineLockScript(): Buffer;
|
|
65
66
|
protected addValueToToOutput(value: number | bigint): void;
|
|
66
67
|
protected getTransactionOPNetFee(): bigint;
|
|
67
68
|
protected calculateTotalUTXOAmount(): bigint;
|
|
@@ -33,6 +33,7 @@ export interface SharedInteractionParameters extends ITransactionParameters {
|
|
|
33
33
|
readonly challenge: ChallengeSolution;
|
|
34
34
|
readonly randomBytes?: Buffer;
|
|
35
35
|
readonly loadedStorage?: LoadedStorage;
|
|
36
|
+
readonly isCancellation?: boolean;
|
|
36
37
|
}
|
|
37
38
|
export interface IInteractionParameters extends SharedInteractionParameters {
|
|
38
39
|
readonly calldata: Buffer;
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.6.
|
|
1
|
+
export declare const version = "1.6.13";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.6.
|
|
1
|
+
export const version = '1.6.13';
|
package/build/opnet.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export * from './transaction/builders/InteractionTransactionP2WDA.js';
|
|
|
34
34
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
35
35
|
export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
36
36
|
export * from './transaction/builders/TransactionBuilder.js';
|
|
37
|
+
export * from './transaction/builders/CancelTransaction.js';
|
|
37
38
|
export * from './epoch/interfaces/IChallengeSolution.js';
|
|
38
39
|
export * from './epoch/validator/EpochValidator.js';
|
|
39
40
|
export * from './epoch/ChallengeSolution.js';
|
package/build/opnet.js
CHANGED
|
@@ -33,6 +33,7 @@ export * from './transaction/builders/InteractionTransactionP2WDA.js';
|
|
|
33
33
|
export * from './transaction/builders/MultiSignTransaction.js';
|
|
34
34
|
export * from './transaction/builders/SharedInteractionTransaction.js';
|
|
35
35
|
export * from './transaction/builders/TransactionBuilder.js';
|
|
36
|
+
export * from './transaction/builders/CancelTransaction.js';
|
|
36
37
|
export * from './epoch/interfaces/IChallengeSolution.js';
|
|
37
38
|
export * from './epoch/validator/EpochValidator.js';
|
|
38
39
|
export * from './epoch/ChallengeSolution.js';
|
|
@@ -12,7 +12,6 @@ export interface DeploymentResult {
|
|
|
12
12
|
readonly contractAddress: string;
|
|
13
13
|
readonly contractPubKey: string;
|
|
14
14
|
readonly challenge: RawChallenge;
|
|
15
|
-
readonly rawTransaction: Transaction;
|
|
16
15
|
readonly utxos: UTXO[];
|
|
17
16
|
}
|
|
18
17
|
export interface FundingTransactionResponse {
|
|
@@ -31,8 +30,10 @@ export interface InteractionResponse {
|
|
|
31
30
|
readonly interactionTransaction: string;
|
|
32
31
|
readonly estimatedFees: bigint;
|
|
33
32
|
readonly nextUTXOs: UTXO[];
|
|
33
|
+
readonly fundingUTXOs: UTXO[];
|
|
34
34
|
readonly challenge: RawChallenge;
|
|
35
|
-
readonly
|
|
35
|
+
readonly interactionAddress: string | null;
|
|
36
|
+
readonly compiledTargetScript: string | null;
|
|
36
37
|
}
|
|
37
38
|
export interface BitcoinTransferResponse extends BitcoinTransferBase {
|
|
38
39
|
readonly original: FundingTransaction;
|
|
@@ -126,9 +126,10 @@ export class TransactionFactory {
|
|
|
126
126
|
if (!signedTransaction) {
|
|
127
127
|
throw new Error('Could not sign funding transaction.');
|
|
128
128
|
}
|
|
129
|
+
const fundingUTXO = this.getUTXOAsTransaction(signedTransaction.tx, finalTransaction.getScriptAddress(), 0);
|
|
129
130
|
const newParams = {
|
|
130
131
|
...interactionParameters,
|
|
131
|
-
utxos:
|
|
132
|
+
utxos: fundingUTXO,
|
|
132
133
|
randomBytes: finalTransaction.getRndBytes(),
|
|
133
134
|
challenge: challenge,
|
|
134
135
|
nonWitnessUtxo: signedTransaction.tx.toBuffer(),
|
|
@@ -138,12 +139,14 @@ export class TransactionFactory {
|
|
|
138
139
|
const interactionTx = new InteractionTransaction(newParams);
|
|
139
140
|
const outTx = await interactionTx.signTransaction();
|
|
140
141
|
return {
|
|
141
|
-
|
|
142
|
+
interactionAddress: finalTransaction.getScriptAddress(),
|
|
142
143
|
fundingTransaction: signedTransaction.tx.toHex(),
|
|
143
144
|
interactionTransaction: outTx.toHex(),
|
|
144
145
|
estimatedFees: interactionTx.transactionFee,
|
|
145
146
|
nextUTXOs: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.from, 1),
|
|
146
147
|
challenge: challenge.toRaw(),
|
|
148
|
+
fundingUTXOs: fundingUTXO,
|
|
149
|
+
compiledTargetScript: interactionTx.exportCompiledTargetScript().toString('hex'),
|
|
147
150
|
};
|
|
148
151
|
}
|
|
149
152
|
async signDeployment(deploymentParameters) {
|
|
@@ -217,7 +220,6 @@ export class TransactionFactory {
|
|
|
217
220
|
value: BigInt(out2.value),
|
|
218
221
|
};
|
|
219
222
|
return {
|
|
220
|
-
rawTransaction: outTx,
|
|
221
223
|
transaction: [signedTransaction.toHex(), outTx.toHex()],
|
|
222
224
|
contractAddress: deploymentTx.getContractAddress(),
|
|
223
225
|
contractPubKey: deploymentTx.contractPubKey,
|
|
@@ -343,12 +345,14 @@ export class TransactionFactory {
|
|
|
343
345
|
const signedTx = await p2wdaTransaction.signTransaction();
|
|
344
346
|
const txHex = signedTx.toHex();
|
|
345
347
|
return {
|
|
346
|
-
|
|
348
|
+
interactionAddress: null,
|
|
347
349
|
fundingTransaction: null,
|
|
348
350
|
interactionTransaction: txHex,
|
|
349
351
|
estimatedFees: p2wdaTransaction.estimatedFees,
|
|
350
352
|
nextUTXOs: this.getUTXOAsTransaction(signedTx, interactionParameters.from, signedTx.outs.length - 1),
|
|
353
|
+
fundingUTXOs: [...interactionParameters.utxos, ...inputs],
|
|
351
354
|
challenge: interactionParameters.challenge.toRaw(),
|
|
355
|
+
compiledTargetScript: null,
|
|
352
356
|
};
|
|
353
357
|
}
|
|
354
358
|
getPriorityFee(params) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
2
|
+
import { P2TRPayment, Psbt, PsbtInput, Taptree } from '@btc-vision/bitcoin';
|
|
3
|
+
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
4
|
+
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
5
|
+
import { ITransactionParameters } from '../interfaces/ITransactionParameters.js';
|
|
6
|
+
export interface ICancelTransactionParameters extends Omit<ITransactionParameters, 'priorityFee' | 'gasSatFee'> {
|
|
7
|
+
readonly compiledTargetScript: string | Buffer;
|
|
8
|
+
}
|
|
9
|
+
export declare class CancelTransaction extends TransactionBuilder<TransactionType.CANCEL> {
|
|
10
|
+
type: TransactionType.CANCEL;
|
|
11
|
+
protected tapLeafScript: TapLeafScript | null;
|
|
12
|
+
protected readonly compiledTargetScript: Buffer;
|
|
13
|
+
protected readonly scriptTree: Taptree;
|
|
14
|
+
protected readonly contractSecret: Buffer;
|
|
15
|
+
protected leftOverFundsScriptRedeem: P2TRPayment | null;
|
|
16
|
+
constructor(parameters: ICancelTransactionParameters);
|
|
17
|
+
protected buildTransaction(): Promise<void>;
|
|
18
|
+
protected generateScriptAddress(): P2TRPayment;
|
|
19
|
+
protected generateTapData(): P2TRPayment;
|
|
20
|
+
protected customFinalizer: (_inputIndex: number, input: PsbtInput) => {
|
|
21
|
+
finalScriptWitness: Buffer<ArrayBufferLike>;
|
|
22
|
+
};
|
|
23
|
+
protected signInputs(transaction: Psbt): Promise<void>;
|
|
24
|
+
protected signInputsWalletBased(transaction: Psbt): Promise<void>;
|
|
25
|
+
protected signInputsNonWalletBased(transaction: Psbt): Promise<void>;
|
|
26
|
+
private getMinimalScriptTree;
|
|
27
|
+
private generateLeftoverFundsRedeem;
|
|
28
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
2
|
+
import { PaymentType } from '@btc-vision/bitcoin';
|
|
3
|
+
import { TransactionBuilder } from './TransactionBuilder.js';
|
|
4
|
+
export class CancelTransaction extends TransactionBuilder {
|
|
5
|
+
constructor(parameters) {
|
|
6
|
+
super({
|
|
7
|
+
...parameters,
|
|
8
|
+
gasSatFee: 1n,
|
|
9
|
+
isCancellation: true,
|
|
10
|
+
priorityFee: 1n,
|
|
11
|
+
calldata: Buffer.alloc(0),
|
|
12
|
+
});
|
|
13
|
+
this.type = TransactionType.CANCEL;
|
|
14
|
+
this.tapLeafScript = null;
|
|
15
|
+
this.leftOverFundsScriptRedeem = null;
|
|
16
|
+
this.customFinalizer = (_inputIndex, input) => {
|
|
17
|
+
if (!this.tapLeafScript) {
|
|
18
|
+
throw new Error('Tap leaf script is required');
|
|
19
|
+
}
|
|
20
|
+
if (!input.tapScriptSig || input.tapScriptSig.length === 0) {
|
|
21
|
+
throw new Error('Tap script signature is required');
|
|
22
|
+
}
|
|
23
|
+
const scriptSolution = [input.tapScriptSig[0].signature];
|
|
24
|
+
const witness = scriptSolution
|
|
25
|
+
.concat(this.tapLeafScript.script)
|
|
26
|
+
.concat(this.tapLeafScript.controlBlock);
|
|
27
|
+
return {
|
|
28
|
+
finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witness),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
this.contractSecret = Buffer.alloc(0);
|
|
32
|
+
if (Buffer.isBuffer(parameters.compiledTargetScript)) {
|
|
33
|
+
this.compiledTargetScript = parameters.compiledTargetScript;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.compiledTargetScript = Buffer.from(parameters.compiledTargetScript, 'hex');
|
|
37
|
+
}
|
|
38
|
+
this.scriptTree = this.getMinimalScriptTree();
|
|
39
|
+
this.internalInit();
|
|
40
|
+
}
|
|
41
|
+
async buildTransaction() {
|
|
42
|
+
if (!this.from) {
|
|
43
|
+
throw new Error('From address is required');
|
|
44
|
+
}
|
|
45
|
+
if (!this.leftOverFundsScriptRedeem) {
|
|
46
|
+
throw new Error('Left over funds script redeem is required');
|
|
47
|
+
}
|
|
48
|
+
if (!this.leftOverFundsScriptRedeem.redeemVersion) {
|
|
49
|
+
throw new Error('Left over funds script redeem version is required');
|
|
50
|
+
}
|
|
51
|
+
if (!this.leftOverFundsScriptRedeem.output) {
|
|
52
|
+
throw new Error('Left over funds script redeem output is required');
|
|
53
|
+
}
|
|
54
|
+
this.tapLeafScript = {
|
|
55
|
+
leafVersion: this.leftOverFundsScriptRedeem.redeemVersion,
|
|
56
|
+
script: this.leftOverFundsScriptRedeem.output,
|
|
57
|
+
controlBlock: this.getWitness(),
|
|
58
|
+
};
|
|
59
|
+
this.addInputsFromUTXO();
|
|
60
|
+
await this.addRefundOutput(0n, true);
|
|
61
|
+
if (!this.feeOutput) {
|
|
62
|
+
throw new Error('Must add extra UTXOs to cancel this transaction');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
generateScriptAddress() {
|
|
66
|
+
return {
|
|
67
|
+
internalPubkey: this.internalPubKeyToXOnly(),
|
|
68
|
+
network: this.network,
|
|
69
|
+
scriptTree: this.scriptTree,
|
|
70
|
+
name: PaymentType.P2TR,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
generateTapData() {
|
|
74
|
+
const selectedRedeem = this.leftOverFundsScriptRedeem;
|
|
75
|
+
if (!selectedRedeem) {
|
|
76
|
+
throw new Error('Left over funds script redeem is required');
|
|
77
|
+
}
|
|
78
|
+
if (!this.scriptTree) {
|
|
79
|
+
throw new Error('Script tree is required');
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
internalPubkey: this.internalPubKeyToXOnly(),
|
|
83
|
+
network: this.network,
|
|
84
|
+
scriptTree: this.scriptTree,
|
|
85
|
+
redeem: selectedRedeem,
|
|
86
|
+
name: PaymentType.P2TR,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async signInputs(transaction) {
|
|
90
|
+
if ('multiSignPsbt' in this.signer) {
|
|
91
|
+
await this.signInputsWalletBased(transaction);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
await this.signInputsNonWalletBased(transaction);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async signInputsWalletBased(transaction) {
|
|
98
|
+
const signer = this.signer;
|
|
99
|
+
await signer.multiSignPsbt([transaction]);
|
|
100
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
101
|
+
if (i === 0) {
|
|
102
|
+
transaction.finalizeInput(i, this.customFinalizer.bind(this));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
try {
|
|
106
|
+
transaction.finalizeInput(i, this.customFinalizerP2SH.bind(this));
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
transaction.finalizeInput(i);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async signInputsNonWalletBased(transaction) {
|
|
115
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
116
|
+
if (i === 0) {
|
|
117
|
+
await this.signInput(transaction, transaction.data.inputs[i], i, this.getSignerKey());
|
|
118
|
+
transaction.finalizeInput(0, this.customFinalizer.bind(this));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
await this.signInput(transaction, transaction.data.inputs[i], i, this.signer);
|
|
122
|
+
try {
|
|
123
|
+
transaction.finalizeInput(i, this.customFinalizerP2SH.bind(this));
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
transaction.finalizeInput(i);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
getMinimalScriptTree() {
|
|
132
|
+
this.generateLeftoverFundsRedeem();
|
|
133
|
+
if (!this.leftOverFundsScriptRedeem || !this.leftOverFundsScriptRedeem.output) {
|
|
134
|
+
throw new Error('Failed to generate leftover funds redeem script');
|
|
135
|
+
}
|
|
136
|
+
return [
|
|
137
|
+
{
|
|
138
|
+
output: this.compiledTargetScript,
|
|
139
|
+
version: 192,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
output: this.leftOverFundsScriptRedeem.output,
|
|
143
|
+
version: 192,
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
generateLeftoverFundsRedeem() {
|
|
148
|
+
this.leftOverFundsScriptRedeem = {
|
|
149
|
+
name: PaymentType.P2TR,
|
|
150
|
+
output: this.LOCK_LEAF_SCRIPT,
|
|
151
|
+
redeemVersion: 192,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -36,6 +36,7 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
36
36
|
throw new Error('Witness(es) are required');
|
|
37
37
|
this.witnesses = parameters.witnesses;
|
|
38
38
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
39
|
+
this.LOCK_LEAF_SCRIPT = this.defineLockScript();
|
|
39
40
|
this.scriptSeed = this.getContractSeed();
|
|
40
41
|
this.contractSigner = EcKeyPair.fromSeedKeyPair(this.scriptSeed, this.network);
|
|
41
42
|
this.generator = new CustomGenerator(this.internalPubKeyToXOnly(), this.network);
|
|
@@ -166,7 +167,7 @@ export class CustomScriptTransaction extends TransactionBuilder {
|
|
|
166
167
|
};
|
|
167
168
|
}
|
|
168
169
|
getLeafScript() {
|
|
169
|
-
return
|
|
170
|
+
return this.LOCK_LEAF_SCRIPT;
|
|
170
171
|
}
|
|
171
172
|
getScriptTree() {
|
|
172
173
|
this.generateRedeemScripts();
|
|
@@ -46,6 +46,7 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
46
46
|
throw new Error('Challenge solution is required');
|
|
47
47
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
48
48
|
this.challenge = parameters.challenge;
|
|
49
|
+
this.LOCK_LEAF_SCRIPT = this.defineLockScript();
|
|
49
50
|
this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(this.challenge.publicKey.originalPublicKeyBuffer(), this.network);
|
|
50
51
|
this.contractSeed = this.getContractSeed();
|
|
51
52
|
this.contractSigner = EcKeyPair.fromSeedKeyPair(this.contractSeed, this.network);
|
|
@@ -231,7 +232,7 @@ export class DeploymentTransaction extends TransactionBuilder {
|
|
|
231
232
|
};
|
|
232
233
|
}
|
|
233
234
|
getLeafScript() {
|
|
234
|
-
return
|
|
235
|
+
return this.LOCK_LEAF_SCRIPT;
|
|
235
236
|
}
|
|
236
237
|
getScriptTree() {
|
|
237
238
|
if (!this.bytecode) {
|
|
@@ -21,6 +21,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
|
|
|
21
21
|
protected readonly scriptSigner: Signer | ECPairInterface;
|
|
22
22
|
protected readonly disableAutoRefund: boolean;
|
|
23
23
|
protected constructor(parameters: SharedInteractionParameters);
|
|
24
|
+
exportCompiledTargetScript(): Buffer;
|
|
24
25
|
getContractSecret(): Buffer;
|
|
25
26
|
getRndBytes(): Buffer;
|
|
26
27
|
getChallenge(): ChallengeSolution;
|
|
@@ -32,6 +32,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
32
32
|
throw new Error('Challenge solution is required');
|
|
33
33
|
}
|
|
34
34
|
this.challenge = parameters.challenge;
|
|
35
|
+
this.LOCK_LEAF_SCRIPT = this.defineLockScript();
|
|
35
36
|
this.disableAutoRefund = parameters.disableAutoRefund || false;
|
|
36
37
|
this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(this.challenge.publicKey.originalPublicKeyBuffer(), this.network);
|
|
37
38
|
this.calldata = Compressor.compress(parameters.calldata);
|
|
@@ -39,6 +40,9 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
39
40
|
this.scriptSigner = this.generateKeyPairFromSeed();
|
|
40
41
|
this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
|
|
41
42
|
}
|
|
43
|
+
exportCompiledTargetScript() {
|
|
44
|
+
return this.compiledTargetScript;
|
|
45
|
+
}
|
|
42
46
|
getContractSecret() {
|
|
43
47
|
return this.contractSecret;
|
|
44
48
|
}
|
|
@@ -136,7 +140,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
136
140
|
version: 192,
|
|
137
141
|
},
|
|
138
142
|
{
|
|
139
|
-
output:
|
|
143
|
+
output: this.LOCK_LEAF_SCRIPT,
|
|
140
144
|
version: 192,
|
|
141
145
|
},
|
|
142
146
|
];
|
|
@@ -203,7 +207,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
203
207
|
};
|
|
204
208
|
this.leftOverFundsScriptRedeem = {
|
|
205
209
|
name: PaymentType.P2TR,
|
|
206
|
-
output:
|
|
210
|
+
output: this.LOCK_LEAF_SCRIPT,
|
|
207
211
|
redeemVersion: 192,
|
|
208
212
|
};
|
|
209
213
|
}
|
|
@@ -11,11 +11,11 @@ export declare const MINIMUM_AMOUNT_REWARD: bigint;
|
|
|
11
11
|
export declare const MINIMUM_AMOUNT_CA: bigint;
|
|
12
12
|
export declare const ANCHOR_SCRIPT: Buffer<ArrayBuffer>;
|
|
13
13
|
export declare abstract class TransactionBuilder<T extends TransactionType> extends TweakedTransaction {
|
|
14
|
-
static readonly LOCK_LEAF_SCRIPT: Buffer;
|
|
15
14
|
static readonly MINIMUM_DUST: bigint;
|
|
16
15
|
abstract readonly type: T;
|
|
17
16
|
readonly logColor: string;
|
|
18
17
|
debugFees: boolean;
|
|
18
|
+
LOCK_LEAF_SCRIPT: Buffer;
|
|
19
19
|
overflowFees: bigint;
|
|
20
20
|
transactionFee: bigint;
|
|
21
21
|
estimatedFees: bigint;
|
|
@@ -61,7 +61,8 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
|
|
|
61
61
|
getInputs(): PsbtInputExtended[];
|
|
62
62
|
getOutputs(): PsbtOutputExtended[];
|
|
63
63
|
getOptionalOutputValue(): bigint;
|
|
64
|
-
protected addRefundOutput(amountSpent: bigint): Promise<void>;
|
|
64
|
+
protected addRefundOutput(amountSpent: bigint, expectRefund?: boolean): Promise<void>;
|
|
65
|
+
protected defineLockScript(): Buffer;
|
|
65
66
|
protected addValueToToOutput(value: number | bigint): void;
|
|
66
67
|
protected getTransactionOPNetFee(): bigint;
|
|
67
68
|
protected calculateTotalUTXOAmount(): bigint;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import bitcoin, { getFinalScripts, initEccLib, opcodes, Psbt, script, Transaction, varuint, } from '@btc-vision/bitcoin';
|
|
1
|
+
import bitcoin, { getFinalScripts, initEccLib, opcodes, Psbt, script, toXOnly, Transaction, varuint, } from '@btc-vision/bitcoin';
|
|
2
2
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
3
3
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
4
4
|
import { AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
@@ -33,6 +33,7 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
33
33
|
this.optionalInputs = parameters.optionalInputs || [];
|
|
34
34
|
this.to = parameters.to || undefined;
|
|
35
35
|
this.debugFees = parameters.debugFees || false;
|
|
36
|
+
this.LOCK_LEAF_SCRIPT = this.defineLockScript();
|
|
36
37
|
if (parameters.note) {
|
|
37
38
|
if (typeof parameters.note === 'string') {
|
|
38
39
|
this.note = Buffer.from(parameters.note, 'utf8');
|
|
@@ -402,7 +403,7 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
402
403
|
}
|
|
403
404
|
return total;
|
|
404
405
|
}
|
|
405
|
-
async addRefundOutput(amountSpent) {
|
|
406
|
+
async addRefundOutput(amountSpent, expectRefund = false) {
|
|
406
407
|
if (this.note) {
|
|
407
408
|
this.addOPReturn(this.note);
|
|
408
409
|
}
|
|
@@ -413,11 +414,12 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
413
414
|
let estimatedFee = 0n;
|
|
414
415
|
let iterations = 0;
|
|
415
416
|
const maxIterations = 5;
|
|
417
|
+
let sendBackAmount = 0n;
|
|
416
418
|
while (iterations < maxIterations && estimatedFee !== previousFee) {
|
|
417
419
|
previousFee = estimatedFee;
|
|
418
420
|
estimatedFee = await this.estimateTransactionFees();
|
|
419
421
|
const totalSpent = amountSpent + estimatedFee;
|
|
420
|
-
|
|
422
|
+
sendBackAmount = this.totalInputAmount - totalSpent;
|
|
421
423
|
if (this.debugFees) {
|
|
422
424
|
this.log(`Iteration ${iterations + 1}: inputAmount=${this.totalInputAmount}, totalSpent=${totalSpent}, sendBackAmount=${sendBackAmount}`);
|
|
423
425
|
}
|
|
@@ -450,7 +452,7 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
450
452
|
else {
|
|
451
453
|
this.feeOutput = null;
|
|
452
454
|
this.overflowFees = 0n;
|
|
453
|
-
if (sendBackAmount < 0n) {
|
|
455
|
+
if (sendBackAmount < 0n && iterations === maxIterations) {
|
|
454
456
|
throw new Error(`Insufficient funds: need ${totalSpent} sats but only have ${this.totalInputAmount} sats`);
|
|
455
457
|
}
|
|
456
458
|
if (this.debugFees) {
|
|
@@ -459,6 +461,9 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
459
461
|
}
|
|
460
462
|
iterations++;
|
|
461
463
|
}
|
|
464
|
+
if (expectRefund && sendBackAmount < 0n) {
|
|
465
|
+
throw new Error(`Insufficient funds: need at least ${-sendBackAmount} more sats to cover fees.`);
|
|
466
|
+
}
|
|
462
467
|
if (iterations >= maxIterations) {
|
|
463
468
|
this.warn(`Fee calculation did not stabilize after ${maxIterations} iterations`);
|
|
464
469
|
}
|
|
@@ -467,6 +472,9 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
467
472
|
this.log(`Final fee: ${estimatedFee} sats, Change output: ${this.feeOutput ? `${this.feeOutput.value} sats` : 'none'}`);
|
|
468
473
|
}
|
|
469
474
|
}
|
|
475
|
+
defineLockScript() {
|
|
476
|
+
return script.compile([toXOnly(Buffer.from(this.signer.publicKey)), opcodes.OP_CHECKSIG]);
|
|
477
|
+
}
|
|
470
478
|
addValueToToOutput(value) {
|
|
471
479
|
if (value < TransactionBuilder.MINIMUM_DUST) {
|
|
472
480
|
throw new Error(`Value to send is less than the minimum dust ${value} < ${TransactionBuilder.MINIMUM_DUST}`);
|
|
@@ -667,8 +675,4 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
667
675
|
return false;
|
|
668
676
|
}
|
|
669
677
|
}
|
|
670
|
-
TransactionBuilder.LOCK_LEAF_SCRIPT = script.compile([
|
|
671
|
-
opcodes.OP_FALSE,
|
|
672
|
-
opcodes.OP_VERIFY,
|
|
673
|
-
]);
|
|
674
678
|
TransactionBuilder.MINIMUM_DUST = 330n;
|
|
@@ -6,5 +6,5 @@ export var TransactionType;
|
|
|
6
6
|
TransactionType[TransactionType["INTERACTION"] = 3] = "INTERACTION";
|
|
7
7
|
TransactionType[TransactionType["MULTI_SIG"] = 4] = "MULTI_SIG";
|
|
8
8
|
TransactionType[TransactionType["CUSTOM_CODE"] = 5] = "CUSTOM_CODE";
|
|
9
|
-
TransactionType[TransactionType["
|
|
9
|
+
TransactionType[TransactionType["CANCEL"] = 6] = "CANCEL";
|
|
10
10
|
})(TransactionType || (TransactionType = {}));
|
|
@@ -33,6 +33,7 @@ export interface SharedInteractionParameters extends ITransactionParameters {
|
|
|
33
33
|
readonly challenge: ChallengeSolution;
|
|
34
34
|
readonly randomBytes?: Buffer;
|
|
35
35
|
readonly loadedStorage?: LoadedStorage;
|
|
36
|
+
readonly isCancellation?: boolean;
|
|
36
37
|
}
|
|
37
38
|
export interface IInteractionParameters extends SharedInteractionParameters {
|
|
38
39
|
readonly calldata: Buffer;
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { crypto as bitCrypto, networks, payments, toXOnly, } from '@btc-vision/bitcoin';
|
|
1
|
+
import { crypto as bitCrypto, networks, opcodes, payments, script, toXOnly, } from '@btc-vision/bitcoin';
|
|
2
2
|
import { DeploymentGenerator } from '../generators/builders/DeploymentGenerator.js';
|
|
3
|
-
import { TransactionBuilder } from '../transaction/builders/TransactionBuilder.js';
|
|
4
3
|
export class TapscriptVerificator {
|
|
5
4
|
static getContractAddress(params) {
|
|
6
5
|
const network = params.network || networks.bitcoin;
|
|
7
6
|
const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
|
|
8
7
|
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features);
|
|
8
|
+
const lockLeafScript = script.compile([
|
|
9
|
+
toXOnly(params.deployerPubKey),
|
|
10
|
+
opcodes.OP_CHECKSIG,
|
|
11
|
+
]);
|
|
9
12
|
const scriptTree = [
|
|
10
13
|
{
|
|
11
14
|
output: compiledTargetScript,
|
|
12
15
|
version: TapscriptVerificator.TAP_SCRIPT_VERSION,
|
|
13
16
|
},
|
|
14
17
|
{
|
|
15
|
-
output:
|
|
18
|
+
output: lockLeafScript,
|
|
16
19
|
version: TapscriptVerificator.TAP_SCRIPT_VERSION,
|
|
17
20
|
},
|
|
18
21
|
];
|
|
@@ -22,13 +25,17 @@ export class TapscriptVerificator {
|
|
|
22
25
|
const network = params.network || networks.bitcoin;
|
|
23
26
|
const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
|
|
24
27
|
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features);
|
|
28
|
+
const lockLeafScript = script.compile([
|
|
29
|
+
toXOnly(params.deployerPubKey),
|
|
30
|
+
opcodes.OP_CHECKSIG,
|
|
31
|
+
]);
|
|
25
32
|
const scriptTree = [
|
|
26
33
|
{
|
|
27
34
|
output: compiledTargetScript,
|
|
28
35
|
version: TapscriptVerificator.TAP_SCRIPT_VERSION,
|
|
29
36
|
},
|
|
30
37
|
{
|
|
31
|
-
output:
|
|
38
|
+
output: lockLeafScript,
|
|
32
39
|
version: TapscriptVerificator.TAP_SCRIPT_VERSION,
|
|
33
40
|
},
|
|
34
41
|
];
|
package/package.json
CHANGED
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.6.
|
|
1
|
+
export const version = '1.6.13';
|