@btc-vision/transaction 1.8.0-rc.9 → 1.8.2
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/_version.d.ts.map +1 -1
- package/browser/btc-vision-bitcoin.js +5000 -8302
- package/browser/generators/builders/HashCommitmentGenerator.d.ts.map +1 -1
- package/browser/index.js +4778 -8637
- package/browser/keypair/MessageSigner.d.ts +5 -1
- package/browser/keypair/MessageSigner.d.ts.map +1 -1
- package/browser/mnemonic/Mnemonic.d.ts +1 -1
- package/browser/mnemonic/Mnemonic.d.ts.map +1 -1
- package/browser/noble-curves.js +1842 -1010
- package/browser/noble-hashes.js +854 -1512
- package/browser/rolldown-runtime.js +27 -0
- package/browser/transaction/TransactionFactory.d.ts +12 -10
- package/browser/transaction/TransactionFactory.d.ts.map +1 -1
- package/browser/transaction/browser/Web3Provider.d.ts +19 -3
- package/browser/transaction/browser/Web3Provider.d.ts.map +1 -1
- package/browser/transaction/browser/types/Unisat.d.ts +2 -6
- package/browser/transaction/browser/types/Unisat.d.ts.map +1 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts.map +1 -1
- package/browser/transaction/builders/FundingTransaction.d.ts.map +1 -1
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts.map +1 -1
- package/browser/transaction/interfaces/ITransactionResponses.d.ts +6 -0
- package/browser/transaction/interfaces/ITransactionResponses.d.ts.map +1 -1
- package/browser/transaction/interfaces/IWeb3ProviderTypes.d.ts +2 -1
- package/browser/transaction/interfaces/IWeb3ProviderTypes.d.ts.map +1 -1
- package/browser/vendors.js +7359 -9101
- package/build/_version.d.ts +1 -1
- package/build/_version.d.ts.map +1 -1
- package/build/_version.js +1 -1
- package/build/_version.js.map +1 -1
- package/build/generators/builders/HashCommitmentGenerator.d.ts.map +1 -1
- package/build/generators/builders/HashCommitmentGenerator.js.map +1 -1
- package/build/keypair/MessageSigner.d.ts +5 -1
- package/build/keypair/MessageSigner.d.ts.map +1 -1
- package/build/keypair/MessageSigner.js +56 -2
- package/build/keypair/MessageSigner.js.map +1 -1
- package/build/mnemonic/Mnemonic.d.ts +1 -1
- package/build/mnemonic/Mnemonic.d.ts.map +1 -1
- package/build/mnemonic/Mnemonic.js +1 -1
- package/build/mnemonic/Mnemonic.js.map +1 -1
- package/build/transaction/TransactionFactory.d.ts +12 -10
- package/build/transaction/TransactionFactory.d.ts.map +1 -1
- package/build/transaction/TransactionFactory.js +40 -3
- package/build/transaction/TransactionFactory.js.map +1 -1
- package/build/transaction/browser/Web3Provider.d.ts +19 -3
- package/build/transaction/browser/Web3Provider.d.ts.map +1 -1
- package/build/transaction/browser/types/Unisat.d.ts +2 -6
- package/build/transaction/browser/types/Unisat.d.ts.map +1 -1
- package/build/transaction/builders/DeploymentTransaction.d.ts.map +1 -1
- package/build/transaction/builders/DeploymentTransaction.js +1 -1
- package/build/transaction/builders/DeploymentTransaction.js.map +1 -1
- package/build/transaction/builders/FundingTransaction.d.ts.map +1 -1
- package/build/transaction/builders/FundingTransaction.js +30 -18
- package/build/transaction/builders/FundingTransaction.js.map +1 -1
- package/build/transaction/builders/SharedInteractionTransaction.d.ts.map +1 -1
- package/build/transaction/builders/SharedInteractionTransaction.js +1 -1
- package/build/transaction/builders/SharedInteractionTransaction.js.map +1 -1
- package/build/transaction/interfaces/ITransactionResponses.d.ts +6 -0
- package/build/transaction/interfaces/ITransactionResponses.d.ts.map +1 -1
- package/build/transaction/interfaces/IWeb3ProviderTypes.d.ts +2 -1
- package/build/transaction/interfaces/IWeb3ProviderTypes.d.ts.map +1 -1
- package/build/transaction/interfaces/IWeb3ProviderTypes.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/documentation/keypair/mnemonic.md +2 -2
- package/package.json +17 -11
- package/src/_version.ts +1 -1
- package/src/generators/builders/HashCommitmentGenerator.ts +8 -0
- package/src/keypair/MessageSigner.ts +101 -3
- package/src/mnemonic/Mnemonic.ts +1 -1
- package/src/transaction/TransactionFactory.ts +55 -11
- package/src/transaction/browser/Web3Provider.ts +21 -1
- package/src/transaction/browser/types/Unisat.ts +3 -7
- package/src/transaction/builders/DeploymentTransaction.ts +1 -1
- package/src/transaction/builders/FundingTransaction.ts +32 -18
- package/src/transaction/builders/SharedInteractionTransaction.ts +1 -1
- package/src/transaction/interfaces/ITransactionResponses.ts +7 -0
- package/src/transaction/interfaces/IWeb3ProviderTypes.ts +10 -1
- package/test/derivePath.test.ts +4 -4
- package/test/split-fee-bug.test.ts +827 -0
|
@@ -268,10 +268,10 @@ for (const w of wallets) {
|
|
|
268
268
|
}
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
-
###
|
|
271
|
+
### deriveMultipleOPWallet()
|
|
272
272
|
|
|
273
273
|
```typescript
|
|
274
|
-
|
|
274
|
+
deriveMultipleOPWallet(
|
|
275
275
|
addressType?: AddressTypes,
|
|
276
276
|
count?: number,
|
|
277
277
|
startIndex?: number,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/transaction",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.2",
|
|
5
5
|
"author": "BlobMaster41",
|
|
6
6
|
"description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
|
|
7
7
|
"engines": {
|
|
@@ -145,30 +145,30 @@
|
|
|
145
145
|
"prebuild": "npm run check:circular"
|
|
146
146
|
},
|
|
147
147
|
"devDependencies": {
|
|
148
|
-
"@types/node": "^25.
|
|
148
|
+
"@types/node": "^25.5.0",
|
|
149
149
|
"@types/sha.js": "^2.4.4",
|
|
150
|
-
"@vitest/browser": "^4.0
|
|
151
|
-
"@vitest/browser-playwright": "^4.0
|
|
152
|
-
"@vitest/ui": "^4.0
|
|
150
|
+
"@vitest/browser": "^4.1.0",
|
|
151
|
+
"@vitest/browser-playwright": "^4.1.0",
|
|
152
|
+
"@vitest/ui": "^4.1.0",
|
|
153
153
|
"browserify-zlib": "^0.2.0",
|
|
154
154
|
"buffer": "^6.0.3",
|
|
155
|
-
"eslint": "^10.0.
|
|
155
|
+
"eslint": "^10.0.3",
|
|
156
156
|
"madge": "^8.0.0",
|
|
157
157
|
"playwright": "^1.58.2",
|
|
158
158
|
"prettier": "^3.8.1",
|
|
159
159
|
"process": "^0.11.10",
|
|
160
160
|
"stream-browserify": "^3.0.0",
|
|
161
161
|
"typedoc": "^0.28.17",
|
|
162
|
-
"typescript-eslint": "^8.
|
|
163
|
-
"vite": "^
|
|
162
|
+
"typescript-eslint": "^8.57.0",
|
|
163
|
+
"vite": "^8.0.0",
|
|
164
164
|
"vite-plugin-dts": "^4.5.4",
|
|
165
165
|
"vite-plugin-node-polyfills": "^0.25.0",
|
|
166
|
-
"vitest": "^4.0
|
|
166
|
+
"vitest": "^4.1.0"
|
|
167
167
|
},
|
|
168
168
|
"dependencies": {
|
|
169
169
|
"@btc-vision/bip32": "^7.1.2",
|
|
170
|
-
"@btc-vision/bitcoin": "^7.0.0
|
|
171
|
-
"@btc-vision/bitcoin-rpc": "^1.
|
|
170
|
+
"@btc-vision/bitcoin": "^7.0.0",
|
|
171
|
+
"@btc-vision/bitcoin-rpc": "^1.1.2",
|
|
172
172
|
"@btc-vision/ecpair": "^4.0.5",
|
|
173
173
|
"@btc-vision/logger": "^1.0.8",
|
|
174
174
|
"@eslint/js": "^10.0.1",
|
|
@@ -177,8 +177,14 @@
|
|
|
177
177
|
"bech32": "^2.0.0",
|
|
178
178
|
"bip174": "^3.0.0",
|
|
179
179
|
"bip39": "^3.1.0",
|
|
180
|
+
"esbuild": "^0.27.4",
|
|
180
181
|
"pako": "^2.1.0",
|
|
181
182
|
"sha.js": "^2.4.12",
|
|
182
183
|
"typescript": "^5.9.3"
|
|
184
|
+
},
|
|
185
|
+
"overrides": {
|
|
186
|
+
"vite-plugin-node-polyfills": {
|
|
187
|
+
"vite": "$vite"
|
|
188
|
+
}
|
|
183
189
|
}
|
|
184
190
|
}
|
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.8.
|
|
1
|
+
export const version = '1.8.2';
|
|
@@ -35,13 +35,16 @@ export class HashCommitmentGenerator extends Logger {
|
|
|
35
35
|
|
|
36
36
|
/** Maximum weight per standard transaction */
|
|
37
37
|
public static readonly MAX_STANDARD_WEIGHT: number = 400000;
|
|
38
|
+
|
|
38
39
|
/** Minimum satoshis per output (dust limit) */
|
|
39
40
|
public static readonly MIN_OUTPUT_VALUE: bigint = 330n;
|
|
41
|
+
|
|
40
42
|
/**
|
|
41
43
|
* Bytes per hash commitment in witness script.
|
|
42
44
|
* OP_HASH160 (1) + push (1) + hash (20) + OP_EQUALVERIFY (1) = 23 bytes
|
|
43
45
|
*/
|
|
44
46
|
private static readonly BYTES_PER_COMMITMENT: number = 23;
|
|
47
|
+
|
|
45
48
|
/**
|
|
46
49
|
* Fixed overhead in witness serialization:
|
|
47
50
|
* - Stack item count: 1 byte
|
|
@@ -50,6 +53,7 @@ export class HashCommitmentGenerator extends Logger {
|
|
|
50
53
|
* - Script base (pubkey + checksig): 35 bytes
|
|
51
54
|
*/
|
|
52
55
|
private static readonly WITNESS_FIXED_OVERHEAD: number = 1 + 73 + 3 + 35;
|
|
56
|
+
|
|
53
57
|
/**
|
|
54
58
|
* Per-chunk overhead in witness:
|
|
55
59
|
* - Data: 81 bytes (80 + 1 length prefix)
|
|
@@ -58,6 +62,7 @@ export class HashCommitmentGenerator extends Logger {
|
|
|
58
62
|
*/
|
|
59
63
|
private static readonly WITNESS_PER_CHUNK_OVERHEAD: number =
|
|
60
64
|
HashCommitmentGenerator.MAX_CHUNK_SIZE + 1 + HashCommitmentGenerator.BYTES_PER_COMMITMENT;
|
|
65
|
+
|
|
61
66
|
/**
|
|
62
67
|
* Maximum data chunks per P2WSH output.
|
|
63
68
|
* Limited by total witness size: (1650 - 112) / 104 = 14 chunks
|
|
@@ -67,6 +72,7 @@ export class HashCommitmentGenerator extends Logger {
|
|
|
67
72
|
HashCommitmentGenerator.WITNESS_FIXED_OVERHEAD) /
|
|
68
73
|
HashCommitmentGenerator.WITNESS_PER_CHUNK_OVERHEAD,
|
|
69
74
|
);
|
|
75
|
+
|
|
70
76
|
/** Base weight per input (non-witness): 41 bytes * 4 = 164 */
|
|
71
77
|
private static readonly INPUT_BASE_WEIGHT: number = 164;
|
|
72
78
|
|
|
@@ -82,7 +88,9 @@ export class HashCommitmentGenerator extends Logger {
|
|
|
82
88
|
public static readonly WEIGHT_PER_INPUT: number =
|
|
83
89
|
HashCommitmentGenerator.INPUT_BASE_WEIGHT +
|
|
84
90
|
HashCommitmentGenerator.INPUT_WITNESS_WEIGHT_MAX;
|
|
91
|
+
|
|
85
92
|
public override readonly logColor: string = '#4a90d9';
|
|
93
|
+
|
|
86
94
|
private readonly publicKey: Uint8Array;
|
|
87
95
|
private readonly network: Network;
|
|
88
96
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
type MessageHash,
|
|
3
3
|
type PublicKey,
|
|
4
4
|
type SchnorrSignature,
|
|
5
|
+
type Signature,
|
|
5
6
|
type UniversalSigner,
|
|
6
7
|
} from '@btc-vision/ecpair';
|
|
7
8
|
import { backend } from '../ecc/backend.js';
|
|
@@ -11,6 +12,7 @@ import { EcKeyPair } from './EcKeyPair.js';
|
|
|
11
12
|
import { MLDSASecurityLevel, type QuantumBIP32Interface } from '@btc-vision/bip32';
|
|
12
13
|
import { isOPWallet, type OPWallet } from '../transaction/browser/types/OPWallet.js';
|
|
13
14
|
import type { MLDSASignature } from '../transaction/interfaces/IWeb3ProviderTypes.js';
|
|
15
|
+
import { SignatureType } from '../transaction/browser/types/Unisat.js';
|
|
14
16
|
|
|
15
17
|
export interface SignedMessage {
|
|
16
18
|
readonly signature: Uint8Array;
|
|
@@ -47,7 +49,38 @@ class MessageSignerBase {
|
|
|
47
49
|
const hashedMessage = this.sha256(messageBuffer);
|
|
48
50
|
const messageHex = toHex(hashedMessage);
|
|
49
51
|
|
|
50
|
-
const signatureHex = await wallet.
|
|
52
|
+
const signatureHex = await wallet.signData(
|
|
53
|
+
messageHex,
|
|
54
|
+
SignatureType.schnorr,
|
|
55
|
+
typeof message === 'string' ? message : undefined,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
signature: fromHex(signatureHex),
|
|
60
|
+
message: hashedMessage,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public async trySignECDSAWithOPWallet(
|
|
65
|
+
message: Uint8Array | string,
|
|
66
|
+
): Promise<SignedMessage | null> {
|
|
67
|
+
const wallet = this.getOPWallet();
|
|
68
|
+
if (!wallet) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const messageBuffer =
|
|
73
|
+
typeof message === 'string' ? new TextEncoder().encode(message) : message;
|
|
74
|
+
|
|
75
|
+
const hashedMessage = this.sha256(messageBuffer);
|
|
76
|
+
const messageHex = toHex(hashedMessage);
|
|
77
|
+
|
|
78
|
+
const signatureHex = await wallet.signData(
|
|
79
|
+
messageHex,
|
|
80
|
+
SignatureType.ecdsa,
|
|
81
|
+
typeof message === 'string' ? message : undefined,
|
|
82
|
+
);
|
|
83
|
+
|
|
51
84
|
return {
|
|
52
85
|
signature: fromHex(signatureHex),
|
|
53
86
|
message: hashedMessage,
|
|
@@ -68,7 +101,11 @@ class MessageSignerBase {
|
|
|
68
101
|
const hashedMessage = this.sha256(messageBuffer);
|
|
69
102
|
const messageHex = toHex(hashedMessage);
|
|
70
103
|
|
|
71
|
-
const result: MLDSASignature = await wallet.web3.signMLDSAMessage(
|
|
104
|
+
const result: MLDSASignature = await wallet.web3.signMLDSAMessage(
|
|
105
|
+
messageHex,
|
|
106
|
+
typeof message === 'string' ? message : undefined,
|
|
107
|
+
);
|
|
108
|
+
|
|
72
109
|
return {
|
|
73
110
|
signature: fromHex(result.signature),
|
|
74
111
|
message: hashedMessage,
|
|
@@ -93,6 +130,22 @@ class MessageSignerBase {
|
|
|
93
130
|
return this.signMessage(keypair, message);
|
|
94
131
|
}
|
|
95
132
|
|
|
133
|
+
public async signMessageECDSAAuto(
|
|
134
|
+
message: Uint8Array | string,
|
|
135
|
+
keypair?: UniversalSigner,
|
|
136
|
+
): Promise<SignedMessage> {
|
|
137
|
+
if (!keypair) {
|
|
138
|
+
const walletResult = await this.trySignECDSAWithOPWallet(message);
|
|
139
|
+
if (walletResult) {
|
|
140
|
+
return walletResult;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
throw new Error('No keypair provided and OP_WALLET is not available.');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.signECDSA(keypair, message);
|
|
147
|
+
}
|
|
148
|
+
|
|
96
149
|
public async tweakAndSignMessageAuto(
|
|
97
150
|
message: Uint8Array | string,
|
|
98
151
|
keypair?: UniversalSigner,
|
|
@@ -194,6 +247,52 @@ class MessageSignerBase {
|
|
|
194
247
|
};
|
|
195
248
|
}
|
|
196
249
|
|
|
250
|
+
public signECDSA(keypair: UniversalSigner, message: Uint8Array | string): SignedMessage {
|
|
251
|
+
if (typeof message === 'string') {
|
|
252
|
+
message = new TextEncoder().encode(message);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!keypair.privateKey) {
|
|
256
|
+
throw new Error('Private key not found in keypair.');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const hashedMessage = this.sha256(message);
|
|
260
|
+
|
|
261
|
+
if (!backend.sign) {
|
|
262
|
+
throw new Error('backend.signSchnorr is not available.');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
signature: backend.sign(hashedMessage as MessageHash, keypair.privateKey),
|
|
267
|
+
message: hashedMessage,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
public verifyECDSASignature(
|
|
272
|
+
publicKey: Uint8Array | PublicKey,
|
|
273
|
+
message: Uint8Array | string,
|
|
274
|
+
signature: Uint8Array | Signature,
|
|
275
|
+
): boolean {
|
|
276
|
+
if (typeof message === 'string') {
|
|
277
|
+
message = new TextEncoder().encode(message);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (signature.length !== 64) {
|
|
281
|
+
throw new Error('Invalid signature length.');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const hashedMessage = this.sha256(message);
|
|
285
|
+
if (!backend.verify) {
|
|
286
|
+
throw new Error('backend.verifySchnorr is not available.');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return backend.verify(
|
|
290
|
+
hashedMessage as MessageHash,
|
|
291
|
+
publicKey as PublicKey,
|
|
292
|
+
signature as Signature,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
197
296
|
public verifySignature(
|
|
198
297
|
publicKey: Uint8Array,
|
|
199
298
|
message: Uint8Array | string,
|
|
@@ -208,7 +307,6 @@ class MessageSignerBase {
|
|
|
208
307
|
}
|
|
209
308
|
|
|
210
309
|
const hashedMessage = this.sha256(message);
|
|
211
|
-
|
|
212
310
|
if (!backend.verifySchnorr) {
|
|
213
311
|
throw new Error('backend.verifySchnorr is not available.');
|
|
214
312
|
}
|
package/src/mnemonic/Mnemonic.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
ICancelTransactionParametersWithoutSigner,
|
|
24
24
|
ICustomTransactionWithoutSigner,
|
|
25
25
|
IDeploymentParametersWithoutSigner,
|
|
26
|
+
IFundingTransactionParametersWithoutSigner,
|
|
26
27
|
InteractionParametersWithoutSigner,
|
|
27
28
|
} from './interfaces/IWeb3ProviderTypes.js';
|
|
28
29
|
import type { WindowWithWallets } from './browser/extensions/UnisatSigner.js';
|
|
@@ -35,6 +36,7 @@ import { CancelTransaction } from './builders/CancelTransaction.js';
|
|
|
35
36
|
import { ConsolidatedInteractionTransaction } from './builders/ConsolidatedInteractionTransaction.js';
|
|
36
37
|
import type { IConsolidatedInteractionParameters } from './interfaces/IConsolidatedTransactionParameters.js';
|
|
37
38
|
import type {
|
|
39
|
+
BitcoinTransferBase,
|
|
38
40
|
CancelledTransaction,
|
|
39
41
|
DeploymentResult,
|
|
40
42
|
InteractionResponse,
|
|
@@ -50,12 +52,7 @@ export interface FundingTransactionResponse {
|
|
|
50
52
|
readonly inputUtxos: UTXO[];
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
export
|
|
54
|
-
readonly tx: string;
|
|
55
|
-
readonly estimatedFees: bigint;
|
|
56
|
-
readonly nextUTXOs: UTXO[];
|
|
57
|
-
readonly inputUtxos: UTXO[];
|
|
58
|
-
}
|
|
55
|
+
export type { BitcoinTransferBase } from './interfaces/ITransactionResponses.js';
|
|
59
56
|
|
|
60
57
|
export interface BitcoinTransferResponse extends BitcoinTransferBase {
|
|
61
58
|
readonly original: FundingTransaction;
|
|
@@ -348,7 +345,7 @@ export class TransactionFactory {
|
|
|
348
345
|
1,
|
|
349
346
|
),
|
|
350
347
|
challenge: challenge.toRaw(),
|
|
351
|
-
fundingUTXOs: fundingUTXO,
|
|
348
|
+
fundingUTXOs: [...fundingUTXO, ...inputs],
|
|
352
349
|
fundingInputUtxos: interactionParameters.utxos,
|
|
353
350
|
compiledTargetScript: toHex(interactionTx.exportCompiledTargetScript()),
|
|
354
351
|
};
|
|
@@ -529,19 +526,31 @@ export class TransactionFactory {
|
|
|
529
526
|
/**
|
|
530
527
|
* @description Creates a funding transaction.
|
|
531
528
|
* @param {IFundingTransactionParameters} parameters - The funding transaction parameters
|
|
532
|
-
* @returns {Promise<
|
|
529
|
+
* @returns {Promise<BitcoinTransferBase>} - The signed transaction
|
|
533
530
|
*/
|
|
534
531
|
public async createBTCTransfer(
|
|
535
|
-
parameters: IFundingTransactionParameters,
|
|
536
|
-
): Promise<
|
|
532
|
+
parameters: IFundingTransactionParameters | IFundingTransactionParametersWithoutSigner,
|
|
533
|
+
): Promise<BitcoinTransferBase> {
|
|
534
|
+
if (!parameters.to) {
|
|
535
|
+
throw new Error('Field "to" not provided.');
|
|
536
|
+
}
|
|
537
|
+
|
|
537
538
|
if (!parameters.from) {
|
|
538
539
|
throw new Error('Field "from" not provided.');
|
|
539
540
|
}
|
|
540
541
|
|
|
542
|
+
const opWalletInteraction = await this.detectFundingOPWallet(parameters);
|
|
543
|
+
if (opWalletInteraction) {
|
|
544
|
+
return opWalletInteraction;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (!('signer' in parameters)) {
|
|
548
|
+
throw new Error('Field "signer" not provided, OP_WALLET not detected.');
|
|
549
|
+
}
|
|
550
|
+
|
|
541
551
|
const resp = await this.createFundTransaction(parameters);
|
|
542
552
|
return {
|
|
543
553
|
estimatedFees: resp.estimatedFees,
|
|
544
|
-
original: resp.original,
|
|
545
554
|
tx: resp.tx.toHex(),
|
|
546
555
|
nextUTXOs: this.getAllNewUTXOs(resp.original, resp.tx, parameters.from),
|
|
547
556
|
inputUtxos: parameters.utxos,
|
|
@@ -600,6 +609,41 @@ export class TransactionFactory {
|
|
|
600
609
|
});
|
|
601
610
|
}
|
|
602
611
|
|
|
612
|
+
/**
|
|
613
|
+
* Detect and use OP_WALLET for funding transactions if available.
|
|
614
|
+
*
|
|
615
|
+
* @param {IFundingTransactionParameters | IFundingTransactionParametersWithoutSigner} fundingParams - The funding transaction parameters
|
|
616
|
+
* @return {Promise<BitcoinTransferBase | null>} - The funding transaction response or null if OP_WALLET not available
|
|
617
|
+
*/
|
|
618
|
+
private async detectFundingOPWallet(
|
|
619
|
+
fundingParams: IFundingTransactionParameters | IFundingTransactionParametersWithoutSigner,
|
|
620
|
+
): Promise<BitcoinTransferBase | null> {
|
|
621
|
+
if (typeof window === 'undefined') {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const _window = window as WindowWithWallets;
|
|
626
|
+
if (!_window || !_window.opnet || !_window.opnet.web3) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const opnet = _window.opnet.web3;
|
|
631
|
+
const result = await opnet.sendBitcoin({
|
|
632
|
+
...fundingParams,
|
|
633
|
+
// @ts-expect-error signer is stripped by the wallet
|
|
634
|
+
signer: undefined,
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
if (!result) {
|
|
638
|
+
throw new Error('Could not sign funding transaction.');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
...result,
|
|
643
|
+
inputUtxos: result.inputUtxos ?? fundingParams.utxos,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
603
647
|
/**
|
|
604
648
|
* Detect and use OP_WALLET for cancel transactions if available.
|
|
605
649
|
* @param {ICancelTransactionParameters | ICancelTransactionParametersWithoutSigner} interactionParameters - The cancel parameters
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
2
2
|
import type {
|
|
3
|
+
BitcoinTransferBase,
|
|
3
4
|
CancelledTransaction,
|
|
4
5
|
DeploymentResult,
|
|
5
6
|
InteractionResponse,
|
|
@@ -10,11 +11,22 @@ import type {
|
|
|
10
11
|
ICancelTransactionParametersWithoutSigner,
|
|
11
12
|
ICustomTransactionWithoutSigner,
|
|
12
13
|
IDeploymentParametersWithoutSigner,
|
|
14
|
+
IFundingTransactionParametersWithoutSigner,
|
|
13
15
|
InteractionParametersWithoutSigner,
|
|
14
16
|
MLDSASignature,
|
|
15
17
|
} from '../interfaces/IWeb3ProviderTypes.js';
|
|
16
18
|
|
|
17
19
|
export interface Web3Provider {
|
|
20
|
+
/**
|
|
21
|
+
* Build, sign, and broadcast a BTC funding transaction.
|
|
22
|
+
* The wallet provides signer, network, and MLDSA internally.
|
|
23
|
+
* The confirmation flow is handled by TxOpnetConfirmScreen.
|
|
24
|
+
*
|
|
25
|
+
* @param params - Funding transaction parameters (amount, to, feeRate, etc.)
|
|
26
|
+
* @returns The BitcoinTransferBase with tx hex, fees, and UTXOs
|
|
27
|
+
*/
|
|
28
|
+
sendBitcoin(params: IFundingTransactionParametersWithoutSigner): Promise<BitcoinTransferBase>;
|
|
29
|
+
|
|
18
30
|
signInteraction(
|
|
19
31
|
interactionParameters: InteractionParametersWithoutSigner,
|
|
20
32
|
): Promise<InteractionResponse>;
|
|
@@ -33,6 +45,13 @@ export interface Web3Provider {
|
|
|
33
45
|
|
|
34
46
|
broadcast(transactions: BroadcastTransactionOptions[]): Promise<BroadcastedTransaction[]>;
|
|
35
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Sign a PSBT (Partially Signed Bitcoin Transaction).
|
|
50
|
+
*
|
|
51
|
+
* NOT IMPLEMENTED YET — will throw an error if called.
|
|
52
|
+
*/
|
|
53
|
+
signPsbt(psbtHex: string, options?: object): Promise<string>;
|
|
54
|
+
|
|
36
55
|
/**
|
|
37
56
|
* Sign a message using Schnorr signature
|
|
38
57
|
* @param message - Hexadecimal string message to sign
|
|
@@ -53,10 +72,11 @@ export interface Web3Provider {
|
|
|
53
72
|
* Sign a message using ML-DSA signature
|
|
54
73
|
*
|
|
55
74
|
* @param message - The message to sign as a hexadecimal string
|
|
75
|
+
* @param [originalMessage] - (Optional) The original message before hashing, used for ML-DSA signature verification. If not provided, the message will be hashed internally.
|
|
56
76
|
* @returns The ML-DSA signature
|
|
57
77
|
* @throws {Error} If signing fails or wallet is not connected
|
|
58
78
|
*/
|
|
59
|
-
signMLDSAMessage(message: string): Promise<MLDSASignature>;
|
|
79
|
+
signMLDSAMessage(message: string, originalMessage?: string): Promise<MLDSASignature>;
|
|
60
80
|
|
|
61
81
|
/**
|
|
62
82
|
* Verify an ML-DSA signature
|
|
@@ -54,12 +54,6 @@ export interface Unisat {
|
|
|
54
54
|
disconnect: () => Promise<void>;
|
|
55
55
|
connect: () => Promise<void>;
|
|
56
56
|
|
|
57
|
-
sendBitcoin(
|
|
58
|
-
toAddress: string,
|
|
59
|
-
satoshis: number,
|
|
60
|
-
options: { feeRate: number; memo?: string; memos?: string[] },
|
|
61
|
-
): Promise<string>;
|
|
62
|
-
|
|
63
57
|
requestAccounts(): Promise<string[]>;
|
|
64
58
|
|
|
65
59
|
getNetwork(): Promise<WalletNetworks>;
|
|
@@ -70,13 +64,15 @@ export interface Unisat {
|
|
|
70
64
|
|
|
71
65
|
switchNetwork(network: WalletNetworks): Promise<void>;
|
|
72
66
|
|
|
67
|
+
switchChain(network: UnisatChainType): Promise<void>;
|
|
68
|
+
|
|
73
69
|
getPublicKey(): Promise<string>;
|
|
74
70
|
|
|
75
71
|
getBalance(): Promise<Balance>;
|
|
76
72
|
|
|
77
73
|
signMessage(message: string | Uint8Array, type?: MessageType): Promise<string>;
|
|
78
74
|
|
|
79
|
-
signData(hex: string, type?: SignatureType): Promise<string>;
|
|
75
|
+
signData(hex: string, type?: SignatureType, originalMessage?: string): Promise<string>;
|
|
80
76
|
|
|
81
77
|
pushTx(options: { rawtx: string }): Promise<string>;
|
|
82
78
|
|
|
@@ -38,7 +38,7 @@ import { type Feature, FeaturePriority, Features } from '../../generators/Featur
|
|
|
38
38
|
import type { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
|
|
39
39
|
|
|
40
40
|
export class DeploymentTransaction extends TransactionBuilder<TransactionType.DEPLOYMENT> {
|
|
41
|
-
public static readonly MAXIMUM_CONTRACT_SIZE =
|
|
41
|
+
public static readonly MAXIMUM_CONTRACT_SIZE = 1536 * 1024;
|
|
42
42
|
|
|
43
43
|
public type: TransactionType.DEPLOYMENT = TransactionType.DEPLOYMENT;
|
|
44
44
|
|
|
@@ -34,29 +34,43 @@ export class FundingTransaction extends TransactionBuilder<TransactionType.FUNDI
|
|
|
34
34
|
// When autoAdjustAmount is enabled and the amount would leave no room for fees,
|
|
35
35
|
// estimate the fee first and reduce the output amount accordingly.
|
|
36
36
|
if (this.autoAdjustAmount && this.amount >= this.totalInputAmount) {
|
|
37
|
-
// Add
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
// Add temporary outputs matching the ACTUAL final transaction shape
|
|
38
|
+
// so the fee estimate accounts for the real vsize.
|
|
39
|
+
const numOutputs = this.splitInputsInto > 1 ? this.splitInputsInto : 1;
|
|
40
|
+
const perOutputAmount = this.amount / BigInt(numOutputs);
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < numOutputs; i++) {
|
|
43
|
+
if (this.isPubKeyDestination) {
|
|
44
|
+
const toHexClean = this.to.startsWith('0x') ? this.to.slice(2) : this.to;
|
|
45
|
+
const pubKeyScript: Script = script.compile([
|
|
46
|
+
fromHex(toHexClean),
|
|
47
|
+
opcodes.OP_CHECKSIG,
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
this.addOutput({
|
|
51
|
+
value: toSatoshi(perOutputAmount),
|
|
52
|
+
script: pubKeyScript,
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
this.addOutput({
|
|
56
|
+
value: toSatoshi(perOutputAmount),
|
|
57
|
+
address: this.to,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
});
|
|
49
|
-
} else {
|
|
50
|
-
this.addOutput({
|
|
51
|
-
value: toSatoshi(this.amount),
|
|
52
|
-
address: this.to,
|
|
53
|
-
});
|
|
62
|
+
// If a note is present, add a temporary OP_RETURN since it affects vsize.
|
|
63
|
+
if (this.note) {
|
|
64
|
+
this.addOPReturn(this.note);
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
const estimatedFee = await this.estimateTransactionFees();
|
|
57
68
|
|
|
58
|
-
// Remove
|
|
59
|
-
this.
|
|
69
|
+
// Remove all temporary outputs.
|
|
70
|
+
const tempCount = numOutputs + (this.note ? 1 : 0);
|
|
71
|
+
for (let i = 0; i < tempCount; i++) {
|
|
72
|
+
this.outputs.pop();
|
|
73
|
+
}
|
|
60
74
|
|
|
61
75
|
const adjustedAmount = this.totalInputAmount - estimatedFee;
|
|
62
76
|
if (adjustedAmount < TransactionBuilder.MINIMUM_DUST) {
|
|
@@ -33,7 +33,7 @@ import type { IP2WSHAddress } from '../mineable/IP2WSHAddress.js';
|
|
|
33
33
|
export abstract class SharedInteractionTransaction<
|
|
34
34
|
T extends TransactionType,
|
|
35
35
|
> extends TransactionBuilder<T> {
|
|
36
|
-
public static readonly MAXIMUM_CALLDATA_SIZE =
|
|
36
|
+
public static readonly MAXIMUM_CALLDATA_SIZE = 380 * 1024; // 1MB
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Random salt for the interaction
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { RawChallenge } from '../../epoch/interfaces/IChallengeSolution.js';
|
|
2
2
|
import type { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
3
3
|
|
|
4
|
+
export interface BitcoinTransferBase {
|
|
5
|
+
readonly tx: string;
|
|
6
|
+
readonly estimatedFees: bigint;
|
|
7
|
+
readonly nextUTXOs: UTXO[];
|
|
8
|
+
readonly inputUtxos: UTXO[];
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
export interface DeploymentResult {
|
|
5
12
|
readonly transaction: [string, string];
|
|
6
13
|
readonly contractAddress: string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
IDeploymentParameters,
|
|
3
|
+
IFundingTransactionParameters,
|
|
4
|
+
IInteractionParameters,
|
|
5
|
+
} from './ITransactionParameters.js';
|
|
2
6
|
import type { ICustomTransactionParameters } from './ICustomTransactionParameters.js';
|
|
3
7
|
import type { ICancelTransactionParameters } from './ICancelTransactionParameters.js';
|
|
4
8
|
import { MLDSASecurityLevel } from '@btc-vision/bip32';
|
|
@@ -23,6 +27,11 @@ export type ICancelTransactionParametersWithoutSigner = Omit<
|
|
|
23
27
|
'signer' | 'challenge' | 'network' | 'mldsaSigner'
|
|
24
28
|
>;
|
|
25
29
|
|
|
30
|
+
export type IFundingTransactionParametersWithoutSigner = Omit<
|
|
31
|
+
IFundingTransactionParameters,
|
|
32
|
+
'signer' | 'network' | 'mldsaSigner' | 'gasSatFee'
|
|
33
|
+
>;
|
|
34
|
+
|
|
26
35
|
export interface BroadcastTransactionOptions {
|
|
27
36
|
raw: string;
|
|
28
37
|
psbt: boolean;
|
package/test/derivePath.test.ts
CHANGED
|
@@ -420,7 +420,7 @@ describe('Mnemonic.deriveOPWallet', () => {
|
|
|
420
420
|
});
|
|
421
421
|
});
|
|
422
422
|
|
|
423
|
-
describe('
|
|
423
|
+
describe('deriveMultipleOPWallet', () => {
|
|
424
424
|
it('should derive multiple wallets', () => {
|
|
425
425
|
const mnemonic = new Mnemonic(
|
|
426
426
|
testMnemonic,
|
|
@@ -429,7 +429,7 @@ describe('Mnemonic.deriveOPWallet', () => {
|
|
|
429
429
|
MLDSASecurityLevel.LEVEL2,
|
|
430
430
|
);
|
|
431
431
|
|
|
432
|
-
const wallets = mnemonic.
|
|
432
|
+
const wallets = mnemonic.deriveMultipleOPWallet(AddressTypes.P2TR, 5);
|
|
433
433
|
|
|
434
434
|
expect(wallets.length).toBe(5);
|
|
435
435
|
expect((wallets[0] as Wallet).p2tr).toBeDefined();
|
|
@@ -444,7 +444,7 @@ describe('Mnemonic.deriveOPWallet', () => {
|
|
|
444
444
|
MLDSASecurityLevel.LEVEL2,
|
|
445
445
|
);
|
|
446
446
|
|
|
447
|
-
const wallets = mnemonic.
|
|
447
|
+
const wallets = mnemonic.deriveMultipleOPWallet(AddressTypes.P2TR, 3);
|
|
448
448
|
|
|
449
449
|
const addresses = wallets.map((w) => w.p2tr);
|
|
450
450
|
const uniqueAddresses = new Set(addresses);
|
|
@@ -460,7 +460,7 @@ describe('Mnemonic.deriveOPWallet', () => {
|
|
|
460
460
|
MLDSASecurityLevel.LEVEL2,
|
|
461
461
|
);
|
|
462
462
|
|
|
463
|
-
const wallets = mnemonic.
|
|
463
|
+
const wallets = mnemonic.deriveMultipleOPWallet(AddressTypes.P2TR, 2, 5);
|
|
464
464
|
const wallet5 = mnemonic.deriveOPWallet(AddressTypes.P2TR, 5);
|
|
465
465
|
|
|
466
466
|
expect((wallets[0] as Wallet).p2tr).toBe(wallet5.p2tr);
|