opnet 1.8.2 → 1.8.3
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/CHANGELOG.md +5 -0
- package/browser/_version.d.ts +1 -1
- package/browser/contracts/interfaces/IProviderForCallResult.d.ts +2 -0
- package/browser/index.js +174 -63
- package/browser/opnet.d.ts +1 -0
- package/browser/providers/AbstractRpcProvider.d.ts +2 -0
- package/browser/providers/interfaces/JSONRpcMethods.d.ts +1 -0
- package/browser/providers/websocket/types/WebSocketOpcodes.d.ts +2 -0
- package/browser/transactions/interfaces/BroadcastedTransactionPackage.d.ts +48 -0
- package/browser/utxos/UTXOsManager.d.ts +1 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/contracts/CallResult.js +54 -19
- package/build/contracts/interfaces/IProviderForCallResult.d.ts +2 -0
- package/build/opnet.d.ts +1 -0
- package/build/opnet.js +1 -0
- package/build/providers/AbstractRpcProvider.d.ts +2 -0
- package/build/providers/AbstractRpcProvider.js +15 -2
- package/build/providers/WebsocketRpcProvider.js +5 -0
- package/build/providers/interfaces/JSONRpcMethods.d.ts +1 -0
- package/build/providers/interfaces/JSONRpcMethods.js +1 -0
- package/build/providers/websocket/MethodMapping.js +6 -0
- package/build/providers/websocket/types/WebSocketOpcodes.d.ts +2 -0
- package/build/providers/websocket/types/WebSocketOpcodes.js +2 -0
- package/build/transactions/interfaces/BroadcastedTransactionPackage.d.ts +48 -0
- package/build/transactions/interfaces/BroadcastedTransactionPackage.js +1 -0
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/build/utxos/UTXOsManager.d.ts +1 -0
- package/build/utxos/UTXOsManager.js +37 -28
- package/docs/api-reference/provider-api.md +16 -0
- package/docs/api-reference/types-interfaces.md +91 -0
- package/docs/api-reference/utxo-manager-api.md +4 -2
- package/docs/svg/tx-broadcast-flow.svg +201 -0
- package/docs/transactions/broadcasting.md +124 -9
- package/package.json +2 -3
- package/src/_version.ts +1 -1
- package/src/contracts/CallResult.ts +82 -32
- package/src/contracts/Contract.ts +11 -4
- package/src/contracts/interfaces/IProviderForCallResult.ts +5 -0
- package/src/opnet.ts +1 -0
- package/src/providers/AbstractRpcProvider.ts +52 -5
- package/src/providers/WebsocketRpcProvider.ts +7 -0
- package/src/providers/interfaces/JSONRpcMethods.ts +1 -0
- package/src/providers/websocket/MethodMapping.ts +6 -0
- package/src/providers/websocket/types/WebSocketOpcodes.ts +2 -0
- package/src/transactions/interfaces/BroadcastedTransactionPackage.ts +72 -0
- package/src/utxos/UTXOsManager.ts +82 -46
package/CHANGELOG.md
CHANGED
package/browser/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.8.
|
|
1
|
+
export declare const version = "1.8.3";
|
|
@@ -2,6 +2,7 @@ import { Network } from '../../../node_modules/@btc-vision/bitcoin/build/index.j
|
|
|
2
2
|
import { Address, ChallengeSolution, IP2WSHAddress } from '../../../node_modules/@btc-vision/transaction/build/index.js';
|
|
3
3
|
import { UTXO, UTXOs } from '../../bitcoin/UTXOs.js';
|
|
4
4
|
import { BroadcastedTransaction } from '../../transactions/interfaces/BroadcastedTransaction.js';
|
|
5
|
+
import { BroadcastedTransactionPackage } from '../../transactions/interfaces/BroadcastedTransactionPackage.js';
|
|
5
6
|
import { RequestUTXOsParamsWithAmount } from '../../utxos/interfaces/IUTXOsManager.js';
|
|
6
7
|
export interface IUTXOManagerForCallResult {
|
|
7
8
|
getUTXOsForAmount(params: RequestUTXOsParamsWithAmount): Promise<UTXO[]>;
|
|
@@ -13,5 +14,6 @@ export interface IProviderForCallResult {
|
|
|
13
14
|
readonly utxoManager: IUTXOManagerForCallResult;
|
|
14
15
|
getChallenge(): Promise<ChallengeSolution>;
|
|
15
16
|
sendRawTransaction(tx: string, psbt: boolean): Promise<BroadcastedTransaction>;
|
|
17
|
+
sendRawTransactionPackage(txs: string[], isPackage?: boolean): Promise<BroadcastedTransactionPackage>;
|
|
16
18
|
getCSV1ForAddress(address: Address): IP2WSHAddress;
|
|
17
19
|
}
|
package/browser/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { d as decompile, f as fromHex, A as Address, b as fromBase64, N as NetEvent, B as BinaryWriter, c as BinaryReader, e as AddressTypes, h as AddressVerificator, t as toHex, C as ChallengeSolution, r as regtest, o as opnetTestnet, i as testnet, j as bitcoin, T as TransactionFactory, k as BufferHelper, l as AddressMap, m as toBase64, p as pLimit, n as process$1, L as Logger, q as Long, s as abiTypeToSelectorString, u as ABICoder, v as isAbiTuple, w as isAbiStruct, x as ABIDataTypes, y as BigNumber, z as fromBech32, P as P2MR_MS, D as P2TR_MS } from './vendors.js';
|
|
2
2
|
import { p as protobuf } from './protobuf.js';
|
|
3
3
|
|
|
4
|
-
const version = "1.8.
|
|
4
|
+
const version = "1.8.3";
|
|
5
5
|
|
|
6
6
|
var OPNetTransactionTypes = /* @__PURE__ */ ((OPNetTransactionTypes2) => {
|
|
7
7
|
OPNetTransactionTypes2["Generic"] = "Generic";
|
|
@@ -953,6 +953,19 @@ class TransactionHelper {
|
|
|
953
953
|
}
|
|
954
954
|
|
|
955
955
|
const factory = new TransactionFactory();
|
|
956
|
+
function extractPackageFailures(packageResult) {
|
|
957
|
+
const failures = [];
|
|
958
|
+
const results = packageResult.txResults;
|
|
959
|
+
for (const [submittedTxid, result] of Object.entries(results)) {
|
|
960
|
+
if (result.error) {
|
|
961
|
+
failures.push(`tx ${submittedTxid} failed: ${result.error}`);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (failures.length === 0 && packageResult.packageMsg !== "success") {
|
|
965
|
+
failures.push(`package rejected: ${packageResult.packageMsg}`);
|
|
966
|
+
}
|
|
967
|
+
return failures;
|
|
968
|
+
}
|
|
956
969
|
class CallResult {
|
|
957
970
|
result;
|
|
958
971
|
accessList;
|
|
@@ -1052,6 +1065,13 @@ class CallResult {
|
|
|
1052
1065
|
)
|
|
1053
1066
|
);
|
|
1054
1067
|
},
|
|
1068
|
+
sendRawTransactionPackage: () => {
|
|
1069
|
+
return Promise.reject(
|
|
1070
|
+
new Error(
|
|
1071
|
+
"Cannot broadcast from offline CallResult. Export signed transaction and broadcast online."
|
|
1072
|
+
)
|
|
1073
|
+
);
|
|
1074
|
+
},
|
|
1055
1075
|
getCSV1ForAddress: () => {
|
|
1056
1076
|
if (!data.csvAddress) {
|
|
1057
1077
|
throw new Error("CSV address not available in offline data");
|
|
@@ -1215,43 +1235,69 @@ class CallResult {
|
|
|
1215
1235
|
}
|
|
1216
1236
|
/**
|
|
1217
1237
|
* Broadcasts a pre-signed interaction transaction.
|
|
1238
|
+
* Uses sendRawTransactionPackage for atomic broadcast when a funding tx is present,
|
|
1239
|
+
* falls back to sendRawTransaction for P2WDA (interaction-only) transactions.
|
|
1218
1240
|
* @param {SignedInteractionTransactionReceipt} signedTx - The signed transaction data.
|
|
1219
1241
|
* @returns {Promise<InteractionTransactionReceipt>} The transaction receipt with broadcast results.
|
|
1220
1242
|
*/
|
|
1221
1243
|
async sendPresignedTransaction(signedTx) {
|
|
1222
|
-
if (
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1226
|
-
const tx1 = await this.#provider.sendRawTransaction(
|
|
1227
|
-
signedTx.fundingTransactionRaw,
|
|
1244
|
+
if (signedTx.utxoTracking.isP2WDA || !signedTx.fundingTransactionRaw) {
|
|
1245
|
+
const tx = await this.#provider.sendRawTransaction(
|
|
1246
|
+
signedTx.interactionTransactionRaw,
|
|
1228
1247
|
false
|
|
1229
1248
|
);
|
|
1230
|
-
if (!
|
|
1231
|
-
throw new Error(`Error sending transaction: ${
|
|
1249
|
+
if (!tx || tx.error) {
|
|
1250
|
+
throw new Error(`Error sending transaction: ${tx?.error || "Unknown error"}`);
|
|
1251
|
+
}
|
|
1252
|
+
if (!tx.result) {
|
|
1253
|
+
throw new Error("No transaction ID returned");
|
|
1232
1254
|
}
|
|
1233
|
-
if (!
|
|
1234
|
-
throw new Error(`Error sending transaction: ${
|
|
1255
|
+
if (!tx.success) {
|
|
1256
|
+
throw new Error(`Error sending transaction: ${tx.result || "Unknown error"}`);
|
|
1235
1257
|
}
|
|
1258
|
+
this.#processUTXOTracking(signedTx);
|
|
1259
|
+
return {
|
|
1260
|
+
interactionAddress: signedTx.interactionAddress,
|
|
1261
|
+
transactionId: tx.result,
|
|
1262
|
+
peerAcknowledgements: tx.peers || 0,
|
|
1263
|
+
newUTXOs: signedTx.nextUTXOs,
|
|
1264
|
+
estimatedFees: signedTx.estimatedFees,
|
|
1265
|
+
challengeSolution: signedTx.challengeSolution,
|
|
1266
|
+
rawTransaction: signedTx.interactionTransactionRaw,
|
|
1267
|
+
fundingUTXOs: signedTx.fundingUTXOs,
|
|
1268
|
+
fundingInputUtxos: signedTx.fundingInputUtxos,
|
|
1269
|
+
compiledTargetScript: signedTx.compiledTargetScript
|
|
1270
|
+
};
|
|
1236
1271
|
}
|
|
1237
|
-
const
|
|
1238
|
-
signedTx.interactionTransactionRaw,
|
|
1239
|
-
|
|
1272
|
+
const result = await this.#provider.sendRawTransactionPackage(
|
|
1273
|
+
[signedTx.fundingTransactionRaw, signedTx.interactionTransactionRaw],
|
|
1274
|
+
true
|
|
1240
1275
|
);
|
|
1241
|
-
if (!
|
|
1242
|
-
throw new Error(
|
|
1276
|
+
if (!result.success) {
|
|
1277
|
+
throw new Error(
|
|
1278
|
+
`Error sending transaction package: ${result.error || "Unknown error"}`
|
|
1279
|
+
);
|
|
1243
1280
|
}
|
|
1244
|
-
if (
|
|
1245
|
-
|
|
1281
|
+
if (result.packageResult) {
|
|
1282
|
+
const failures = extractPackageFailures(result.packageResult);
|
|
1283
|
+
if (failures.length > 0) {
|
|
1284
|
+
throw new Error(`Transaction package failed:
|
|
1285
|
+
${failures.join("\n")}`);
|
|
1286
|
+
}
|
|
1246
1287
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1288
|
+
const interactionSeqResult = result.sequentialResults?.[1];
|
|
1289
|
+
if (interactionSeqResult && !interactionSeqResult.success) {
|
|
1290
|
+
throw new Error(
|
|
1291
|
+
`Interaction transaction failed: ${interactionSeqResult.error || "Unknown error"}`
|
|
1292
|
+
);
|
|
1249
1293
|
}
|
|
1294
|
+
const interactionTxId = interactionSeqResult?.txid || signedTx.interactionTransactionRaw;
|
|
1295
|
+
const peers = interactionSeqResult?.peers || 0;
|
|
1250
1296
|
this.#processUTXOTracking(signedTx);
|
|
1251
1297
|
return {
|
|
1252
1298
|
interactionAddress: signedTx.interactionAddress,
|
|
1253
|
-
transactionId:
|
|
1254
|
-
peerAcknowledgements:
|
|
1299
|
+
transactionId: interactionTxId,
|
|
1300
|
+
peerAcknowledgements: peers,
|
|
1255
1301
|
newUTXOs: signedTx.nextUTXOs,
|
|
1256
1302
|
estimatedFees: signedTx.estimatedFees,
|
|
1257
1303
|
challengeSolution: signedTx.challengeSolution,
|
|
@@ -1949,6 +1995,7 @@ var JSONRpcMethods = /* @__PURE__ */ ((JSONRpcMethods2) => {
|
|
|
1949
1995
|
JSONRpcMethods2["GAS"] = "btc_gas";
|
|
1950
1996
|
JSONRpcMethods2["GET_TRANSACTION_BY_HASH"] = "btc_getTransactionByHash";
|
|
1951
1997
|
JSONRpcMethods2["BROADCAST_TRANSACTION"] = "btc_sendRawTransaction";
|
|
1998
|
+
JSONRpcMethods2["BROADCAST_TRANSACTION_PACKAGE"] = "btc_sendRawTransactionPackage";
|
|
1952
1999
|
JSONRpcMethods2["TRANSACTION_PREIMAGE"] = "btc_preimage";
|
|
1953
2000
|
JSONRpcMethods2["PUBLIC_KEY_INFO"] = "btc_publicKeyInfo";
|
|
1954
2001
|
JSONRpcMethods2["GET_UTXOS"] = "btc_getUTXOs";
|
|
@@ -1977,7 +2024,7 @@ class UTXOsManager {
|
|
|
1977
2024
|
this.provider = provider;
|
|
1978
2025
|
}
|
|
1979
2026
|
/**
|
|
1980
|
-
* Holds all address-specific data so we don
|
|
2027
|
+
* Holds all address-specific data so we don't mix up UTXOs between addresses/wallets.
|
|
1981
2028
|
*/
|
|
1982
2029
|
dataByAddress = {};
|
|
1983
2030
|
/**
|
|
@@ -2112,11 +2159,14 @@ class UTXOsManager {
|
|
|
2112
2159
|
* Fetch UTXOs for a specific amount needed, from a single address,
|
|
2113
2160
|
* merging from pending and confirmed UTXOs.
|
|
2114
2161
|
*
|
|
2162
|
+
* Prioritizes normal UTXOs first, only falling back to CSV UTXOs
|
|
2163
|
+
* if the normal ones cannot cover the requested amount.
|
|
2164
|
+
*
|
|
2115
2165
|
* @param {object} options
|
|
2116
2166
|
* @param {string} options.address The address to fetch UTXOs for
|
|
2117
2167
|
* @param {bigint} options.amount The needed amount
|
|
2118
2168
|
* @param {boolean} [options.optimize=true] Optimize the UTXOs
|
|
2119
|
-
* @param {boolean} [options.csvAddress] Use CSV UTXOs
|
|
2169
|
+
* @param {boolean} [options.csvAddress] Use CSV UTXOs as fallback
|
|
2120
2170
|
* @param {boolean} [options.mergePendingUTXOs=true] Merge pending
|
|
2121
2171
|
* @param {boolean} [options.filterSpentUTXOs=true] Filter out spent
|
|
2122
2172
|
* @param {boolean} [options.throwErrors=false] Throw error if insufficient
|
|
@@ -2135,52 +2185,47 @@ class UTXOsManager {
|
|
|
2135
2185
|
maxUTXOs = 5e3,
|
|
2136
2186
|
throwIfUTXOsLimitReached = false
|
|
2137
2187
|
}) {
|
|
2138
|
-
const
|
|
2139
|
-
if (csvAddress) {
|
|
2140
|
-
utxosPromises.push(
|
|
2141
|
-
this.getUTXOs({
|
|
2142
|
-
address: csvAddress,
|
|
2143
|
-
optimize: true,
|
|
2144
|
-
mergePendingUTXOs: false,
|
|
2145
|
-
filterSpentUTXOs: true,
|
|
2146
|
-
olderThan: 1n,
|
|
2147
|
-
isCSV: true
|
|
2148
|
-
})
|
|
2149
|
-
);
|
|
2150
|
-
}
|
|
2151
|
-
utxosPromises.push(
|
|
2152
|
-
this.getUTXOs({
|
|
2153
|
-
address,
|
|
2154
|
-
optimize,
|
|
2155
|
-
mergePendingUTXOs,
|
|
2156
|
-
filterSpentUTXOs,
|
|
2157
|
-
olderThan
|
|
2158
|
-
})
|
|
2159
|
-
);
|
|
2160
|
-
const combinedUTXOs = (await Promise.all(utxosPromises)).flat();
|
|
2161
|
-
const utxoUntilAmount = [];
|
|
2188
|
+
const selected = [];
|
|
2162
2189
|
let currentValue = 0n;
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
currentValue
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2190
|
+
const normalUTXOs = await this.getUTXOs({
|
|
2191
|
+
address,
|
|
2192
|
+
optimize,
|
|
2193
|
+
mergePendingUTXOs,
|
|
2194
|
+
filterSpentUTXOs,
|
|
2195
|
+
olderThan
|
|
2196
|
+
});
|
|
2197
|
+
currentValue = this.selectUTXOsGreedily(
|
|
2198
|
+
normalUTXOs,
|
|
2199
|
+
selected,
|
|
2200
|
+
currentValue,
|
|
2201
|
+
amount,
|
|
2202
|
+
maxUTXOs,
|
|
2203
|
+
throwIfUTXOsLimitReached
|
|
2204
|
+
);
|
|
2205
|
+
if (currentValue < amount && csvAddress) {
|
|
2206
|
+
const csvUTXOs = await this.getUTXOs({
|
|
2207
|
+
address: csvAddress,
|
|
2208
|
+
optimize: true,
|
|
2209
|
+
mergePendingUTXOs: false,
|
|
2210
|
+
filterSpentUTXOs: true,
|
|
2211
|
+
olderThan: 1n,
|
|
2212
|
+
isCSV: true
|
|
2213
|
+
});
|
|
2214
|
+
currentValue = this.selectUTXOsGreedily(
|
|
2215
|
+
csvUTXOs,
|
|
2216
|
+
selected,
|
|
2217
|
+
currentValue,
|
|
2218
|
+
amount,
|
|
2219
|
+
maxUTXOs,
|
|
2220
|
+
throwIfUTXOsLimitReached
|
|
2221
|
+
);
|
|
2177
2222
|
}
|
|
2178
2223
|
if (currentValue < amount && throwErrors) {
|
|
2179
2224
|
throw new Error(
|
|
2180
2225
|
`Insufficient UTXOs to cover amount. Available: ${currentValue}, Needed: ${amount}`
|
|
2181
2226
|
);
|
|
2182
2227
|
}
|
|
2183
|
-
return
|
|
2228
|
+
return selected;
|
|
2184
2229
|
}
|
|
2185
2230
|
/**
|
|
2186
2231
|
* Fetch UTXOs for multiple addresses in a single batch request.
|
|
@@ -2254,6 +2299,33 @@ class UTXOsManager {
|
|
|
2254
2299
|
}
|
|
2255
2300
|
return result;
|
|
2256
2301
|
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Sort UTXOs by value descending and greedily append to `selected` until
|
|
2304
|
+
* `currentValue >= amount` or the pool is exhausted. Mutates `candidates`
|
|
2305
|
+
* (sort in-place) and `selected` (pushes chosen UTXOs). Returns the
|
|
2306
|
+
* updated cumulative value.
|
|
2307
|
+
*/
|
|
2308
|
+
selectUTXOsGreedily(candidates, selected, currentValue, amount, maxUTXOs, throwIfLimitReached) {
|
|
2309
|
+
candidates.sort((a, b) => {
|
|
2310
|
+
if (b.value > a.value) return 1;
|
|
2311
|
+
if (b.value < a.value) return -1;
|
|
2312
|
+
return 0;
|
|
2313
|
+
});
|
|
2314
|
+
for (const utxo of candidates) {
|
|
2315
|
+
if (currentValue >= amount) break;
|
|
2316
|
+
if (maxUTXOs && selected.length >= maxUTXOs) {
|
|
2317
|
+
if (throwIfLimitReached) {
|
|
2318
|
+
throw new Error(
|
|
2319
|
+
`Woah. You must consolidate your UTXOs (${candidates.length + selected.length})! This transaction is too large.`
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
break;
|
|
2323
|
+
}
|
|
2324
|
+
selected.push(utxo);
|
|
2325
|
+
currentValue += utxo.value;
|
|
2326
|
+
}
|
|
2327
|
+
return currentValue;
|
|
2328
|
+
}
|
|
2257
2329
|
/**
|
|
2258
2330
|
* Fetch UTXOs for multiple addresses in a single batch RPC call.
|
|
2259
2331
|
* @private
|
|
@@ -2914,6 +2986,32 @@ class AbstractRpcProvider {
|
|
|
2914
2986
|
return rawTx.result;
|
|
2915
2987
|
});
|
|
2916
2988
|
}
|
|
2989
|
+
/**
|
|
2990
|
+
* Broadcast a package of raw transactions atomically.
|
|
2991
|
+
* @description Submits an ordered array of raw transactions via Bitcoin Core's submitpackage
|
|
2992
|
+
* RPC for atomic acceptance, or falls back to validated sequential broadcast.
|
|
2993
|
+
* @param {string[]} txs The raw transactions to send as hex strings (max 25)
|
|
2994
|
+
* @param {boolean} [isPackage=true] Whether to use atomic package submission (submitpackage)
|
|
2995
|
+
* or validated sequential broadcast (testmempoolaccept + sendrawtransaction)
|
|
2996
|
+
* @returns {Promise<BroadcastedTransactionPackage>} The result of the package broadcast
|
|
2997
|
+
* @throws {Error} If something went wrong while broadcasting the package
|
|
2998
|
+
*/
|
|
2999
|
+
async sendRawTransactionPackage(txs, isPackage = true) {
|
|
3000
|
+
if (!txs.length) {
|
|
3001
|
+
throw new Error("sendRawTransactionPackage: txs array must not be empty");
|
|
3002
|
+
}
|
|
3003
|
+
for (let i = 0; i < txs.length; i++) {
|
|
3004
|
+
if (!/^[0-9A-Fa-f]+$/.test(txs[i])) {
|
|
3005
|
+
throw new Error(`sendRawTransactionPackage: txs[${i}] is not a valid hex string`);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
const payload = this.buildJsonRpcPayload(
|
|
3009
|
+
JSONRpcMethods.BROADCAST_TRANSACTION_PACKAGE,
|
|
3010
|
+
[txs, isPackage]
|
|
3011
|
+
);
|
|
3012
|
+
const result = await this.callPayloadSingle(payload);
|
|
3013
|
+
return result.result;
|
|
3014
|
+
}
|
|
2917
3015
|
/**
|
|
2918
3016
|
* Get block witnesses.
|
|
2919
3017
|
* @description This method is used to get the witnesses of a block. This proves that the actions executed inside a block are valid and confirmed by the network. If the minimum number of witnesses are not met, the block is considered as potentially invalid.
|
|
@@ -3965,6 +4063,7 @@ var WebSocketRequestOpcode = /* @__PURE__ */ ((WebSocketRequestOpcode2) => {
|
|
|
3965
4063
|
WebSocketRequestOpcode2[WebSocketRequestOpcode2["GET_TRANSACTION_BY_HASH"] = 32] = "GET_TRANSACTION_BY_HASH";
|
|
3966
4064
|
WebSocketRequestOpcode2[WebSocketRequestOpcode2["GET_TRANSACTION_RECEIPT"] = 33] = "GET_TRANSACTION_RECEIPT";
|
|
3967
4065
|
WebSocketRequestOpcode2[WebSocketRequestOpcode2["BROADCAST_TRANSACTION"] = 34] = "BROADCAST_TRANSACTION";
|
|
4066
|
+
WebSocketRequestOpcode2[WebSocketRequestOpcode2["BROADCAST_TRANSACTION_PACKAGE"] = 39] = "BROADCAST_TRANSACTION_PACKAGE";
|
|
3968
4067
|
WebSocketRequestOpcode2[WebSocketRequestOpcode2["GET_PREIMAGE"] = 35] = "GET_PREIMAGE";
|
|
3969
4068
|
WebSocketRequestOpcode2[WebSocketRequestOpcode2["GET_MEMPOOL_INFO"] = 36] = "GET_MEMPOOL_INFO";
|
|
3970
4069
|
WebSocketRequestOpcode2[WebSocketRequestOpcode2["GET_PENDING_TRANSACTION"] = 37] = "GET_PENDING_TRANSACTION";
|
|
@@ -3999,6 +4098,7 @@ var WebSocketResponseOpcode = /* @__PURE__ */ ((WebSocketResponseOpcode2) => {
|
|
|
3999
4098
|
WebSocketResponseOpcode2[WebSocketResponseOpcode2["TRANSACTION"] = 160] = "TRANSACTION";
|
|
4000
4099
|
WebSocketResponseOpcode2[WebSocketResponseOpcode2["TRANSACTION_RECEIPT"] = 161] = "TRANSACTION_RECEIPT";
|
|
4001
4100
|
WebSocketResponseOpcode2[WebSocketResponseOpcode2["BROADCAST_RESULT"] = 162] = "BROADCAST_RESULT";
|
|
4101
|
+
WebSocketResponseOpcode2[WebSocketResponseOpcode2["BROADCAST_PACKAGE_RESULT"] = 167] = "BROADCAST_PACKAGE_RESULT";
|
|
4002
4102
|
WebSocketResponseOpcode2[WebSocketResponseOpcode2["PREIMAGE"] = 163] = "PREIMAGE";
|
|
4003
4103
|
WebSocketResponseOpcode2[WebSocketResponseOpcode2["MEMPOOL_INFO"] = 164] = "MEMPOOL_INFO";
|
|
4004
4104
|
WebSocketResponseOpcode2[WebSocketResponseOpcode2["PENDING_TRANSACTION"] = 165] = "PENDING_TRANSACTION";
|
|
@@ -4077,6 +4177,12 @@ const METHOD_MAPPINGS = {
|
|
|
4077
4177
|
requestType: "BroadcastTransactionRequest",
|
|
4078
4178
|
responseType: "BroadcastTransactionResponse"
|
|
4079
4179
|
},
|
|
4180
|
+
[JSONRpcMethods.BROADCAST_TRANSACTION_PACKAGE]: {
|
|
4181
|
+
requestOpcode: WebSocketRequestOpcode.BROADCAST_TRANSACTION_PACKAGE,
|
|
4182
|
+
responseOpcode: WebSocketResponseOpcode.BROADCAST_PACKAGE_RESULT,
|
|
4183
|
+
requestType: "BroadcastTransactionPackageRequest",
|
|
4184
|
+
responseType: "BroadcastTransactionPackageResponse"
|
|
4185
|
+
},
|
|
4080
4186
|
[JSONRpcMethods.TRANSACTION_PREIMAGE]: {
|
|
4081
4187
|
requestOpcode: WebSocketRequestOpcode.GET_PREIMAGE,
|
|
4082
4188
|
responseOpcode: WebSocketResponseOpcode.PREIMAGE,
|
|
@@ -4724,6 +4830,11 @@ class WebSocketRpcProvider extends AbstractRpcProvider {
|
|
|
4724
4830
|
2: params[0],
|
|
4725
4831
|
3: params[1] ?? false
|
|
4726
4832
|
};
|
|
4833
|
+
case JSONRpcMethods.BROADCAST_TRANSACTION_PACKAGE:
|
|
4834
|
+
return {
|
|
4835
|
+
2: params[0],
|
|
4836
|
+
3: params[1] ?? true
|
|
4837
|
+
};
|
|
4727
4838
|
case JSONRpcMethods.TRANSACTION_PREIMAGE:
|
|
4728
4839
|
return {};
|
|
4729
4840
|
case JSONRpcMethods.GET_BALANCE:
|
package/browser/opnet.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ export * from './storage/interfaces/IStorageValue.js';
|
|
|
70
70
|
export * from './storage/StoredValue.js';
|
|
71
71
|
export * from './contracts/interfaces/IRawContract.js';
|
|
72
72
|
export * from './transactions/interfaces/BroadcastedTransaction.js';
|
|
73
|
+
export * from './transactions/interfaces/BroadcastedTransactionPackage.js';
|
|
73
74
|
export * from './transactions/interfaces/ITransaction.js';
|
|
74
75
|
export * from './transactions/interfaces/ITransactionReceipt.js';
|
|
75
76
|
export * from './transactions/metadata/TransactionReceipt.js';
|
|
@@ -18,6 +18,7 @@ import { OPNetTransactionTypes } from '../interfaces/opnet/OPNetTransactionTypes
|
|
|
18
18
|
import { MempoolTransactionData } from '../mempool/MempoolTransactionData.js';
|
|
19
19
|
import { StoredValue } from '../storage/StoredValue.js';
|
|
20
20
|
import { BroadcastedTransaction } from '../transactions/interfaces/BroadcastedTransaction.js';
|
|
21
|
+
import { BroadcastedTransactionPackage } from '../transactions/interfaces/BroadcastedTransactionPackage.js';
|
|
21
22
|
import { TransactionReceipt } from '../transactions/metadata/TransactionReceipt.js';
|
|
22
23
|
import { TransactionBase } from '../transactions/Transaction.js';
|
|
23
24
|
import { UTXOsManager } from '../utxos/UTXOsManager.js';
|
|
@@ -59,6 +60,7 @@ export declare abstract class AbstractRpcProvider {
|
|
|
59
60
|
gasParameters(): Promise<BlockGasParameters>;
|
|
60
61
|
sendRawTransaction(tx: string, psbt: boolean): Promise<BroadcastedTransaction>;
|
|
61
62
|
sendRawTransactions(txs: string[]): Promise<BroadcastedTransaction[]>;
|
|
63
|
+
sendRawTransactionPackage(txs: string[], isPackage?: boolean): Promise<BroadcastedTransactionPackage>;
|
|
62
64
|
getBlockWitness(height?: BigNumberish, trusted?: boolean, limit?: number, page?: number): Promise<BlockWitnesses>;
|
|
63
65
|
getReorg(fromBlock?: BigNumberish, toBlock?: BigNumberish): Promise<ReorgInformation[]>;
|
|
64
66
|
abstract _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcCallResult>;
|
|
@@ -8,6 +8,7 @@ export declare enum JSONRpcMethods {
|
|
|
8
8
|
GAS = "btc_gas",
|
|
9
9
|
GET_TRANSACTION_BY_HASH = "btc_getTransactionByHash",
|
|
10
10
|
BROADCAST_TRANSACTION = "btc_sendRawTransaction",
|
|
11
|
+
BROADCAST_TRANSACTION_PACKAGE = "btc_sendRawTransactionPackage",
|
|
11
12
|
TRANSACTION_PREIMAGE = "btc_preimage",
|
|
12
13
|
PUBLIC_KEY_INFO = "btc_publicKeyInfo",
|
|
13
14
|
GET_UTXOS = "btc_getUTXOs",
|
|
@@ -10,6 +10,7 @@ export declare enum WebSocketRequestOpcode {
|
|
|
10
10
|
GET_TRANSACTION_BY_HASH = 32,
|
|
11
11
|
GET_TRANSACTION_RECEIPT = 33,
|
|
12
12
|
BROADCAST_TRANSACTION = 34,
|
|
13
|
+
BROADCAST_TRANSACTION_PACKAGE = 39,
|
|
13
14
|
GET_PREIMAGE = 35,
|
|
14
15
|
GET_MEMPOOL_INFO = 36,
|
|
15
16
|
GET_PENDING_TRANSACTION = 37,
|
|
@@ -43,6 +44,7 @@ export declare enum WebSocketResponseOpcode {
|
|
|
43
44
|
TRANSACTION = 160,
|
|
44
45
|
TRANSACTION_RECEIPT = 161,
|
|
45
46
|
BROADCAST_RESULT = 162,
|
|
47
|
+
BROADCAST_PACKAGE_RESULT = 167,
|
|
46
48
|
PREIMAGE = 163,
|
|
47
49
|
MEMPOOL_INFO = 164,
|
|
48
50
|
PENDING_TRANSACTION = 165,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface SequentialBroadcastTxResult {
|
|
2
|
+
readonly txid: string;
|
|
3
|
+
readonly success: boolean;
|
|
4
|
+
readonly error?: string;
|
|
5
|
+
readonly peers?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface BroadcastedTransactionPackage {
|
|
8
|
+
readonly success: boolean;
|
|
9
|
+
readonly error?: string;
|
|
10
|
+
readonly testResults?: readonly TestMempoolAcceptResult[];
|
|
11
|
+
readonly packageResult?: PackageResult;
|
|
12
|
+
readonly sequentialResults?: readonly SequentialBroadcastTxResult[];
|
|
13
|
+
readonly fellBackToSequential?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface TestMempoolAcceptResult {
|
|
16
|
+
readonly txid: string;
|
|
17
|
+
readonly wtxid: string;
|
|
18
|
+
readonly allowed?: boolean;
|
|
19
|
+
readonly vsize?: number;
|
|
20
|
+
readonly packageError?: string;
|
|
21
|
+
readonly rejectReason?: string;
|
|
22
|
+
readonly rejectDetails?: string;
|
|
23
|
+
readonly fees?: TestMempoolAcceptFees;
|
|
24
|
+
}
|
|
25
|
+
export interface TestMempoolAcceptFees {
|
|
26
|
+
readonly base: number;
|
|
27
|
+
readonly effectiveFeerate: number;
|
|
28
|
+
readonly effectiveIncludes: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
export interface PackageTxResult {
|
|
31
|
+
readonly txid: string;
|
|
32
|
+
readonly otherWtxid?: string;
|
|
33
|
+
readonly vsize?: number;
|
|
34
|
+
readonly fees?: PackageTxFees;
|
|
35
|
+
readonly error?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface PackageTxFees {
|
|
38
|
+
readonly base: number;
|
|
39
|
+
readonly effectiveFeerate?: number;
|
|
40
|
+
readonly effectiveIncludes?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
export interface PackageResult {
|
|
43
|
+
readonly packageMsg: string;
|
|
44
|
+
readonly txResults: {
|
|
45
|
+
readonly [wtxid: string]: PackageTxResult;
|
|
46
|
+
};
|
|
47
|
+
readonly replacedTransactions?: readonly string[];
|
|
48
|
+
}
|
|
@@ -11,6 +11,7 @@ export declare class UTXOsManager {
|
|
|
11
11
|
getUTXOs({ address, isCSV, optimize, mergePendingUTXOs, filterSpentUTXOs, olderThan, }: RequestUTXOsParams): Promise<UTXOs>;
|
|
12
12
|
getUTXOsForAmount({ address, amount, csvAddress, optimize, mergePendingUTXOs, filterSpentUTXOs, throwErrors, olderThan, maxUTXOs, throwIfUTXOsLimitReached, }: RequestUTXOsParamsWithAmount): Promise<UTXOs>;
|
|
13
13
|
getMultipleUTXOs({ requests, mergePendingUTXOs, filterSpentUTXOs, }: RequestMultipleUTXOsParams): Promise<Record<string, UTXOs>>;
|
|
14
|
+
private selectUTXOsGreedily;
|
|
14
15
|
private fetchMultipleUTXOs;
|
|
15
16
|
private getAddressData;
|
|
16
17
|
private maybeFetchUTXOs;
|
package/build/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.8.
|
|
1
|
+
export declare const version = "1.8.3";
|
package/build/_version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.8.
|
|
1
|
+
export const version = '1.8.3';
|
|
@@ -4,6 +4,19 @@ import { decodeRevertData } from '../utils/RevertDecoder.js';
|
|
|
4
4
|
import { CallResultSerializer, NetworkName } from './CallResultSerializer.js';
|
|
5
5
|
import { TransactionHelper } from './TransactionHelpper.js';
|
|
6
6
|
const factory = new TransactionFactory();
|
|
7
|
+
function extractPackageFailures(packageResult) {
|
|
8
|
+
const failures = [];
|
|
9
|
+
const results = packageResult.txResults;
|
|
10
|
+
for (const [submittedTxid, result] of Object.entries(results)) {
|
|
11
|
+
if (result.error) {
|
|
12
|
+
failures.push(`tx ${submittedTxid} failed: ${result.error}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (failures.length === 0 && packageResult.packageMsg !== 'success') {
|
|
16
|
+
failures.push(`package rejected: ${packageResult.packageMsg}`);
|
|
17
|
+
}
|
|
18
|
+
return failures;
|
|
19
|
+
}
|
|
7
20
|
export class CallResult {
|
|
8
21
|
result;
|
|
9
22
|
accessList;
|
|
@@ -82,6 +95,9 @@ export class CallResult {
|
|
|
82
95
|
sendRawTransaction: () => {
|
|
83
96
|
return Promise.reject(new Error('Cannot broadcast from offline CallResult. Export signed transaction and broadcast online.'));
|
|
84
97
|
},
|
|
98
|
+
sendRawTransactionPackage: () => {
|
|
99
|
+
return Promise.reject(new Error('Cannot broadcast from offline CallResult. Export signed transaction and broadcast online.'));
|
|
100
|
+
},
|
|
85
101
|
getCSV1ForAddress: () => {
|
|
86
102
|
if (!data.csvAddress) {
|
|
87
103
|
throw new Error('CSV address not available in offline data');
|
|
@@ -241,33 +257,52 @@ export class CallResult {
|
|
|
241
257
|
};
|
|
242
258
|
}
|
|
243
259
|
async sendPresignedTransaction(signedTx) {
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
260
|
+
if (signedTx.utxoTracking.isP2WDA || !signedTx.fundingTransactionRaw) {
|
|
261
|
+
const tx = await this.#provider.sendRawTransaction(signedTx.interactionTransactionRaw, false);
|
|
262
|
+
if (!tx || tx.error) {
|
|
263
|
+
throw new Error(`Error sending transaction: ${tx?.error || 'Unknown error'}`);
|
|
247
264
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
throw new Error(`Error sending transaction: ${tx1?.error || 'Unknown error'}`);
|
|
265
|
+
if (!tx.result) {
|
|
266
|
+
throw new Error('No transaction ID returned');
|
|
251
267
|
}
|
|
252
|
-
if (!
|
|
253
|
-
throw new Error(`Error sending transaction: ${
|
|
268
|
+
if (!tx.success) {
|
|
269
|
+
throw new Error(`Error sending transaction: ${tx.result || 'Unknown error'}`);
|
|
270
|
+
}
|
|
271
|
+
this.#processUTXOTracking(signedTx);
|
|
272
|
+
return {
|
|
273
|
+
interactionAddress: signedTx.interactionAddress,
|
|
274
|
+
transactionId: tx.result,
|
|
275
|
+
peerAcknowledgements: tx.peers || 0,
|
|
276
|
+
newUTXOs: signedTx.nextUTXOs,
|
|
277
|
+
estimatedFees: signedTx.estimatedFees,
|
|
278
|
+
challengeSolution: signedTx.challengeSolution,
|
|
279
|
+
rawTransaction: signedTx.interactionTransactionRaw,
|
|
280
|
+
fundingUTXOs: signedTx.fundingUTXOs,
|
|
281
|
+
fundingInputUtxos: signedTx.fundingInputUtxos,
|
|
282
|
+
compiledTargetScript: signedTx.compiledTargetScript,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const result = await this.#provider.sendRawTransactionPackage([signedTx.fundingTransactionRaw, signedTx.interactionTransactionRaw], true);
|
|
286
|
+
if (!result.success) {
|
|
287
|
+
throw new Error(`Error sending transaction package: ${result.error || 'Unknown error'}`);
|
|
288
|
+
}
|
|
289
|
+
if (result.packageResult) {
|
|
290
|
+
const failures = extractPackageFailures(result.packageResult);
|
|
291
|
+
if (failures.length > 0) {
|
|
292
|
+
throw new Error(`Transaction package failed:\n${failures.join('\n')}`);
|
|
254
293
|
}
|
|
255
294
|
}
|
|
256
|
-
const
|
|
257
|
-
if (
|
|
258
|
-
throw new Error(`
|
|
259
|
-
}
|
|
260
|
-
if (!tx2.result) {
|
|
261
|
-
throw new Error('No transaction ID returned');
|
|
262
|
-
}
|
|
263
|
-
if (!tx2.success) {
|
|
264
|
-
throw new Error(`Error sending transaction: ${tx2.result || 'Unknown error'}`);
|
|
295
|
+
const interactionSeqResult = result.sequentialResults?.[1];
|
|
296
|
+
if (interactionSeqResult && !interactionSeqResult.success) {
|
|
297
|
+
throw new Error(`Interaction transaction failed: ${interactionSeqResult.error || 'Unknown error'}`);
|
|
265
298
|
}
|
|
299
|
+
const interactionTxId = interactionSeqResult?.txid || signedTx.interactionTransactionRaw;
|
|
300
|
+
const peers = interactionSeqResult?.peers || 0;
|
|
266
301
|
this.#processUTXOTracking(signedTx);
|
|
267
302
|
return {
|
|
268
303
|
interactionAddress: signedTx.interactionAddress,
|
|
269
|
-
transactionId:
|
|
270
|
-
peerAcknowledgements:
|
|
304
|
+
transactionId: interactionTxId,
|
|
305
|
+
peerAcknowledgements: peers,
|
|
271
306
|
newUTXOs: signedTx.nextUTXOs,
|
|
272
307
|
estimatedFees: signedTx.estimatedFees,
|
|
273
308
|
challengeSolution: signedTx.challengeSolution,
|
|
@@ -2,6 +2,7 @@ import { Network } from '@btc-vision/bitcoin';
|
|
|
2
2
|
import { Address, ChallengeSolution, IP2WSHAddress } from '@btc-vision/transaction';
|
|
3
3
|
import { UTXO, UTXOs } from '../../bitcoin/UTXOs.js';
|
|
4
4
|
import { BroadcastedTransaction } from '../../transactions/interfaces/BroadcastedTransaction.js';
|
|
5
|
+
import { BroadcastedTransactionPackage } from '../../transactions/interfaces/BroadcastedTransactionPackage.js';
|
|
5
6
|
import { RequestUTXOsParamsWithAmount } from '../../utxos/interfaces/IUTXOsManager.js';
|
|
6
7
|
export interface IUTXOManagerForCallResult {
|
|
7
8
|
getUTXOsForAmount(params: RequestUTXOsParamsWithAmount): Promise<UTXO[]>;
|
|
@@ -13,5 +14,6 @@ export interface IProviderForCallResult {
|
|
|
13
14
|
readonly utxoManager: IUTXOManagerForCallResult;
|
|
14
15
|
getChallenge(): Promise<ChallengeSolution>;
|
|
15
16
|
sendRawTransaction(tx: string, psbt: boolean): Promise<BroadcastedTransaction>;
|
|
17
|
+
sendRawTransactionPackage(txs: string[], isPackage?: boolean): Promise<BroadcastedTransactionPackage>;
|
|
16
18
|
getCSV1ForAddress(address: Address): IP2WSHAddress;
|
|
17
19
|
}
|
package/build/opnet.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ export * from './storage/interfaces/IStorageValue.js';
|
|
|
70
70
|
export * from './storage/StoredValue.js';
|
|
71
71
|
export * from './contracts/interfaces/IRawContract.js';
|
|
72
72
|
export * from './transactions/interfaces/BroadcastedTransaction.js';
|
|
73
|
+
export * from './transactions/interfaces/BroadcastedTransactionPackage.js';
|
|
73
74
|
export * from './transactions/interfaces/ITransaction.js';
|
|
74
75
|
export * from './transactions/interfaces/ITransactionReceipt.js';
|
|
75
76
|
export * from './transactions/metadata/TransactionReceipt.js';
|
package/build/opnet.js
CHANGED
|
@@ -70,6 +70,7 @@ export * from './storage/interfaces/IStorageValue.js';
|
|
|
70
70
|
export * from './storage/StoredValue.js';
|
|
71
71
|
export * from './contracts/interfaces/IRawContract.js';
|
|
72
72
|
export * from './transactions/interfaces/BroadcastedTransaction.js';
|
|
73
|
+
export * from './transactions/interfaces/BroadcastedTransactionPackage.js';
|
|
73
74
|
export * from './transactions/interfaces/ITransaction.js';
|
|
74
75
|
export * from './transactions/interfaces/ITransactionReceipt.js';
|
|
75
76
|
export * from './transactions/metadata/TransactionReceipt.js';
|