@btc-vision/transaction 1.7.19 → 1.7.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/.babelrc +10 -1
- package/LICENSE +190 -21
- package/README.md +1 -1
- package/browser/_version.d.ts +1 -1
- package/browser/bip39.js +204 -0
- package/browser/bitcoin-utils.js +3172 -0
- package/browser/btc-vision-bip32.js +805 -0
- package/browser/btc-vision-bitcoin.js +4179 -0
- package/browser/btc-vision-logger.js +273 -0
- package/browser/btc-vision-post-quantum.js +542 -0
- package/browser/chain/ChainData.d.ts +1 -1
- package/browser/crypto/crypto.d.ts +1 -1
- package/browser/generators/AddressGenerator.d.ts +1 -1
- package/browser/generators/Generator.d.ts +1 -1
- package/browser/generators/MLDSAData.d.ts +1 -1
- package/browser/generators/builders/CalldataGenerator.d.ts +1 -1
- package/browser/generators/builders/CustomGenerator.d.ts +1 -1
- package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
- package/browser/generators/builders/HashCommitmentGenerator.d.ts +49 -0
- package/browser/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
- package/browser/generators/builders/P2WDAGenerator.d.ts +1 -1
- package/browser/index.js +10775 -2
- package/browser/keypair/Address.d.ts +5 -3
- package/browser/keypair/AddressVerificator.d.ts +2 -2
- package/browser/keypair/EcKeyPair.d.ts +2 -2
- package/browser/keypair/MessageSigner.d.ts +2 -2
- package/browser/keypair/Wallet.d.ts +2 -2
- package/browser/metadata/ContractBaseMetadata.d.ts +1 -1
- package/browser/mnemonic/Mnemonic.d.ts +2 -2
- package/browser/noble-curves.js +3316 -0
- package/browser/noble-hashes.js +1608 -0
- package/browser/opnet.d.ts +15 -2
- package/browser/p2wda/P2WDADetector.d.ts +2 -2
- package/browser/polyfills.js +4590 -0
- package/browser/scure-base.js +410 -0
- package/browser/signer/AddressRotation.d.ts +12 -0
- package/browser/signer/SignerUtils.d.ts +1 -1
- package/browser/signer/TweakedSigner.d.ts +1 -1
- package/browser/transaction/TransactionFactory.d.ts +15 -1
- package/browser/transaction/browser/BrowserSignerBase.d.ts +1 -1
- package/browser/transaction/browser/Web3Provider.d.ts +1 -1
- package/browser/transaction/browser/extensions/UnisatSigner.d.ts +1 -1
- package/browser/transaction/browser/extensions/XverseSigner.d.ts +1 -1
- package/browser/transaction/builders/CancelTransaction.d.ts +1 -1
- package/browser/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
- package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -1
- package/browser/transaction/builders/FundingTransaction.d.ts +1 -1
- package/browser/transaction/builders/InteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/InteractionTransactionP2WDA.d.ts +2 -2
- package/browser/transaction/builders/MultiSignTransaction.d.ts +1 -1
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/TransactionBuilder.d.ts +1 -1
- package/browser/transaction/enums/TransactionType.d.ts +3 -1
- package/browser/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +3 -1
- package/browser/transaction/interfaces/Tap.d.ts +1 -1
- package/browser/transaction/mineable/TimelockGenerator.d.ts +1 -1
- package/browser/transaction/offline/OfflineTransactionManager.d.ts +69 -0
- package/browser/transaction/offline/TransactionReconstructor.d.ts +28 -0
- package/browser/transaction/offline/TransactionSerializer.d.ts +50 -0
- package/browser/transaction/offline/TransactionStateCapture.d.ts +52 -0
- package/browser/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
- package/browser/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
- package/browser/transaction/processor/PsbtTransaction.d.ts +1 -1
- package/browser/transaction/shared/P2TR_MS.d.ts +1 -1
- package/browser/transaction/shared/TweakedTransaction.d.ts +15 -4
- package/browser/utxo/OPNetLimitedProvider.d.ts +1 -1
- package/browser/utxo/interfaces/IUTXO.d.ts +2 -0
- package/browser/valibot.js +4948 -0
- package/browser/vendors.js +12913 -0
- package/browser/verification/TapscriptVerificator.d.ts +1 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/generators/builders/HashCommitmentGenerator.d.ts +49 -0
- package/build/generators/builders/HashCommitmentGenerator.js +229 -0
- package/build/keypair/Address.d.ts +3 -1
- package/build/keypair/Address.js +87 -54
- package/build/opnet.d.ts +14 -1
- package/build/opnet.js +11 -1
- package/build/signer/AddressRotation.d.ts +12 -0
- package/build/signer/AddressRotation.js +16 -0
- package/build/transaction/TransactionFactory.d.ts +14 -0
- package/build/transaction/TransactionFactory.js +36 -0
- package/build/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
- package/build/transaction/builders/ConsolidatedInteractionTransaction.js +259 -0
- package/build/transaction/builders/TransactionBuilder.js +2 -0
- package/build/transaction/enums/TransactionType.d.ts +3 -1
- package/build/transaction/enums/TransactionType.js +2 -0
- package/build/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
- package/build/transaction/interfaces/IConsolidatedTransactionParameters.js +1 -0
- package/build/transaction/interfaces/ITransactionParameters.d.ts +2 -0
- package/build/transaction/offline/OfflineTransactionManager.d.ts +69 -0
- package/build/transaction/offline/OfflineTransactionManager.js +255 -0
- package/build/transaction/offline/TransactionReconstructor.d.ts +28 -0
- package/build/transaction/offline/TransactionReconstructor.js +243 -0
- package/build/transaction/offline/TransactionSerializer.d.ts +50 -0
- package/build/transaction/offline/TransactionSerializer.js +700 -0
- package/build/transaction/offline/TransactionStateCapture.d.ts +52 -0
- package/build/transaction/offline/TransactionStateCapture.js +275 -0
- package/build/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
- package/build/transaction/offline/interfaces/ISerializableState.js +2 -0
- package/build/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
- package/build/transaction/offline/interfaces/ITypeSpecificData.js +19 -0
- package/build/transaction/shared/TweakedTransaction.d.ts +12 -1
- package/build/transaction/shared/TweakedTransaction.js +75 -8
- package/build/utxo/interfaces/IUTXO.d.ts +2 -0
- package/documentation/README.md +5 -0
- package/documentation/offline-transaction-signing.md +650 -0
- package/documentation/transaction-building.md +603 -0
- package/package.json +62 -4
- package/src/_version.ts +1 -1
- package/src/generators/builders/HashCommitmentGenerator.ts +495 -0
- package/src/keypair/Address.ts +123 -70
- package/src/opnet.ts +16 -1
- package/src/signer/AddressRotation.ts +72 -0
- package/src/transaction/TransactionFactory.ts +87 -0
- package/src/transaction/builders/CancelTransaction.ts +4 -2
- package/src/transaction/builders/ConsolidatedInteractionTransaction.ts +561 -0
- package/src/transaction/builders/CustomScriptTransaction.ts +4 -2
- package/src/transaction/builders/MultiSignTransaction.ts +4 -2
- package/src/transaction/builders/TransactionBuilder.ts +8 -2
- package/src/transaction/enums/TransactionType.ts +2 -0
- package/src/transaction/interfaces/IConsolidatedTransactionParameters.ts +78 -0
- package/src/transaction/interfaces/ITransactionParameters.ts +8 -0
- package/src/transaction/offline/OfflineTransactionManager.ts +630 -0
- package/src/transaction/offline/TransactionReconstructor.ts +402 -0
- package/src/transaction/offline/TransactionSerializer.ts +920 -0
- package/src/transaction/offline/TransactionStateCapture.ts +469 -0
- package/src/transaction/offline/interfaces/ISerializableState.ts +141 -0
- package/src/transaction/offline/interfaces/ITypeSpecificData.ts +172 -0
- package/src/transaction/shared/TweakedTransaction.ts +156 -9
- package/src/utxo/interfaces/IUTXO.ts +8 -0
- package/test/address-rotation.test.ts +553 -0
- package/test/offline-transaction.test.ts +2065 -0
- package/vite.config.browser.ts +92 -0
- package/webpack.config.js +143 -2
- package/browser/crypto/crypto-browser.d.ts +0 -11
- package/browser/index.js.LICENSE.txt +0 -29
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
import { Psbt, Signer } from '@btc-vision/bitcoin';
|
|
2
|
+
import { ECPairInterface } from 'ecpair';
|
|
3
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
4
|
+
import { TransactionBuilder } from '../builders/TransactionBuilder.js';
|
|
5
|
+
import { MultiSignTransaction } from '../builders/MultiSignTransaction.js';
|
|
6
|
+
import { ISerializableTransactionState, PrecomputedData } from './interfaces/ISerializableState.js';
|
|
7
|
+
import { TransactionSerializer } from './TransactionSerializer.js';
|
|
8
|
+
import { ReconstructionOptions, TransactionReconstructor } from './TransactionReconstructor.js';
|
|
9
|
+
import { TransactionStateCapture } from './TransactionStateCapture.js';
|
|
10
|
+
import { isMultiSigSpecificData } from './interfaces/ITypeSpecificData.js';
|
|
11
|
+
import {
|
|
12
|
+
IDeploymentParameters,
|
|
13
|
+
IFundingTransactionParameters,
|
|
14
|
+
IInteractionParameters,
|
|
15
|
+
ITransactionParameters,
|
|
16
|
+
} from '../interfaces/ITransactionParameters.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Export options for offline transaction signing
|
|
20
|
+
*/
|
|
21
|
+
export interface ExportOptions {
|
|
22
|
+
/** The original transaction parameters */
|
|
23
|
+
params: ITransactionParameters;
|
|
24
|
+
/** Transaction type */
|
|
25
|
+
type: TransactionType;
|
|
26
|
+
/** Precomputed data from the builder */
|
|
27
|
+
precomputed?: Partial<PrecomputedData>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Main entry point for offline transaction signing workflow.
|
|
32
|
+
*
|
|
33
|
+
* This class provides a complete API for:
|
|
34
|
+
* 1. Phase 1 (Online): Building transactions and exporting state for offline signing
|
|
35
|
+
* 2. Phase 2 (Offline): Importing state, providing signers, and signing transactions
|
|
36
|
+
*
|
|
37
|
+
* Also supports fee bumping by allowing reconstruction with new fee parameters.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Phase 1 (Online environment)
|
|
42
|
+
* const params: IFundingTransactionParameters = { ... };
|
|
43
|
+
* const state = OfflineTransactionManager.exportFunding(params);
|
|
44
|
+
* // Send state to offline environment
|
|
45
|
+
*
|
|
46
|
+
* // Phase 2 (Offline environment)
|
|
47
|
+
* const signedTxHex = await OfflineTransactionManager.importSignAndExport(state, {
|
|
48
|
+
* signer: offlineSigner,
|
|
49
|
+
* });
|
|
50
|
+
* // Send signedTxHex back to online environment for broadcast
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export class OfflineTransactionManager {
|
|
54
|
+
/**
|
|
55
|
+
* Export a FundingTransaction for offline signing
|
|
56
|
+
* @param params - Funding transaction parameters
|
|
57
|
+
* @param precomputed - Optional precomputed data
|
|
58
|
+
* @returns Base64-encoded serialized state
|
|
59
|
+
*/
|
|
60
|
+
public static exportFunding(
|
|
61
|
+
params: IFundingTransactionParameters,
|
|
62
|
+
precomputed?: Partial<PrecomputedData>,
|
|
63
|
+
): string {
|
|
64
|
+
const state = TransactionStateCapture.fromFunding(params, precomputed);
|
|
65
|
+
return TransactionSerializer.toBase64(state);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Export a DeploymentTransaction for offline signing
|
|
70
|
+
* @param params - Deployment transaction parameters
|
|
71
|
+
* @param precomputed - Required precomputed data (randomBytes, compiledTargetScript)
|
|
72
|
+
* @returns Base64-encoded serialized state
|
|
73
|
+
*/
|
|
74
|
+
public static exportDeployment(
|
|
75
|
+
params: IDeploymentParameters,
|
|
76
|
+
precomputed: Partial<PrecomputedData> & {
|
|
77
|
+
compiledTargetScript: string;
|
|
78
|
+
randomBytes: string;
|
|
79
|
+
},
|
|
80
|
+
): string {
|
|
81
|
+
const state = TransactionStateCapture.fromDeployment(params, precomputed);
|
|
82
|
+
return TransactionSerializer.toBase64(state);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Export an InteractionTransaction for offline signing
|
|
87
|
+
* @param params - Interaction transaction parameters
|
|
88
|
+
* @param precomputed - Required precomputed data (randomBytes, compiledTargetScript)
|
|
89
|
+
* @returns Base64-encoded serialized state
|
|
90
|
+
*/
|
|
91
|
+
public static exportInteraction(
|
|
92
|
+
params: IInteractionParameters,
|
|
93
|
+
precomputed: Partial<PrecomputedData> & {
|
|
94
|
+
compiledTargetScript: string;
|
|
95
|
+
randomBytes: string;
|
|
96
|
+
},
|
|
97
|
+
): string {
|
|
98
|
+
const state = TransactionStateCapture.fromInteraction(params, precomputed);
|
|
99
|
+
return TransactionSerializer.toBase64(state);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Export a MultiSignTransaction for offline signing
|
|
104
|
+
* @param params - MultiSig transaction parameters
|
|
105
|
+
* @param precomputed - Optional precomputed data
|
|
106
|
+
* @returns Base64-encoded serialized state
|
|
107
|
+
*/
|
|
108
|
+
public static exportMultiSig(
|
|
109
|
+
params: ITransactionParameters & {
|
|
110
|
+
pubkeys: Buffer[];
|
|
111
|
+
minimumSignatures: number;
|
|
112
|
+
receiver: string;
|
|
113
|
+
requestedAmount: bigint;
|
|
114
|
+
refundVault: string;
|
|
115
|
+
originalInputCount?: number;
|
|
116
|
+
existingPsbtBase64?: string;
|
|
117
|
+
},
|
|
118
|
+
precomputed?: Partial<PrecomputedData>,
|
|
119
|
+
): string {
|
|
120
|
+
const state = TransactionStateCapture.fromMultiSig(params, precomputed);
|
|
121
|
+
return TransactionSerializer.toBase64(state);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Export a CustomScriptTransaction for offline signing
|
|
126
|
+
* @param params - Custom script transaction parameters
|
|
127
|
+
* @param precomputed - Optional precomputed data
|
|
128
|
+
* @returns Base64-encoded serialized state
|
|
129
|
+
*/
|
|
130
|
+
public static exportCustomScript(
|
|
131
|
+
params: ITransactionParameters & {
|
|
132
|
+
scriptElements: (Buffer | number)[];
|
|
133
|
+
witnesses: Buffer[];
|
|
134
|
+
annex?: Buffer;
|
|
135
|
+
},
|
|
136
|
+
precomputed?: Partial<PrecomputedData>,
|
|
137
|
+
): string {
|
|
138
|
+
const state = TransactionStateCapture.fromCustomScript(params, precomputed);
|
|
139
|
+
return TransactionSerializer.toBase64(state);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Export a CancelTransaction for offline signing
|
|
144
|
+
* @param params - Cancel transaction parameters
|
|
145
|
+
* @param precomputed - Optional precomputed data
|
|
146
|
+
* @returns Base64-encoded serialized state
|
|
147
|
+
*/
|
|
148
|
+
public static exportCancel(
|
|
149
|
+
params: ITransactionParameters & {
|
|
150
|
+
compiledTargetScript: Buffer | string;
|
|
151
|
+
},
|
|
152
|
+
precomputed?: Partial<PrecomputedData>,
|
|
153
|
+
): string {
|
|
154
|
+
const state = TransactionStateCapture.fromCancel(params, precomputed);
|
|
155
|
+
return TransactionSerializer.toBase64(state);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Export transaction state from a builder instance.
|
|
160
|
+
* The builder must have been built but not yet signed.
|
|
161
|
+
* @param builder - Transaction builder instance
|
|
162
|
+
* @param params - Original construction parameters
|
|
163
|
+
* @param precomputed - Precomputed data from the builder
|
|
164
|
+
* @returns Base64-encoded serialized state
|
|
165
|
+
*/
|
|
166
|
+
public static exportFromBuilder<T extends TransactionType>(
|
|
167
|
+
builder: TransactionBuilder<T>,
|
|
168
|
+
params: ITransactionParameters,
|
|
169
|
+
precomputed?: Partial<PrecomputedData>,
|
|
170
|
+
): string {
|
|
171
|
+
const type = builder.type;
|
|
172
|
+
let state: ISerializableTransactionState;
|
|
173
|
+
|
|
174
|
+
switch (type) {
|
|
175
|
+
case TransactionType.FUNDING:
|
|
176
|
+
state = TransactionStateCapture.fromFunding(
|
|
177
|
+
params as IFundingTransactionParameters,
|
|
178
|
+
precomputed,
|
|
179
|
+
);
|
|
180
|
+
break;
|
|
181
|
+
case TransactionType.DEPLOYMENT:
|
|
182
|
+
state = TransactionStateCapture.fromDeployment(
|
|
183
|
+
params as IDeploymentParameters,
|
|
184
|
+
precomputed as Partial<PrecomputedData> & {
|
|
185
|
+
compiledTargetScript: string;
|
|
186
|
+
randomBytes: string;
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
break;
|
|
190
|
+
case TransactionType.INTERACTION:
|
|
191
|
+
state = TransactionStateCapture.fromInteraction(
|
|
192
|
+
params as IInteractionParameters,
|
|
193
|
+
precomputed as Partial<PrecomputedData> & {
|
|
194
|
+
compiledTargetScript: string;
|
|
195
|
+
randomBytes: string;
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
break;
|
|
199
|
+
default:
|
|
200
|
+
throw new Error(`Unsupported transaction type for export: ${type}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return TransactionSerializer.toBase64(state);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Import and reconstruct transaction for signing
|
|
208
|
+
* @param serializedState - Base64-encoded state from Phase 1
|
|
209
|
+
* @param options - Signer(s) and optional fee overrides
|
|
210
|
+
* @returns Reconstructed transaction builder ready for signing
|
|
211
|
+
*/
|
|
212
|
+
public static importForSigning(
|
|
213
|
+
serializedState: string,
|
|
214
|
+
options: ReconstructionOptions,
|
|
215
|
+
): TransactionBuilder<TransactionType> {
|
|
216
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
217
|
+
return TransactionReconstructor.reconstruct(state, options);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Complete signing and export signed transaction
|
|
222
|
+
* @param builder - Reconstructed builder from importForSigning
|
|
223
|
+
* @returns Signed transaction hex ready for broadcast
|
|
224
|
+
*/
|
|
225
|
+
public static async signAndExport(
|
|
226
|
+
builder: TransactionBuilder<TransactionType>,
|
|
227
|
+
): Promise<string> {
|
|
228
|
+
const tx = await builder.signTransaction();
|
|
229
|
+
return tx.toHex();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Convenience: Full Phase 2 in one call - import, sign, and export
|
|
234
|
+
* @param serializedState - Base64-encoded state
|
|
235
|
+
* @param options - Signer(s) and optional fee overrides
|
|
236
|
+
* @returns Signed transaction hex ready for broadcast
|
|
237
|
+
*/
|
|
238
|
+
public static async importSignAndExport(
|
|
239
|
+
serializedState: string,
|
|
240
|
+
options: ReconstructionOptions,
|
|
241
|
+
): Promise<string> {
|
|
242
|
+
const builder = this.importForSigning(serializedState, options);
|
|
243
|
+
return this.signAndExport(builder);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Rebuild transaction with new fee rate (fee bumping)
|
|
248
|
+
* @param serializedState - Original state
|
|
249
|
+
* @param newFeeRate - New fee rate in sat/vB
|
|
250
|
+
* @returns New serialized state with updated fees (not signed yet)
|
|
251
|
+
*/
|
|
252
|
+
public static rebuildWithNewFees(serializedState: string, newFeeRate: number): string {
|
|
253
|
+
// Parse the existing state
|
|
254
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
255
|
+
|
|
256
|
+
// Create a new state with updated fee rate
|
|
257
|
+
const newState: ISerializableTransactionState = {
|
|
258
|
+
...state,
|
|
259
|
+
baseParams: {
|
|
260
|
+
...state.baseParams,
|
|
261
|
+
feeRate: newFeeRate,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return TransactionSerializer.toBase64(newState);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Rebuild and immediately sign with new fee rate
|
|
270
|
+
* @param serializedState - Original state
|
|
271
|
+
* @param newFeeRate - New fee rate in sat/vB
|
|
272
|
+
* @param options - Signer options
|
|
273
|
+
* @returns Signed transaction hex with new fees
|
|
274
|
+
*/
|
|
275
|
+
public static async rebuildSignAndExport(
|
|
276
|
+
serializedState: string,
|
|
277
|
+
newFeeRate: number,
|
|
278
|
+
options: ReconstructionOptions,
|
|
279
|
+
): Promise<string> {
|
|
280
|
+
const builder = this.importForSigning(serializedState, {
|
|
281
|
+
...options,
|
|
282
|
+
newFeeRate,
|
|
283
|
+
});
|
|
284
|
+
return this.signAndExport(builder);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Inspect serialized state without signing
|
|
289
|
+
* @param serializedState - Base64-encoded state
|
|
290
|
+
* @returns Parsed state object for inspection
|
|
291
|
+
*/
|
|
292
|
+
public static inspect(serializedState: string): ISerializableTransactionState {
|
|
293
|
+
return TransactionSerializer.fromBase64(serializedState);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Validate serialized state integrity
|
|
298
|
+
* @param serializedState - Base64-encoded state
|
|
299
|
+
* @returns True if checksum and format are valid
|
|
300
|
+
*/
|
|
301
|
+
public static validate(serializedState: string): boolean {
|
|
302
|
+
try {
|
|
303
|
+
TransactionSerializer.fromBase64(serializedState);
|
|
304
|
+
return true;
|
|
305
|
+
} catch {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get transaction type from serialized state
|
|
312
|
+
* @param serializedState - Base64-encoded state
|
|
313
|
+
* @returns Transaction type enum value
|
|
314
|
+
*/
|
|
315
|
+
public static getType(serializedState: string): TransactionType {
|
|
316
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
317
|
+
return state.header.transactionType;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Parse base64-encoded state into state object
|
|
322
|
+
* @param base64State - Base64-encoded state
|
|
323
|
+
* @returns Parsed state object
|
|
324
|
+
*/
|
|
325
|
+
public static fromBase64(base64State: string): ISerializableTransactionState {
|
|
326
|
+
return TransactionSerializer.fromBase64(base64State);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Serialize state object to base64
|
|
331
|
+
* @param state - State object to serialize
|
|
332
|
+
* @returns Base64-encoded state
|
|
333
|
+
*/
|
|
334
|
+
public static toBase64(state: ISerializableTransactionState): string {
|
|
335
|
+
return TransactionSerializer.toBase64(state);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Convert serialized state to hex format
|
|
340
|
+
* @param serializedState - Base64-encoded state
|
|
341
|
+
* @returns Hex-encoded state
|
|
342
|
+
*/
|
|
343
|
+
public static toHex(serializedState: string): string {
|
|
344
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
345
|
+
return TransactionSerializer.toHex(state);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Convert hex format back to base64
|
|
350
|
+
* @param hexState - Hex-encoded state
|
|
351
|
+
* @returns Base64-encoded state
|
|
352
|
+
*/
|
|
353
|
+
public static fromHex(hexState: string): string {
|
|
354
|
+
const state = TransactionSerializer.fromHex(hexState);
|
|
355
|
+
return TransactionSerializer.toBase64(state);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Add a partial signature to a multisig transaction state.
|
|
360
|
+
* This method signs the transaction with the provided signer and returns
|
|
361
|
+
* updated state with the new signature included.
|
|
362
|
+
*
|
|
363
|
+
* @param serializedState - Base64-encoded multisig state
|
|
364
|
+
* @param signer - The signer to add a signature with
|
|
365
|
+
* @returns Updated state with new signature, and signing result
|
|
366
|
+
*/
|
|
367
|
+
public static async multiSigAddSignature(
|
|
368
|
+
serializedState: string,
|
|
369
|
+
signer: Signer | ECPairInterface,
|
|
370
|
+
): Promise<{
|
|
371
|
+
state: string;
|
|
372
|
+
signed: boolean;
|
|
373
|
+
final: boolean;
|
|
374
|
+
psbtBase64: string;
|
|
375
|
+
}> {
|
|
376
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
377
|
+
|
|
378
|
+
if (!isMultiSigSpecificData(state.typeSpecificData)) {
|
|
379
|
+
throw new Error('State is not a multisig transaction');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const typeData = state.typeSpecificData;
|
|
383
|
+
const pubkeys = typeData.pubkeys.map((pk) => Buffer.from(pk, 'hex'));
|
|
384
|
+
|
|
385
|
+
// Parse existing PSBT or create new one
|
|
386
|
+
let psbt: Psbt;
|
|
387
|
+
const network = TransactionReconstructor['nameToNetwork'](state.baseParams.networkName);
|
|
388
|
+
|
|
389
|
+
if (typeData.existingPsbtBase64) {
|
|
390
|
+
psbt = Psbt.fromBase64(typeData.existingPsbtBase64, { network });
|
|
391
|
+
} else {
|
|
392
|
+
// Need to build the transaction first
|
|
393
|
+
const builder = this.importForSigning(serializedState, {
|
|
394
|
+
signer,
|
|
395
|
+
}) as MultiSignTransaction;
|
|
396
|
+
psbt = await builder.signPSBT();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Calculate minimums array for each input
|
|
400
|
+
const minimums: number[] = [];
|
|
401
|
+
for (let i = typeData.originalInputCount; i < psbt.data.inputs.length; i++) {
|
|
402
|
+
minimums.push(typeData.minimumSignatures);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Sign the PSBT
|
|
406
|
+
const result = MultiSignTransaction.signPartial(
|
|
407
|
+
psbt,
|
|
408
|
+
signer,
|
|
409
|
+
typeData.originalInputCount,
|
|
410
|
+
minimums,
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
// Finalize inputs (partial finalization to preserve signatures)
|
|
414
|
+
const orderedPubKeys: Buffer[][] = [];
|
|
415
|
+
for (let i = typeData.originalInputCount; i < psbt.data.inputs.length; i++) {
|
|
416
|
+
orderedPubKeys.push(pubkeys);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
MultiSignTransaction.attemptFinalizeInputs(
|
|
420
|
+
psbt,
|
|
421
|
+
typeData.originalInputCount,
|
|
422
|
+
orderedPubKeys,
|
|
423
|
+
result.final,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
const newPsbtBase64 = psbt.toBase64();
|
|
427
|
+
|
|
428
|
+
// Update the state with new PSBT
|
|
429
|
+
const newState: ISerializableTransactionState = {
|
|
430
|
+
...state,
|
|
431
|
+
typeSpecificData: {
|
|
432
|
+
...typeData,
|
|
433
|
+
existingPsbtBase64: newPsbtBase64,
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
state: TransactionSerializer.toBase64(newState),
|
|
439
|
+
signed: result.signed,
|
|
440
|
+
final: result.final,
|
|
441
|
+
psbtBase64: newPsbtBase64,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Check if a public key has already signed a multisig transaction
|
|
447
|
+
*
|
|
448
|
+
* @param serializedState - Base64-encoded multisig state
|
|
449
|
+
* @param signerPubKey - Public key to check (Buffer or hex string)
|
|
450
|
+
* @returns True if the public key has already signed
|
|
451
|
+
*/
|
|
452
|
+
public static multiSigHasSigned(
|
|
453
|
+
serializedState: string,
|
|
454
|
+
signerPubKey: Buffer | string,
|
|
455
|
+
): boolean {
|
|
456
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
457
|
+
|
|
458
|
+
if (!isMultiSigSpecificData(state.typeSpecificData)) {
|
|
459
|
+
throw new Error('State is not a multisig transaction');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const typeData = state.typeSpecificData;
|
|
463
|
+
|
|
464
|
+
if (!typeData.existingPsbtBase64) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const network = TransactionReconstructor['nameToNetwork'](state.baseParams.networkName);
|
|
469
|
+
const psbt = Psbt.fromBase64(typeData.existingPsbtBase64, { network });
|
|
470
|
+
|
|
471
|
+
const pubKeyBuffer = Buffer.isBuffer(signerPubKey)
|
|
472
|
+
? signerPubKey
|
|
473
|
+
: Buffer.from(signerPubKey, 'hex');
|
|
474
|
+
|
|
475
|
+
return MultiSignTransaction.verifyIfSigned(psbt, pubKeyBuffer);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Get the current signature count for a multisig transaction
|
|
480
|
+
*
|
|
481
|
+
* @param serializedState - Base64-encoded multisig state
|
|
482
|
+
* @returns Object with signature count info
|
|
483
|
+
*/
|
|
484
|
+
public static multiSigGetSignatureStatus(serializedState: string): {
|
|
485
|
+
required: number;
|
|
486
|
+
collected: number;
|
|
487
|
+
isComplete: boolean;
|
|
488
|
+
signers: string[];
|
|
489
|
+
} {
|
|
490
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
491
|
+
|
|
492
|
+
if (!isMultiSigSpecificData(state.typeSpecificData)) {
|
|
493
|
+
throw new Error('State is not a multisig transaction');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const typeData = state.typeSpecificData;
|
|
497
|
+
const required = typeData.minimumSignatures;
|
|
498
|
+
|
|
499
|
+
if (!typeData.existingPsbtBase64) {
|
|
500
|
+
return {
|
|
501
|
+
required,
|
|
502
|
+
collected: 0,
|
|
503
|
+
isComplete: false,
|
|
504
|
+
signers: [],
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const network = TransactionReconstructor['nameToNetwork'](state.baseParams.networkName);
|
|
509
|
+
const psbt = Psbt.fromBase64(typeData.existingPsbtBase64, { network });
|
|
510
|
+
|
|
511
|
+
// Collect signers from all inputs
|
|
512
|
+
const signerSet = new Set<string>();
|
|
513
|
+
|
|
514
|
+
for (let i = typeData.originalInputCount; i < psbt.data.inputs.length; i++) {
|
|
515
|
+
const input = psbt.data.inputs[i];
|
|
516
|
+
|
|
517
|
+
if (input.tapScriptSig) {
|
|
518
|
+
for (const sig of input.tapScriptSig) {
|
|
519
|
+
signerSet.add(sig.pubkey.toString('hex'));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (input.finalScriptWitness) {
|
|
524
|
+
const decoded = TransactionBuilder.readScriptWitnessToWitnessStack(
|
|
525
|
+
input.finalScriptWitness,
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
for (let j = 0; j < decoded.length - 2; j += 3) {
|
|
529
|
+
const pubKey = decoded[j + 2];
|
|
530
|
+
signerSet.add(pubKey.toString('hex'));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const signers = Array.from(signerSet);
|
|
536
|
+
|
|
537
|
+
return {
|
|
538
|
+
required,
|
|
539
|
+
collected: signers.length,
|
|
540
|
+
isComplete: signers.length >= required,
|
|
541
|
+
signers,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Finalize a multisig transaction and extract the signed transaction hex.
|
|
547
|
+
* Only call this when all required signatures have been collected.
|
|
548
|
+
*
|
|
549
|
+
* @param serializedState - Base64-encoded multisig state with all signatures
|
|
550
|
+
* @returns Signed transaction hex ready for broadcast
|
|
551
|
+
*/
|
|
552
|
+
public static multiSigFinalize(serializedState: string): string {
|
|
553
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
554
|
+
|
|
555
|
+
if (!isMultiSigSpecificData(state.typeSpecificData)) {
|
|
556
|
+
throw new Error('State is not a multisig transaction');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const typeData = state.typeSpecificData;
|
|
560
|
+
|
|
561
|
+
if (!typeData.existingPsbtBase64) {
|
|
562
|
+
throw new Error('No PSBT found in state - transaction has not been signed');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const network = TransactionReconstructor['nameToNetwork'](state.baseParams.networkName);
|
|
566
|
+
const psbt = Psbt.fromBase64(typeData.existingPsbtBase64, { network });
|
|
567
|
+
|
|
568
|
+
const pubkeys = typeData.pubkeys.map((pk) => Buffer.from(pk, 'hex'));
|
|
569
|
+
const orderedPubKeys: Buffer[][] = [];
|
|
570
|
+
|
|
571
|
+
for (let i = typeData.originalInputCount; i < psbt.data.inputs.length; i++) {
|
|
572
|
+
orderedPubKeys.push(pubkeys);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Final finalization
|
|
576
|
+
const success = MultiSignTransaction.attemptFinalizeInputs(
|
|
577
|
+
psbt,
|
|
578
|
+
typeData.originalInputCount,
|
|
579
|
+
orderedPubKeys,
|
|
580
|
+
true, // isFinal = true
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
if (!success) {
|
|
584
|
+
throw new Error('Failed to finalize multisig transaction - not enough signatures');
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return psbt.extractTransaction(true, true).toHex();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Get the PSBT from a multisig state (for external signing tools)
|
|
592
|
+
*
|
|
593
|
+
* @param serializedState - Base64-encoded multisig state
|
|
594
|
+
* @returns PSBT in base64 format, or null if not yet built
|
|
595
|
+
*/
|
|
596
|
+
public static multiSigGetPsbt(serializedState: string): string | null {
|
|
597
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
598
|
+
|
|
599
|
+
if (!isMultiSigSpecificData(state.typeSpecificData)) {
|
|
600
|
+
throw new Error('State is not a multisig transaction');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return state.typeSpecificData.existingPsbtBase64 || null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Update the PSBT in a multisig state (after external signing)
|
|
608
|
+
*
|
|
609
|
+
* @param serializedState - Base64-encoded multisig state
|
|
610
|
+
* @param psbtBase64 - New PSBT with additional signatures
|
|
611
|
+
* @returns Updated state
|
|
612
|
+
*/
|
|
613
|
+
public static multiSigUpdatePsbt(serializedState: string, psbtBase64: string): string {
|
|
614
|
+
const state = TransactionSerializer.fromBase64(serializedState);
|
|
615
|
+
|
|
616
|
+
if (!isMultiSigSpecificData(state.typeSpecificData)) {
|
|
617
|
+
throw new Error('State is not a multisig transaction');
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const newState: ISerializableTransactionState = {
|
|
621
|
+
...state,
|
|
622
|
+
typeSpecificData: {
|
|
623
|
+
...state.typeSpecificData,
|
|
624
|
+
existingPsbtBase64: psbtBase64,
|
|
625
|
+
},
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
return TransactionSerializer.toBase64(newState);
|
|
629
|
+
}
|
|
630
|
+
}
|