@btc-vision/transaction 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/keypair/Wallet.d.ts +1 -0
- package/browser/opnet.d.ts +4 -0
- package/browser/transaction/TransactionFactory.d.ts +7 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +2 -2
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +0 -3
- package/browser/transaction/builders/TransactionBuilder.d.ts +5 -30
- package/browser/transaction/builders/UnwarpTransaction.d.ts +19 -0
- package/browser/transaction/enums/TransactionType.d.ts +2 -1
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +7 -5
- package/browser/transaction/processor/PsbtTransaction.d.ts +43 -0
- package/browser/transaction/psbt/PSBTTypes.d.ts +3 -0
- package/browser/transaction/shared/TweakedTransaction.d.ts +48 -0
- package/browser/utxo/OPNetLimitedProvider.d.ts +3 -0
- package/browser/utxo/interfaces/BroadcastResponse.d.ts +7 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/keypair/Wallet.d.ts +1 -0
- package/build/keypair/Wallet.js +3 -0
- package/build/metadata/contracts/wBTC.js +1 -1
- package/build/opnet.d.ts +4 -0
- package/build/opnet.js +4 -0
- package/build/tests/adaptPSBT.d.ts +1 -0
- package/build/tests/adaptPSBT.js +59 -0
- package/build/tests/massWrapReg.d.ts +1 -0
- package/build/tests/massWrapReg.js +86 -0
- package/build/tests/unwrapReg.d.ts +1 -0
- package/build/tests/unwrapReg.js +46 -0
- package/build/transaction/TransactionFactory.d.ts +7 -1
- package/build/transaction/TransactionFactory.js +49 -0
- package/build/transaction/builders/DeploymentTransaction.d.ts +2 -2
- package/build/transaction/builders/DeploymentTransaction.js +1 -18
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +0 -3
- package/build/transaction/builders/SharedInteractionTransaction.js +0 -22
- package/build/transaction/builders/TransactionBuilder.d.ts +5 -30
- package/build/transaction/builders/TransactionBuilder.js +15 -123
- package/build/transaction/builders/UnwarpTransaction.d.ts +19 -0
- package/build/transaction/builders/UnwarpTransaction.js +43 -0
- package/build/transaction/enums/TransactionType.d.ts +2 -1
- package/build/transaction/enums/TransactionType.js +1 -0
- package/build/transaction/interfaces/ITransactionParameters.d.ts +7 -5
- package/build/transaction/processor/PsbtTransaction.d.ts +43 -0
- package/build/transaction/processor/PsbtTransaction.js +131 -0
- package/build/transaction/psbt/PSBTTypes.d.ts +3 -0
- package/build/transaction/psbt/PSBTTypes.js +4 -0
- package/build/transaction/shared/TweakedTransaction.d.ts +48 -0
- package/build/transaction/shared/TweakedTransaction.js +150 -0
- package/build/utxo/OPNetLimitedProvider.d.ts +3 -0
- package/build/utxo/OPNetLimitedProvider.js +21 -5
- package/build/utxo/interfaces/BroadcastResponse.d.ts +7 -0
- package/build/utxo/interfaces/BroadcastResponse.js +1 -0
- package/build/wbtc/BroadcastResponse.d.ts +0 -0
- package/build/wbtc/BroadcastResponse.js +1 -0
- package/bytecode/wbtc.wasm +0 -0
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/AddressGenerator.html +2 -2
- package/docs/classes/AddressVerificator.html +2 -2
- package/docs/classes/BitcoinUtils.html +4 -4
- package/docs/classes/CalldataGenerator.html +10 -10
- package/docs/classes/Compressor.html +4 -4
- package/docs/classes/ContractBaseMetadata.html +4 -4
- package/docs/classes/DeploymentGenerator.html +9 -9
- package/docs/classes/DeploymentTransaction.html +100 -81
- package/docs/classes/EcKeyPair.html +16 -16
- package/docs/classes/FundingTransaction.html +79 -58
- package/docs/classes/Generator.html +9 -9
- package/docs/classes/InteractionTransaction.html +89 -70
- package/docs/classes/OPNetLimitedProvider.html +15 -6
- package/docs/classes/PsbtTransaction.html +305 -0
- package/docs/classes/SharedInteractionTransaction.html +95 -76
- package/docs/classes/TapscriptVerificator.html +2 -2
- package/docs/classes/TransactionBuilder.html +102 -82
- package/docs/classes/TransactionFactory.html +7 -5
- package/docs/classes/TweakedSigner.html +2 -2
- package/docs/classes/TweakedTransaction.html +275 -0
- package/docs/classes/Wallet.html +15 -10
- package/docs/classes/WrapTransaction.html +102 -84
- package/docs/classes/WrappedGeneration.html +8 -8
- package/docs/classes/wBTC.html +7 -7
- package/docs/enums/PSBTTypes.html +175 -0
- package/docs/enums/TransactionSequence.html +2 -2
- package/docs/enums/TransactionType.html +3 -2
- package/docs/hierarchy.html +1 -1
- package/docs/interfaces/BroadcastResponse.html +179 -0
- package/docs/interfaces/ContractAddressVerificationParams.html +2 -2
- package/docs/interfaces/DeploymentResult.html +2 -2
- package/docs/interfaces/FetchUTXOParams.html +2 -2
- package/docs/interfaces/FetchUTXOParamsMultiAddress.html +2 -2
- package/docs/interfaces/GenerationConstraints.html +5 -5
- package/docs/interfaces/IDeploymentParameters.html +2 -2
- package/docs/interfaces/IFundingTransactionParameters.html +2 -2
- package/docs/interfaces/IInteractionParameters.html +2 -2
- package/docs/interfaces/ITransactionParameters.html +4 -4
- package/docs/interfaces/ITweakedTransactionData.html +177 -0
- package/docs/interfaces/IUnwrapParameters.html +186 -0
- package/docs/interfaces/IWBTCUTXODocument.html +180 -0
- package/docs/interfaces/IWallet.html +4 -4
- package/docs/interfaces/IWrapParameters.html +2 -2
- package/docs/interfaces/NetworkInformation.html +2 -2
- package/docs/interfaces/PsbtInputExtended.html +1 -1
- package/docs/interfaces/PsbtOutputExtendedAddress.html +2 -2
- package/docs/interfaces/PsbtOutputExtendedScript.html +2 -2
- package/docs/interfaces/PsbtTransactionData.html +179 -0
- package/docs/interfaces/RawUTXOResponse.html +2 -2
- package/docs/interfaces/SharedInteractionParameters.html +2 -2
- package/docs/interfaces/TapLeafScript.html +2 -2
- package/docs/interfaces/TweakSettings.html +3 -3
- package/docs/interfaces/UTXO.html +2 -2
- package/docs/interfaces/UnwrapResult.html +176 -0
- package/docs/interfaces/UpdateInput.html +2 -2
- package/docs/interfaces/VaultUTXOs.html +178 -0
- package/docs/interfaces/WrapResult.html +2 -2
- package/docs/interfaces/WrappedGenerationParameters.html +6 -6
- package/docs/modules.html +14 -3
- package/docs/types/FromBase64Params.html +174 -0
- package/docs/types/PsbtOutputExtended.html +1 -1
- package/docs/variables/version.html +1 -1
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/generators/builders/CalldataGenerator.ts +1 -1
- package/src/keypair/Wallet.ts +10 -0
- package/src/metadata/contracts/wBTC.ts +1 -1
- package/src/opnet.ts +8 -0
- package/src/tests/adaptPSBT.ts +72 -0
- package/src/tests/massWrapReg.ts +111 -0
- package/src/tests/unwrapReg.ts +89 -0
- package/src/transaction/TransactionFactory.ts +77 -1
- package/src/transaction/builders/DeploymentTransaction.ts +6 -35
- package/src/transaction/builders/SharedInteractionTransaction.ts +2 -35
- package/src/transaction/builders/TransactionBuilder.ts +30 -246
- package/src/transaction/builders/UnwarpTransaction.ts +108 -0
- package/src/transaction/enums/TransactionType.ts +1 -0
- package/src/transaction/interfaces/ITransactionParameters.ts +10 -6
- package/src/transaction/processor/PsbtTransaction.ts +254 -0
- package/src/transaction/psbt/PSBTTypes.ts +3 -0
- package/src/transaction/shared/TweakedTransaction.ts +342 -0
- package/src/utxo/OPNetLimitedProvider.ts +47 -9
- package/src/utxo/interfaces/BroadcastResponse.ts +8 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Network, payments, Psbt, Signer, Transaction } from 'bitcoinjs-lib';
|
|
2
|
+
import { ITweakedTransactionData, TweakedTransaction } from '../shared/TweakedTransaction.js';
|
|
3
|
+
import { PsbtInputExtended, PsbtOutputExtended } from '../interfaces/Tap.js';
|
|
4
|
+
import { Address } from '@btc-vision/bsi-binary';
|
|
5
|
+
|
|
6
|
+
export interface PsbtTransactionData extends ITweakedTransactionData {
|
|
7
|
+
readonly psbt: Psbt;
|
|
8
|
+
readonly signer: Signer;
|
|
9
|
+
readonly network: Network;
|
|
10
|
+
readonly amountRequested: bigint;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IWBTCUTXODocument {
|
|
14
|
+
readonly vault: Address;
|
|
15
|
+
readonly blockId: bigint;
|
|
16
|
+
|
|
17
|
+
readonly hash: string;
|
|
18
|
+
readonly value: bigint;
|
|
19
|
+
readonly outputIndex: number;
|
|
20
|
+
|
|
21
|
+
readonly output: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface VaultUTXOs {
|
|
25
|
+
readonly vault: Address;
|
|
26
|
+
readonly publicKeys: Address[];
|
|
27
|
+
readonly minimum: number;
|
|
28
|
+
readonly utxos: IWBTCUTXODocument[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type FromBase64Params = Omit<PsbtTransactionData, 'psbt'>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @description PSBT Transaction processor.
|
|
35
|
+
* */
|
|
36
|
+
export class PsbtTransaction extends TweakedTransaction {
|
|
37
|
+
public readonly logColor: string = '#00ffe1';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description The transaction
|
|
41
|
+
* @protected
|
|
42
|
+
*/
|
|
43
|
+
protected readonly transaction: Psbt;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @description Sign hash types
|
|
47
|
+
* @protected
|
|
48
|
+
*/
|
|
49
|
+
protected readonly sighashTypes: number[] = [
|
|
50
|
+
Transaction.SIGHASH_ALL,
|
|
51
|
+
Transaction.SIGHASH_ANYONECANPAY,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
protected readonly amountRequested: bigint;
|
|
55
|
+
|
|
56
|
+
constructor(data: PsbtTransactionData) {
|
|
57
|
+
super(data);
|
|
58
|
+
|
|
59
|
+
if (!data.amountRequested) {
|
|
60
|
+
throw new Error('Amount requested is required');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.signer = data.signer;
|
|
64
|
+
this.network = data.network;
|
|
65
|
+
this.amountRequested = data.amountRequested;
|
|
66
|
+
|
|
67
|
+
this.transaction = data.psbt;
|
|
68
|
+
|
|
69
|
+
this.tweakSigner();
|
|
70
|
+
this.internalInit();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static fromBase64(data: string, params: FromBase64Params): PsbtTransaction {
|
|
74
|
+
const psbt = Psbt.fromBase64(data, {
|
|
75
|
+
network: params.network,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return new PsbtTransaction({
|
|
79
|
+
...params,
|
|
80
|
+
psbt,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @description Add an input to the transaction
|
|
86
|
+
* @param input
|
|
87
|
+
*/
|
|
88
|
+
public addInput(input: PsbtInputExtended): void {
|
|
89
|
+
this.transaction.addInput(input);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @description Add an output to the transaction
|
|
94
|
+
* @param output
|
|
95
|
+
*/
|
|
96
|
+
public addOutput(output: PsbtOutputExtended): void {
|
|
97
|
+
this.transaction.addOutput(output);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @description Merge vault UTXOs into the transaction
|
|
102
|
+
* @param {VaultUTXOs[]} input The vault UTXOs
|
|
103
|
+
* @param {Signer} [firstSigner] The first signer
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
public mergeVaults(input: VaultUTXOs[], firstSigner?: Signer): void {
|
|
107
|
+
const firstVault = input[0];
|
|
108
|
+
if (!firstVault) {
|
|
109
|
+
throw new Error('No vaults provided');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const outputLeftAmount = this.calculateOuputLeftAmountFromVaults(input);
|
|
113
|
+
if (outputLeftAmount < 0) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Output left amount is negative ${outputLeftAmount} for vault ${firstVault.vault}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.addOutput({
|
|
120
|
+
address: firstVault.vault,
|
|
121
|
+
value: Number(outputLeftAmount),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
for (const vault of input) {
|
|
125
|
+
this.addVaultInputs(vault, firstSigner);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Attempt to sign all inputs
|
|
131
|
+
*/
|
|
132
|
+
public attemptSignAllInputs(): boolean {
|
|
133
|
+
let signed = false;
|
|
134
|
+
for (let i = 0; i < this.transaction.data.inputs.length; i++) {
|
|
135
|
+
const input = this.transaction.data.inputs[i];
|
|
136
|
+
if (!input.partialSig) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
this.signInput(this.transaction, input, i, this.signer);
|
|
142
|
+
signed = true;
|
|
143
|
+
} catch (e) {}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return signed;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public attemptFinalizeInputs(): boolean {
|
|
150
|
+
try {
|
|
151
|
+
this.transaction.finalizeAllInputs();
|
|
152
|
+
return true;
|
|
153
|
+
} catch (e) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Generate a multi-signature redeem script
|
|
160
|
+
* @param {string[]} publicKeys The public keys
|
|
161
|
+
* @param {number} minimum The minimum number of signatures
|
|
162
|
+
* @protected
|
|
163
|
+
* @returns {Buffer} The redeem script
|
|
164
|
+
*/
|
|
165
|
+
protected generateMultiSignRedeemScript(publicKeys: string[], minimum: number): Buffer {
|
|
166
|
+
const redeemBuffer = payments.p2ms({
|
|
167
|
+
m: minimum,
|
|
168
|
+
pubkeys: publicKeys.map((key) => Buffer.from(key, 'base64')),
|
|
169
|
+
network: this.network,
|
|
170
|
+
}).output;
|
|
171
|
+
|
|
172
|
+
if (!redeemBuffer) {
|
|
173
|
+
throw new Error('Failed to generate redeem script');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return redeemBuffer;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @description Calculate the amount left to refund to the first vault.
|
|
181
|
+
* @param {VaultUTXOs[]} vaults The vaults
|
|
182
|
+
* @private
|
|
183
|
+
* @returns {bigint} The amount left
|
|
184
|
+
*/
|
|
185
|
+
private calculateOuputLeftAmountFromVaults(vaults: VaultUTXOs[]): bigint {
|
|
186
|
+
let total = BigInt(0);
|
|
187
|
+
for (const vault of vaults) {
|
|
188
|
+
for (const utxo of vault.utxos) {
|
|
189
|
+
total += BigInt(utxo.value);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return total - this.amountRequested;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @description Add vault inputs to the transaction
|
|
198
|
+
* @param {VaultUTXOs} vault The vault UTXOs
|
|
199
|
+
* @param {Signer} [firstSigner] The first signer
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
private addVaultInputs(vault: VaultUTXOs, firstSigner: Signer = this.signer): void {
|
|
203
|
+
const vaultRedeem = this.generateMultiSignRedeemScript(vault.publicKeys, vault.minimum);
|
|
204
|
+
for (const utxo of vault.utxos) {
|
|
205
|
+
const inputIndex = this.transaction.inputCount;
|
|
206
|
+
this.addVaultUTXO(utxo, vaultRedeem);
|
|
207
|
+
|
|
208
|
+
if (firstSigner) {
|
|
209
|
+
this.log(
|
|
210
|
+
`Signing input ${inputIndex} with ${firstSigner.publicKey.toString('hex')}`,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// we don't care if we fail to sign the input
|
|
214
|
+
try {
|
|
215
|
+
this.signInput(
|
|
216
|
+
this.transaction,
|
|
217
|
+
this.transaction.data.inputs[inputIndex],
|
|
218
|
+
inputIndex,
|
|
219
|
+
firstSigner,
|
|
220
|
+
);
|
|
221
|
+
} catch (e) {
|
|
222
|
+
this.warn(
|
|
223
|
+
`Failed to sign input ${inputIndex} with ${firstSigner.publicKey.toString('hex')}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @description Add a vault UTXO to the transaction
|
|
232
|
+
* @param {IWBTCUTXODocument} utxo The UTXO to add
|
|
233
|
+
* @param {Buffer} redeem The redeem script
|
|
234
|
+
* @private
|
|
235
|
+
*/
|
|
236
|
+
private addVaultUTXO(utxo: IWBTCUTXODocument, redeem: Buffer): void {
|
|
237
|
+
const input: PsbtInputExtended = {
|
|
238
|
+
hash: Buffer.from(utxo.hash, 'hex'),
|
|
239
|
+
index: utxo.outputIndex,
|
|
240
|
+
witnessUtxo: {
|
|
241
|
+
script: Buffer.from(utxo.output, 'base64'),
|
|
242
|
+
value: Number(utxo.value),
|
|
243
|
+
},
|
|
244
|
+
witnessScript: redeem,
|
|
245
|
+
partialSig: [],
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
if (this.sighashTypes) {
|
|
249
|
+
input.sighashType = this.calculateSignHash();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.addInput(input);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { Logger } from '@btc-vision/logger';
|
|
2
|
+
import { Network, Payment, payments, Psbt, Signer, Transaction } from 'bitcoinjs-lib';
|
|
3
|
+
import { TweakedSigner, TweakSettings } from '../../signer/TweakedSigner.js';
|
|
4
|
+
import { ECPairInterface } from 'ecpair';
|
|
5
|
+
import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
|
|
6
|
+
import { PsbtInput } from 'bip174/src/lib/interfaces.js';
|
|
7
|
+
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
8
|
+
import { PsbtInputExtended, TapLeafScript } from '../interfaces/Tap.js';
|
|
9
|
+
import { AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
10
|
+
|
|
11
|
+
export interface ITweakedTransactionData {
|
|
12
|
+
readonly signer: Signer;
|
|
13
|
+
readonly network: Network;
|
|
14
|
+
readonly nonWitnessUtxo?: Buffer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The transaction sequence
|
|
19
|
+
*/
|
|
20
|
+
export enum TransactionSequence {
|
|
21
|
+
REPLACE_BY_FEE = 0xfffffffd,
|
|
22
|
+
FINAL = 0xffffffff,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @description PSBT Transaction processor.
|
|
27
|
+
* */
|
|
28
|
+
export abstract class TweakedTransaction extends Logger {
|
|
29
|
+
public readonly logColor: string = '#00ffe1';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @description Was the transaction signed?
|
|
33
|
+
*/
|
|
34
|
+
protected signer: Signer;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @description Tweaked signer
|
|
38
|
+
*/
|
|
39
|
+
protected tweakedSigner?: Signer;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @description The network of the transaction
|
|
43
|
+
*/
|
|
44
|
+
protected network: Network;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @description Was the transaction signed?
|
|
48
|
+
*/
|
|
49
|
+
protected signed: boolean = false;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @description The transaction
|
|
53
|
+
* @protected
|
|
54
|
+
*/
|
|
55
|
+
protected abstract readonly transaction: Psbt;
|
|
56
|
+
/**
|
|
57
|
+
* @description The sighash types of the transaction
|
|
58
|
+
* @protected
|
|
59
|
+
*/
|
|
60
|
+
protected readonly sighashTypes: number[] | undefined;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @description The script data of the transaction
|
|
64
|
+
*/
|
|
65
|
+
protected scriptData: Payment | null = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @description The tap data of the transaction
|
|
69
|
+
*/
|
|
70
|
+
protected tapData: Payment | null = null;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @description The inputs of the transaction
|
|
74
|
+
*/
|
|
75
|
+
protected readonly inputs: PsbtInputExtended[] = [];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @description The sequence of the transaction
|
|
79
|
+
* @protected
|
|
80
|
+
*/
|
|
81
|
+
protected sequence: number = TransactionSequence.REPLACE_BY_FEE;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The tap leaf script
|
|
85
|
+
* @protected
|
|
86
|
+
*/
|
|
87
|
+
protected tapLeafScript: TapLeafScript | null = null;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Add a non-witness utxo to the transaction
|
|
91
|
+
* @protected
|
|
92
|
+
*/
|
|
93
|
+
protected nonWitnessUtxo?: Buffer;
|
|
94
|
+
|
|
95
|
+
protected constructor(data: ITweakedTransactionData) {
|
|
96
|
+
super();
|
|
97
|
+
|
|
98
|
+
this.signer = data.signer;
|
|
99
|
+
this.network = data.network;
|
|
100
|
+
|
|
101
|
+
this.nonWitnessUtxo = data.nonWitnessUtxo;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @description Returns the script address
|
|
106
|
+
* @returns {string}
|
|
107
|
+
*/
|
|
108
|
+
public getScriptAddress(): string {
|
|
109
|
+
if (!this.scriptData || !this.scriptData.address) {
|
|
110
|
+
throw new Error('Tap data is required');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return this.scriptData.address;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @description Returns the transaction
|
|
118
|
+
* @returns {Transaction}
|
|
119
|
+
*/
|
|
120
|
+
public getTransaction(): Transaction {
|
|
121
|
+
return this.transaction.extractTransaction(false);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @description Returns the tap address
|
|
126
|
+
* @returns {string}
|
|
127
|
+
* @throws {Error} - If tap data is not set
|
|
128
|
+
*/
|
|
129
|
+
public getTapAddress(): string {
|
|
130
|
+
if (!this.tapData || !this.tapData.address) {
|
|
131
|
+
throw new Error('Tap data is required');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return this.tapData.address;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get the transaction PSBT as a base64 string.
|
|
139
|
+
* @public
|
|
140
|
+
* @returns {string} - The transaction as a base64 string
|
|
141
|
+
*/
|
|
142
|
+
public toBase64(): string {
|
|
143
|
+
return this.transaction.toBase64();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @description Disables replace by fee on the transaction
|
|
148
|
+
*/
|
|
149
|
+
public disableRBF(): void {
|
|
150
|
+
if (this.signed) throw new Error('Transaction is already signed');
|
|
151
|
+
|
|
152
|
+
this.sequence = TransactionSequence.FINAL;
|
|
153
|
+
|
|
154
|
+
for (let input of this.inputs) {
|
|
155
|
+
input.sequence = TransactionSequence.FINAL;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected generateTapData(): Payment {
|
|
160
|
+
return {
|
|
161
|
+
internalPubkey: this.internalPubKeyToXOnly(),
|
|
162
|
+
network: this.network,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Generates the script address.
|
|
168
|
+
* @protected
|
|
169
|
+
* @returns {Payment}
|
|
170
|
+
*/
|
|
171
|
+
protected generateScriptAddress(): Payment {
|
|
172
|
+
return {
|
|
173
|
+
internalPubkey: this.internalPubKeyToXOnly(),
|
|
174
|
+
network: this.network,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Returns the signer key.
|
|
180
|
+
* @protected
|
|
181
|
+
* @returns {Signer}
|
|
182
|
+
*/
|
|
183
|
+
protected getSignerKey(): Signer {
|
|
184
|
+
return this.signer;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Signs an input of the transaction.
|
|
189
|
+
* @param {Psbt} transaction - The transaction to sign
|
|
190
|
+
* @param {PsbtInput} input - The input to sign
|
|
191
|
+
* @param {number} i - The index of the input
|
|
192
|
+
* @param {Signer} [signer] - The signer to use
|
|
193
|
+
* @protected
|
|
194
|
+
*/
|
|
195
|
+
protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer): void {
|
|
196
|
+
const signHash = this.sighashTypes ? [this.calculateSignHash()] : undefined;
|
|
197
|
+
|
|
198
|
+
if (input.tapInternalKey) {
|
|
199
|
+
if (!this.tweakedSigner) throw new Error('Tweaked signer is required');
|
|
200
|
+
transaction.signTaprootInput(i, this.tweakedSigner, undefined, signHash);
|
|
201
|
+
} else {
|
|
202
|
+
transaction.signInput(i, signer || this.getSignerKey(), signHash);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Signs all the inputs of the transaction.
|
|
208
|
+
* @param {Psbt} transaction - The transaction to sign
|
|
209
|
+
* @protected
|
|
210
|
+
* @returns {void}
|
|
211
|
+
*/
|
|
212
|
+
protected signInputs(transaction: Psbt): void {
|
|
213
|
+
for (let i = 0; i < transaction.data.inputs.length; i++) {
|
|
214
|
+
let input: PsbtInput = transaction.data.inputs[i];
|
|
215
|
+
|
|
216
|
+
this.signInput(transaction, input, i);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
transaction.finalizeAllInputs();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Calculate the sign hash number
|
|
224
|
+
* @description Calculates the sign hash
|
|
225
|
+
* @protected
|
|
226
|
+
* @returns {number}
|
|
227
|
+
*/
|
|
228
|
+
protected calculateSignHash(): number {
|
|
229
|
+
if (!this.sighashTypes) {
|
|
230
|
+
throw new Error('Sighash types are required');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let signHash: number = 0;
|
|
234
|
+
for (let sighashType of this.sighashTypes) {
|
|
235
|
+
signHash |= sighashType;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return signHash;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Converts the public key to x-only.
|
|
243
|
+
* @protected
|
|
244
|
+
* @returns {Buffer}
|
|
245
|
+
*/
|
|
246
|
+
protected internalPubKeyToXOnly(): Buffer {
|
|
247
|
+
return toXOnly(this.signer.publicKey);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Internal init.
|
|
252
|
+
* @protected
|
|
253
|
+
*/
|
|
254
|
+
protected internalInit(): void {
|
|
255
|
+
this.scriptData = payments.p2tr(this.generateScriptAddress());
|
|
256
|
+
this.tapData = payments.p2tr(this.generateTapData());
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Tweak the signer for the interaction
|
|
261
|
+
* @protected
|
|
262
|
+
*/
|
|
263
|
+
protected tweakSigner(): void {
|
|
264
|
+
if (this.tweakedSigner) return;
|
|
265
|
+
|
|
266
|
+
// tweaked p2tr signer.
|
|
267
|
+
this.tweakedSigner = this.getTweakedSigner(true);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Get the tweaked signer
|
|
272
|
+
* @param {boolean} useTweakedHash Whether to use the tweaked hash
|
|
273
|
+
* @private
|
|
274
|
+
*
|
|
275
|
+
* @returns {Signer} The tweaked signer
|
|
276
|
+
*/
|
|
277
|
+
protected getTweakedSigner(useTweakedHash: boolean = false): Signer {
|
|
278
|
+
const settings: TweakSettings = {
|
|
279
|
+
network: this.network,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
if (useTweakedHash) {
|
|
283
|
+
settings.tweakHash = this.getTweakerHash();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return TweakedSigner.tweakSigner(this.signer as ECPairInterface, settings);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get the tweaked hash
|
|
291
|
+
* @private
|
|
292
|
+
*
|
|
293
|
+
* @returns {Buffer | undefined} The tweaked hash
|
|
294
|
+
*/
|
|
295
|
+
protected getTweakerHash(): Buffer | undefined {
|
|
296
|
+
return this.tapData?.hash;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Generate the PSBT input extended
|
|
301
|
+
* @param {UTXO} utxo The UTXO
|
|
302
|
+
* @param {number} i The index of the input
|
|
303
|
+
* @protected
|
|
304
|
+
* @returns {PsbtInputExtended} The PSBT input extended
|
|
305
|
+
*/
|
|
306
|
+
protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended {
|
|
307
|
+
const input: PsbtInputExtended = {
|
|
308
|
+
hash: utxo.transactionId,
|
|
309
|
+
index: utxo.outputIndex,
|
|
310
|
+
witnessUtxo: {
|
|
311
|
+
value: Number(utxo.value),
|
|
312
|
+
script: Buffer.from(utxo.scriptPubKey.hex, 'hex'),
|
|
313
|
+
},
|
|
314
|
+
sequence: this.sequence,
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
if (this.sighashTypes) {
|
|
318
|
+
input.sighashType = this.calculateSignHash();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (this.tapLeafScript) {
|
|
322
|
+
input.tapLeafScript = [this.tapLeafScript];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (i === 0 && this.nonWitnessUtxo) {
|
|
326
|
+
input.nonWitnessUtxo = this.nonWitnessUtxo;
|
|
327
|
+
this.log(`Using non-witness utxo for input ${i}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// automatically detect p2tr inputs.
|
|
331
|
+
if (
|
|
332
|
+
utxo.scriptPubKey.address &&
|
|
333
|
+
AddressVerificator.isValidP2TRAddress(utxo.scriptPubKey.address, this.network)
|
|
334
|
+
) {
|
|
335
|
+
this.tweakSigner();
|
|
336
|
+
|
|
337
|
+
input.tapInternalKey = this.internalPubKeyToXOnly();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return input;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FetchUTXOParams, FetchUTXOParamsMultiAddress, RawUTXOResponse, UTXO } from './interfaces/IUTXO.js';
|
|
2
2
|
import { WrappedGeneration } from '../wbtc/WrappedGenerationParameters.js';
|
|
3
3
|
import { WrappedGenerationParameters } from '../wbtc/Generate.js';
|
|
4
|
+
import { BroadcastResponse } from './interfaces/BroadcastResponse.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Allows to fetch UTXO data from any OPNET node
|
|
@@ -117,12 +118,32 @@ export class OPNetLimitedProvider {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
|
-
*
|
|
121
|
-
* @param {
|
|
122
|
-
* @
|
|
123
|
-
* @
|
|
121
|
+
* Broadcasts a transaction to the OPNET node
|
|
122
|
+
* @param {string} transaction - The transaction to broadcast
|
|
123
|
+
* @param {boolean} psbt - Whether the transaction is a PSBT
|
|
124
|
+
* @returns {Promise<BroadcastResponse>} - The response from the OPNET node
|
|
124
125
|
*/
|
|
125
|
-
public async
|
|
126
|
+
public async broadcastTransaction(
|
|
127
|
+
transaction: string,
|
|
128
|
+
psbt: boolean,
|
|
129
|
+
): Promise<BroadcastResponse | undefined> {
|
|
130
|
+
const params = [transaction, psbt];
|
|
131
|
+
const result = await this.rpcMethod('btc_sendRawTransaction', params);
|
|
132
|
+
|
|
133
|
+
if (!result) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return result as BroadcastResponse;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Fetches to the OPNET node
|
|
142
|
+
* @param {string} method
|
|
143
|
+
* @param {unknown[]} paramsMethod
|
|
144
|
+
* @returns {Promise<unknown>}
|
|
145
|
+
*/
|
|
146
|
+
public async rpcMethod(method: string, paramsMethod: unknown[]): Promise<unknown> {
|
|
126
147
|
const params = {
|
|
127
148
|
method: 'POST',
|
|
128
149
|
headers: {
|
|
@@ -130,8 +151,8 @@ export class OPNetLimitedProvider {
|
|
|
130
151
|
},
|
|
131
152
|
body: JSON.stringify({
|
|
132
153
|
jsonrpc: '2.0',
|
|
133
|
-
method:
|
|
134
|
-
params:
|
|
154
|
+
method: method,
|
|
155
|
+
params: paramsMethod,
|
|
135
156
|
id: 1,
|
|
136
157
|
}),
|
|
137
158
|
};
|
|
@@ -146,7 +167,7 @@ export class OPNetLimitedProvider {
|
|
|
146
167
|
|
|
147
168
|
const fetchedData = await resp.json();
|
|
148
169
|
if (!fetchedData) {
|
|
149
|
-
throw new Error('No
|
|
170
|
+
throw new Error('No data fetched');
|
|
150
171
|
}
|
|
151
172
|
|
|
152
173
|
const result = fetchedData.result;
|
|
@@ -158,9 +179,26 @@ export class OPNetLimitedProvider {
|
|
|
158
179
|
throw new Error('Something went wrong while fetching wrap parameters');
|
|
159
180
|
}
|
|
160
181
|
|
|
161
|
-
return
|
|
182
|
+
return result;
|
|
162
183
|
} catch (e) {
|
|
163
184
|
console.error(`Failed to fetch wrap parameters: ${(e as Error).stack}`);
|
|
164
185
|
}
|
|
165
186
|
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fetches the wrap parameters from the OPNET node
|
|
190
|
+
* @param {bigint} amount - The amount to wrap
|
|
191
|
+
* @returns {Promise<WrappedGeneration | undefined>} - The wrap parameters fetched
|
|
192
|
+
* @throws {Error} - If wrap parameters could not be fetched
|
|
193
|
+
*/
|
|
194
|
+
public async fetchWrapParameters(amount: bigint): Promise<WrappedGeneration | undefined> {
|
|
195
|
+
const params = [0, amount.toString()];
|
|
196
|
+
const result = await this.rpcMethod('btc_generate', params);
|
|
197
|
+
|
|
198
|
+
if (!result) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return new WrappedGeneration(result as WrappedGenerationParameters);
|
|
203
|
+
}
|
|
166
204
|
}
|