@buildonspark/spark-sdk 0.1.38 → 0.1.40
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 +12 -0
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/arm64-v8a/libspark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/arm64-v8a/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/armeabi-v7a/libspark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/armeabi-v7a/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86/libspark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86_64/libspark_frost.so +0 -0
- package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86_64/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/arm64-v8a/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/arm64-v8a/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/armeabi-v7a/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/armeabi-v7a/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86_64/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86_64/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/arm64-v8a/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/arm64-v8a/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/armeabi-v7a/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/armeabi-v7a/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86/libuniffi_spark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86_64/libspark_frost.so +0 -0
- package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86_64/libuniffi_spark_frost.so +0 -0
- package/dist/{RequestLightningSendInput-B4JdzclX.d.ts → RequestLightningSendInput-CJtcHOnu.d.ts} +1 -1
- package/dist/{RequestLightningSendInput-39_zGri6.d.cts → RequestLightningSendInput-DfmfqzZo.d.cts} +1 -1
- package/dist/address/index.d.cts +1 -1
- package/dist/address/index.d.ts +1 -1
- package/dist/address/index.js +2 -2
- package/dist/{chunk-W3EC5XSA.js → chunk-5MNQB2T4.js} +2 -2
- package/dist/chunk-ED3ZAFDI.js +784 -0
- package/dist/{chunk-VJTDG4BQ.js → chunk-HK6LPV6Z.js} +10 -1
- package/dist/{chunk-7WRK6WNJ.js → chunk-LHT4QTFK.js} +556 -41
- package/dist/{chunk-RAPBVYJY.js → chunk-RFCXPGDM.js} +26 -4
- package/dist/{chunk-DI7QXUQJ.js → chunk-W2VXS35Y.js} +4 -4
- package/dist/graphql/objects/index.d.cts +5 -4
- package/dist/graphql/objects/index.d.ts +5 -4
- package/dist/{index-CxAi2L8y.d.ts → index-BDEYgYxP.d.ts} +42 -4
- package/dist/{index-Dm17Ggfe.d.cts → index-CLdtdMU4.d.cts} +42 -4
- package/dist/index.cjs +1069 -40
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +33 -17
- package/dist/index.node.cjs +1069 -40
- package/dist/index.node.d.cts +6 -6
- package/dist/index.node.d.ts +6 -6
- package/dist/index.node.js +33 -17
- package/dist/native/index.cjs +1069 -40
- package/dist/native/index.d.cts +108 -5
- package/dist/native/index.d.ts +108 -5
- package/dist/native/index.js +1065 -40
- package/dist/{network-GFGEHkS4.d.cts → network-B10hBoHp.d.cts} +8 -1
- package/dist/{network-DobHpaV6.d.ts → network-CCgyIsGl.d.ts} +8 -1
- package/dist/services/config.cjs +29 -12
- package/dist/services/config.d.cts +4 -4
- package/dist/services/config.d.ts +4 -4
- package/dist/services/config.js +5 -5
- package/dist/services/connection.d.cts +4 -4
- package/dist/services/connection.d.ts +4 -4
- package/dist/services/connection.js +2 -2
- package/dist/services/index.cjs +30 -13
- package/dist/services/index.d.cts +4 -4
- package/dist/services/index.d.ts +4 -4
- package/dist/services/index.js +8 -8
- package/dist/services/lrc-connection.d.cts +4 -4
- package/dist/services/lrc-connection.d.ts +4 -4
- package/dist/services/lrc-connection.js +1 -1
- package/dist/services/token-transactions.cjs +1 -1
- package/dist/services/token-transactions.d.cts +4 -4
- package/dist/services/token-transactions.d.ts +4 -4
- package/dist/services/token-transactions.js +3 -3
- package/dist/services/wallet-config.d.cts +4 -4
- package/dist/services/wallet-config.d.ts +4 -4
- package/dist/signer/signer.cjs +23 -6
- package/dist/signer/signer.d.cts +3 -2
- package/dist/signer/signer.d.ts +3 -2
- package/dist/signer/signer.js +1 -1
- package/dist/{signer-DFGw9RRp.d.ts → signer-C5h1DpjF.d.ts} +4 -1
- package/dist/{signer-C1t40Wus.d.cts → signer-CYwn7h9U.d.cts} +4 -1
- package/dist/types/index.d.cts +4 -3
- package/dist/types/index.d.ts +4 -3
- package/dist/utils/index.cjs +891 -2
- package/dist/utils/index.d.cts +62 -6
- package/dist/utils/index.d.ts +62 -6
- package/dist/utils/index.js +23 -7
- package/package.json +1 -1
- package/src/services/deposit.ts +23 -5
- package/src/services/token-transactions.ts +1 -1
- package/src/services/transfer.ts +218 -11
- package/src/services/tree-creation.ts +29 -14
- package/src/signer/signer.ts +47 -5
- package/src/spark-wallet/spark-wallet.ts +430 -4
- package/src/tests/integration/swap.test.ts +225 -0
- package/src/tests/integration/tree-creation.test.ts +5 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/mempool.ts +26 -1
- package/src/utils/network.ts +15 -0
- package/src/utils/transaction.ts +22 -2
- package/src/utils/unilateral-exit.ts +729 -0
- package/dist/chunk-E5SL7XTO.js +0 -301
- package/dist/{chunk-LIP2K6KR.js → chunk-2CDJZQN4.js} +3 -3
- package/dist/{chunk-RGWBSZIO.js → chunk-I4JI6TYN.js} +4 -4
package/dist/native/index.cjs
CHANGED
|
@@ -1290,6 +1290,7 @@ var index_exports = {};
|
|
|
1290
1290
|
__export(index_exports, {
|
|
1291
1291
|
AuthenticationError: () => AuthenticationError,
|
|
1292
1292
|
ConfigurationError: () => ConfigurationError,
|
|
1293
|
+
DEFAULT_FEE_SATS: () => DEFAULT_FEE_SATS,
|
|
1293
1294
|
DefaultSparkSigner: () => ReactNativeSparkSigner,
|
|
1294
1295
|
InternalValidationError: () => InternalValidationError,
|
|
1295
1296
|
LRC_WALLET_NETWORK: () => LRC_WALLET_NETWORK,
|
|
@@ -1313,6 +1314,9 @@ __export(index_exports, {
|
|
|
1313
1314
|
collectResponses: () => collectResponses,
|
|
1314
1315
|
computeTaprootKeyNoScript: () => computeTaprootKeyNoScript,
|
|
1315
1316
|
computerLagrangeCoefficients: () => computerLagrangeCoefficients,
|
|
1317
|
+
constructFeeBumpTx: () => constructFeeBumpTx,
|
|
1318
|
+
constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
|
|
1319
|
+
constructUnilateralExitTxs: () => constructUnilateralExitTxs,
|
|
1316
1320
|
createRefundTx: () => createRefundTx,
|
|
1317
1321
|
createSigningCommitment: () => createSigningCommitment,
|
|
1318
1322
|
createSigningNonce: () => createSigningNonce,
|
|
@@ -1331,6 +1335,7 @@ __export(index_exports, {
|
|
|
1331
1335
|
getLatestDepositTxId: () => getLatestDepositTxId,
|
|
1332
1336
|
getNetwork: () => getNetwork,
|
|
1333
1337
|
getNetworkFromAddress: () => getNetworkFromAddress,
|
|
1338
|
+
getNetworkFromString: () => getNetworkFromString,
|
|
1334
1339
|
getNextTransactionSequence: () => getNextTransactionSequence,
|
|
1335
1340
|
getP2TRAddressFromPkScript: () => getP2TRAddressFromPkScript,
|
|
1336
1341
|
getP2TRAddressFromPublicKey: () => getP2TRAddressFromPublicKey,
|
|
@@ -1346,7 +1351,10 @@ __export(index_exports, {
|
|
|
1346
1351
|
getTxFromRawTxHex: () => getTxFromRawTxHex,
|
|
1347
1352
|
getTxId: () => getTxId,
|
|
1348
1353
|
getTxIdNoReverse: () => getTxIdNoReverse,
|
|
1354
|
+
isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
|
|
1355
|
+
isTxBroadcast: () => isTxBroadcast,
|
|
1349
1356
|
lastKeyWithTarget: () => lastKeyWithTarget,
|
|
1357
|
+
maybeApplyFee: () => maybeApplyFee,
|
|
1350
1358
|
modInverse: () => modInverse,
|
|
1351
1359
|
proofOfPossessionMessageHashForDepositAddress: () => proofOfPossessionMessageHashForDepositAddress,
|
|
1352
1360
|
recoverSecret: () => recoverSecret,
|
|
@@ -2106,11 +2114,9 @@ function decodeBytesToSigningCommitment(bytes2) {
|
|
|
2106
2114
|
}
|
|
2107
2115
|
|
|
2108
2116
|
// src/signer/signer.ts
|
|
2109
|
-
var import_lrc20_sdk = require("@buildonspark/lrc20-sdk");
|
|
2110
|
-
var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
|
|
2111
2117
|
var import_secp256k16 = require("@bitcoinerlab/secp256k1");
|
|
2118
|
+
var import_lrc20_sdk = require("@buildonspark/lrc20-sdk");
|
|
2112
2119
|
var import_sha2 = require("@noble/hashes/sha2");
|
|
2113
|
-
var import_lrc20_sdk3 = require("@buildonspark/lrc20-sdk");
|
|
2114
2120
|
var sparkFrostModule = void 0;
|
|
2115
2121
|
var getSparkFrostModule = async () => {
|
|
2116
2122
|
if (isReactNative) {
|
|
@@ -2576,16 +2582,16 @@ var DefaultSparkSigner = class {
|
|
|
2576
2582
|
}
|
|
2577
2583
|
if (receipt) {
|
|
2578
2584
|
const receiptPrivateKey = this.getReceiptPrivateKey(receipt);
|
|
2579
|
-
const tweakedKeyPair = (0,
|
|
2585
|
+
const tweakedKeyPair = (0, import_lrc20_sdk.fromPrivateKey)(import_buffer.Buffer.from(receiptPrivateKey));
|
|
2580
2586
|
psbt.signInput(input, tweakedKeyPair, sighashTypes);
|
|
2581
2587
|
return psbt;
|
|
2582
2588
|
}
|
|
2583
|
-
const keypair = (0,
|
|
2589
|
+
const keypair = (0, import_lrc20_sdk.fromPrivateKey)(import_buffer.Buffer.from(this.identityKey.privateKey));
|
|
2584
2590
|
psbt.signInput(input, keypair, sighashTypes);
|
|
2585
2591
|
return psbt;
|
|
2586
2592
|
}
|
|
2587
2593
|
getReceiptPrivateKey(receipt) {
|
|
2588
|
-
const pxh =
|
|
2594
|
+
const pxh = import_lrc20_sdk.Receipt.receiptHash(receipt);
|
|
2589
2595
|
let innerKey = this.identityKey.publicKey;
|
|
2590
2596
|
let privateKey = this.identityKey.privateKey;
|
|
2591
2597
|
if (innerKey[0] === 3) {
|
|
@@ -2596,6 +2602,25 @@ var DefaultSparkSigner = class {
|
|
|
2596
2602
|
const receiptProof = (0, import_secp256k16.privateAdd)(privateKey, pxhPubkey);
|
|
2597
2603
|
return import_buffer.Buffer.from(receiptProof);
|
|
2598
2604
|
}
|
|
2605
|
+
signTransactionIndex(tx, index, publicKey) {
|
|
2606
|
+
let privateKey;
|
|
2607
|
+
if ((0, import_utils4.equalBytes)(publicKey, this.identityKey?.publicKey ?? new Uint8Array())) {
|
|
2608
|
+
privateKey = this.identityKey?.privateKey;
|
|
2609
|
+
} else if ((0, import_utils4.equalBytes)(publicKey, this.depositKey?.publicKey ?? new Uint8Array())) {
|
|
2610
|
+
privateKey = this.depositKey?.privateKey;
|
|
2611
|
+
} else {
|
|
2612
|
+
privateKey = (0, import_utils4.hexToBytes)(
|
|
2613
|
+
this.publicKeyToPrivateKeyMap.get((0, import_utils4.bytesToHex)(publicKey)) ?? ""
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
if (!privateKey) {
|
|
2617
|
+
throw new ValidationError("Private key not found for public key", {
|
|
2618
|
+
field: "privateKey",
|
|
2619
|
+
value: (0, import_utils4.bytesToHex)(publicKey)
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
tx.signIdx(privateKey, index);
|
|
2623
|
+
}
|
|
2599
2624
|
};
|
|
2600
2625
|
|
|
2601
2626
|
// src/signer/signer.react-native.ts
|
|
@@ -17706,7 +17731,7 @@ init_buffer();
|
|
|
17706
17731
|
|
|
17707
17732
|
// src/utils/network.ts
|
|
17708
17733
|
init_buffer();
|
|
17709
|
-
var
|
|
17734
|
+
var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
|
|
17710
17735
|
var btc = __toESM(require("@scure/btc-signer"), 1);
|
|
17711
17736
|
var bitcoin = __toESM(require("bitcoinjs-lib"), 1);
|
|
17712
17737
|
var Network2 = /* @__PURE__ */ ((Network5) => {
|
|
@@ -17740,11 +17765,11 @@ var LRC_WALLET_NETWORK = Object.freeze({
|
|
|
17740
17765
|
[4 /* LOCAL */]: bitcoin.networks.regtest
|
|
17741
17766
|
});
|
|
17742
17767
|
var LRC_WALLET_NETWORK_TYPE = Object.freeze({
|
|
17743
|
-
[0 /* MAINNET */]:
|
|
17744
|
-
[1 /* TESTNET */]:
|
|
17745
|
-
[2 /* SIGNET */]:
|
|
17746
|
-
[3 /* REGTEST */]:
|
|
17747
|
-
[4 /* LOCAL */]:
|
|
17768
|
+
[0 /* MAINNET */]: import_lrc20_sdk2.NetworkType.MAINNET,
|
|
17769
|
+
[1 /* TESTNET */]: import_lrc20_sdk2.NetworkType.TESTNET,
|
|
17770
|
+
[2 /* SIGNET */]: import_lrc20_sdk2.NetworkType.TESTNET,
|
|
17771
|
+
[3 /* REGTEST */]: import_lrc20_sdk2.NetworkType.REGTEST,
|
|
17772
|
+
[4 /* LOCAL */]: import_lrc20_sdk2.NetworkType.LOCAL
|
|
17748
17773
|
});
|
|
17749
17774
|
function getNetworkFromAddress(address2) {
|
|
17750
17775
|
try {
|
|
@@ -17767,6 +17792,14 @@ function getNetworkFromAddress(address2) {
|
|
|
17767
17792
|
}
|
|
17768
17793
|
return null;
|
|
17769
17794
|
}
|
|
17795
|
+
function getNetworkFromString(network) {
|
|
17796
|
+
const net = (network ?? "REGTEST").toUpperCase();
|
|
17797
|
+
if (net === "MAINNET") return 0 /* MAINNET */;
|
|
17798
|
+
if (net === "TESTNET") return 1 /* TESTNET */;
|
|
17799
|
+
if (net === "SIGNET") return 2 /* SIGNET */;
|
|
17800
|
+
if (net === "LOCAL") return 4 /* LOCAL */;
|
|
17801
|
+
return 3 /* REGTEST */;
|
|
17802
|
+
}
|
|
17770
17803
|
|
|
17771
17804
|
// src/services/wallet-config.ts
|
|
17772
17805
|
init_buffer();
|
|
@@ -19324,8 +19357,20 @@ function getTxIdNoReverse(tx) {
|
|
|
19324
19357
|
init_buffer();
|
|
19325
19358
|
var import_btc_signer = require("@scure/btc-signer");
|
|
19326
19359
|
var TIME_LOCK_INTERVAL = 100;
|
|
19360
|
+
var ESTIMATED_TX_SIZE = 191;
|
|
19361
|
+
var DEFAULT_SATS_PER_VBYTE = 5;
|
|
19362
|
+
var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
|
|
19363
|
+
function maybeApplyFee(amount) {
|
|
19364
|
+
if (amount > BigInt(DEFAULT_FEE_SATS)) {
|
|
19365
|
+
return amount - BigInt(DEFAULT_FEE_SATS);
|
|
19366
|
+
}
|
|
19367
|
+
return amount;
|
|
19368
|
+
}
|
|
19327
19369
|
function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
|
|
19328
|
-
const newRefundTx = new import_btc_signer.Transaction({
|
|
19370
|
+
const newRefundTx = new import_btc_signer.Transaction({
|
|
19371
|
+
version: 3,
|
|
19372
|
+
allowUnknownOutputs: true
|
|
19373
|
+
});
|
|
19329
19374
|
newRefundTx.addInput({
|
|
19330
19375
|
...nodeOutPoint,
|
|
19331
19376
|
sequence
|
|
@@ -19354,7 +19399,7 @@ function getNextTransactionSequence(currSequence, forRefresh) {
|
|
|
19354
19399
|
needRefresh: true
|
|
19355
19400
|
};
|
|
19356
19401
|
}
|
|
19357
|
-
if (nextTimelock
|
|
19402
|
+
if (nextTimelock < 0) {
|
|
19358
19403
|
throw new ValidationError("timelock interval is less than or equal to 0", {
|
|
19359
19404
|
field: "nextTimelock",
|
|
19360
19405
|
value: nextTimelock
|
|
@@ -19465,6 +19510,32 @@ var BaseTransferService = class {
|
|
|
19465
19510
|
}
|
|
19466
19511
|
return updatedTransfer;
|
|
19467
19512
|
}
|
|
19513
|
+
async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
|
|
19514
|
+
const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
|
|
19515
|
+
transfer.id,
|
|
19516
|
+
transfer.receiverIdentityPublicKey,
|
|
19517
|
+
leaves,
|
|
19518
|
+
refundSignatureMap
|
|
19519
|
+
);
|
|
19520
|
+
const transferPackage = await this.prepareTransferPackage(
|
|
19521
|
+
transfer.id,
|
|
19522
|
+
keyTweakInputMap,
|
|
19523
|
+
leaves,
|
|
19524
|
+
transfer.receiverIdentityPublicKey
|
|
19525
|
+
);
|
|
19526
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
19527
|
+
this.config.getCoordinatorAddress()
|
|
19528
|
+
);
|
|
19529
|
+
const response = await sparkClient.finalize_transfer_with_transfer_package({
|
|
19530
|
+
transferId: transfer.id,
|
|
19531
|
+
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
19532
|
+
transferPackage
|
|
19533
|
+
});
|
|
19534
|
+
if (!response.transfer) {
|
|
19535
|
+
throw new ValidationError("No transfer response from operator");
|
|
19536
|
+
}
|
|
19537
|
+
return response.transfer;
|
|
19538
|
+
}
|
|
19468
19539
|
async sendTransferWithKeyTweaks(leaves, receiverIdentityPubkey) {
|
|
19469
19540
|
const transferID = (0, import_uuidv7.uuidv7)();
|
|
19470
19541
|
const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
|
|
@@ -20222,9 +20293,20 @@ var TransferService = class extends BaseTransferService {
|
|
|
20222
20293
|
if (!input) {
|
|
20223
20294
|
throw Error("Could not fetch tx input");
|
|
20224
20295
|
}
|
|
20225
|
-
const newTx = new import_btc_signer2.Transaction({ allowUnknownOutputs: true });
|
|
20226
|
-
|
|
20227
|
-
|
|
20296
|
+
const newTx = new import_btc_signer2.Transaction({ version: 3, allowUnknownOutputs: true });
|
|
20297
|
+
const originalOutput = nodeTx.getOutput(0);
|
|
20298
|
+
if (!originalOutput) {
|
|
20299
|
+
throw Error("Could not get original output");
|
|
20300
|
+
}
|
|
20301
|
+
newTx.addOutput({
|
|
20302
|
+
script: originalOutput.script,
|
|
20303
|
+
amount: originalOutput.amount
|
|
20304
|
+
});
|
|
20305
|
+
for (let j = 1; j < nodeTx.outputsLength; j++) {
|
|
20306
|
+
const additionalOutput = nodeTx.getOutput(j);
|
|
20307
|
+
if (additionalOutput) {
|
|
20308
|
+
newTx.addOutput(additionalOutput);
|
|
20309
|
+
}
|
|
20228
20310
|
}
|
|
20229
20311
|
if (i === 0) {
|
|
20230
20312
|
const currSequence = input.sequence;
|
|
@@ -20251,9 +20333,23 @@ var TransferService = class extends BaseTransferService {
|
|
|
20251
20333
|
throw Error("leaf does not have refund tx");
|
|
20252
20334
|
}
|
|
20253
20335
|
const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
|
|
20254
|
-
const newRefundTx = new import_btc_signer2.Transaction({
|
|
20255
|
-
|
|
20256
|
-
|
|
20336
|
+
const newRefundTx = new import_btc_signer2.Transaction({
|
|
20337
|
+
version: 3,
|
|
20338
|
+
allowUnknownOutputs: true
|
|
20339
|
+
});
|
|
20340
|
+
const originalRefundOutput = refundTx.getOutput(0);
|
|
20341
|
+
if (!originalRefundOutput) {
|
|
20342
|
+
throw Error("Could not get original refund output");
|
|
20343
|
+
}
|
|
20344
|
+
newRefundTx.addOutput({
|
|
20345
|
+
script: originalRefundOutput.script,
|
|
20346
|
+
amount: originalRefundOutput.amount
|
|
20347
|
+
});
|
|
20348
|
+
for (let j = 1; j < refundTx.outputsLength; j++) {
|
|
20349
|
+
const additionalOutput = refundTx.getOutput(j);
|
|
20350
|
+
if (additionalOutput) {
|
|
20351
|
+
newRefundTx.addOutput(additionalOutput);
|
|
20352
|
+
}
|
|
20257
20353
|
}
|
|
20258
20354
|
const refundTxInput = refundTx.getInput(0);
|
|
20259
20355
|
if (!refundTxInput) {
|
|
@@ -20362,10 +20458,11 @@ var TransferService = class extends BaseTransferService {
|
|
|
20362
20458
|
nodeTxSignature: leafSignature,
|
|
20363
20459
|
refundTxSignature: refundSignature
|
|
20364
20460
|
});
|
|
20365
|
-
|
|
20461
|
+
const result = await sparkClient.finalize_node_signatures({
|
|
20366
20462
|
intent: 3 /* REFRESH */,
|
|
20367
20463
|
nodeSignatures
|
|
20368
20464
|
});
|
|
20465
|
+
return result;
|
|
20369
20466
|
}
|
|
20370
20467
|
async extendTimelock(node, signingPubKey) {
|
|
20371
20468
|
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
@@ -20376,9 +20473,20 @@ var TransferService = class extends BaseTransferService {
|
|
|
20376
20473
|
index: 0
|
|
20377
20474
|
};
|
|
20378
20475
|
const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
|
|
20379
|
-
const newNodeTx = new import_btc_signer2.Transaction({
|
|
20476
|
+
const newNodeTx = new import_btc_signer2.Transaction({
|
|
20477
|
+
version: 3,
|
|
20478
|
+
allowUnknownOutputs: true
|
|
20479
|
+
});
|
|
20380
20480
|
newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
|
|
20381
|
-
|
|
20481
|
+
const originalOutput = nodeTx.getOutput(0);
|
|
20482
|
+
if (!originalOutput) {
|
|
20483
|
+
throw Error("Could not get original node output");
|
|
20484
|
+
}
|
|
20485
|
+
newNodeTx.addOutput({
|
|
20486
|
+
script: originalOutput.script,
|
|
20487
|
+
amount: originalOutput.amount
|
|
20488
|
+
// feeReducedAmount,
|
|
20489
|
+
});
|
|
20382
20490
|
newNodeTx.addOutput(getEphemeralAnchorOutput());
|
|
20383
20491
|
const newRefundOutPoint = {
|
|
20384
20492
|
txid: (0, import_utils8.hexToBytes)(getTxId(newNodeTx)),
|
|
@@ -20392,11 +20500,16 @@ var TransferService = class extends BaseTransferService {
|
|
|
20392
20500
|
initialSequence(),
|
|
20393
20501
|
newRefundOutPoint,
|
|
20394
20502
|
amountSats,
|
|
20503
|
+
// feeReducedRefundAmount,
|
|
20395
20504
|
signingPubKey,
|
|
20396
20505
|
this.config.getNetwork()
|
|
20397
20506
|
);
|
|
20398
20507
|
const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
|
|
20399
|
-
const refundSighash = getSigHashFromTx(
|
|
20508
|
+
const refundSighash = getSigHashFromTx(
|
|
20509
|
+
newRefundTx,
|
|
20510
|
+
0,
|
|
20511
|
+
newNodeTx.getOutput(0)
|
|
20512
|
+
);
|
|
20400
20513
|
const newNodeSigningJob = {
|
|
20401
20514
|
signingPublicKey: signingPubKey,
|
|
20402
20515
|
rawTx: newNodeTx.toBytes(),
|
|
@@ -20470,6 +20583,94 @@ var TransferService = class extends BaseTransferService {
|
|
|
20470
20583
|
]
|
|
20471
20584
|
});
|
|
20472
20585
|
}
|
|
20586
|
+
async refreshTimelockRefundTx(node, signingPubKey) {
|
|
20587
|
+
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
20588
|
+
const refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
20589
|
+
const currSequence = refundTx.getInput(0).sequence || 0;
|
|
20590
|
+
const { nextSequence } = getNextTransactionSequence(currSequence);
|
|
20591
|
+
const newRefundTx = new import_btc_signer2.Transaction({
|
|
20592
|
+
version: 3,
|
|
20593
|
+
allowUnknownOutputs: true
|
|
20594
|
+
});
|
|
20595
|
+
const originalRefundOutput = refundTx.getOutput(0);
|
|
20596
|
+
if (!originalRefundOutput) {
|
|
20597
|
+
throw Error("Could not get original refund output");
|
|
20598
|
+
}
|
|
20599
|
+
newRefundTx.addOutput({
|
|
20600
|
+
script: originalRefundOutput.script,
|
|
20601
|
+
amount: originalRefundOutput.amount
|
|
20602
|
+
});
|
|
20603
|
+
for (let j = 1; j < refundTx.outputsLength; j++) {
|
|
20604
|
+
const additionalOutput = refundTx.getOutput(j);
|
|
20605
|
+
if (additionalOutput) {
|
|
20606
|
+
newRefundTx.addOutput(additionalOutput);
|
|
20607
|
+
}
|
|
20608
|
+
}
|
|
20609
|
+
const refundTxInput = refundTx.getInput(0);
|
|
20610
|
+
if (!refundTxInput) {
|
|
20611
|
+
throw Error("refund tx doesn't have input");
|
|
20612
|
+
}
|
|
20613
|
+
newRefundTx.addInput({
|
|
20614
|
+
...refundTxInput,
|
|
20615
|
+
sequence: nextSequence
|
|
20616
|
+
});
|
|
20617
|
+
const refundSigningJob = {
|
|
20618
|
+
signingPublicKey: signingPubKey,
|
|
20619
|
+
rawTx: newRefundTx.toBytes(),
|
|
20620
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
20621
|
+
};
|
|
20622
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
20623
|
+
this.config.getCoordinatorAddress()
|
|
20624
|
+
);
|
|
20625
|
+
const response = await sparkClient.refresh_timelock({
|
|
20626
|
+
leafId: node.id,
|
|
20627
|
+
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
20628
|
+
signingJobs: [refundSigningJob]
|
|
20629
|
+
});
|
|
20630
|
+
if (response.signingResults.length !== 1) {
|
|
20631
|
+
throw Error(
|
|
20632
|
+
`Expected 1 signing result, got ${response.signingResults.length}`
|
|
20633
|
+
);
|
|
20634
|
+
}
|
|
20635
|
+
const signingResult = response.signingResults[0];
|
|
20636
|
+
if (!signingResult || !refundSigningJob.signingNonceCommitment) {
|
|
20637
|
+
throw Error("Signing result or nonce commitment does not exist");
|
|
20638
|
+
}
|
|
20639
|
+
const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
|
|
20640
|
+
const txOut = nodeTx.getOutput(0);
|
|
20641
|
+
const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
|
|
20642
|
+
const userSignature = await this.config.signer.signFrost({
|
|
20643
|
+
message: rawTxSighash,
|
|
20644
|
+
privateAsPubKey: signingPubKey,
|
|
20645
|
+
publicKey: signingPubKey,
|
|
20646
|
+
verifyingKey: signingResult.verifyingKey,
|
|
20647
|
+
selfCommitment: refundSigningJob.signingNonceCommitment,
|
|
20648
|
+
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
20649
|
+
adaptorPubKey: new Uint8Array()
|
|
20650
|
+
});
|
|
20651
|
+
const signature = await this.config.signer.aggregateFrost({
|
|
20652
|
+
message: rawTxSighash,
|
|
20653
|
+
statechainSignatures: signingResult.signingResult?.signatureShares,
|
|
20654
|
+
statechainPublicKeys: signingResult.signingResult?.publicKeys,
|
|
20655
|
+
verifyingKey: signingResult.verifyingKey,
|
|
20656
|
+
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
20657
|
+
selfCommitment: refundSigningJob.signingNonceCommitment,
|
|
20658
|
+
publicKey: signingPubKey,
|
|
20659
|
+
selfSignature: userSignature,
|
|
20660
|
+
adaptorPubKey: new Uint8Array()
|
|
20661
|
+
});
|
|
20662
|
+
const result = await sparkClient.finalize_node_signatures({
|
|
20663
|
+
intent: 3 /* REFRESH */,
|
|
20664
|
+
nodeSignatures: [
|
|
20665
|
+
{
|
|
20666
|
+
nodeId: node.id,
|
|
20667
|
+
nodeTxSignature: new Uint8Array(),
|
|
20668
|
+
refundTxSignature: signature
|
|
20669
|
+
}
|
|
20670
|
+
]
|
|
20671
|
+
});
|
|
20672
|
+
return result;
|
|
20673
|
+
}
|
|
20473
20674
|
};
|
|
20474
20675
|
|
|
20475
20676
|
// src/services/coop-exit.ts
|
|
@@ -20773,7 +20974,7 @@ var DepositService = class {
|
|
|
20773
20974
|
depositTx,
|
|
20774
20975
|
vout
|
|
20775
20976
|
}) {
|
|
20776
|
-
const rootTx = new import_btc_signer4.Transaction();
|
|
20977
|
+
const rootTx = new import_btc_signer4.Transaction({ version: 3 });
|
|
20777
20978
|
const output = depositTx.getOutput(vout);
|
|
20778
20979
|
if (!output) {
|
|
20779
20980
|
throw new ValidationError("Invalid deposit transaction output", {
|
|
@@ -20791,17 +20992,19 @@ var DepositService = class {
|
|
|
20791
20992
|
expected: "Output with script and amount"
|
|
20792
20993
|
});
|
|
20793
20994
|
}
|
|
20995
|
+
let outputAmount = amount;
|
|
20794
20996
|
rootTx.addInput({
|
|
20795
20997
|
txid: getTxId(depositTx),
|
|
20796
20998
|
index: vout
|
|
20797
20999
|
});
|
|
20798
21000
|
rootTx.addOutput({
|
|
20799
21001
|
script,
|
|
20800
|
-
amount
|
|
21002
|
+
amount: outputAmount
|
|
20801
21003
|
});
|
|
21004
|
+
rootTx.addOutput(getEphemeralAnchorOutput());
|
|
20802
21005
|
const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
20803
21006
|
const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
|
|
20804
|
-
const refundTx = new import_btc_signer4.Transaction();
|
|
21007
|
+
const refundTx = new import_btc_signer4.Transaction({ version: 3 });
|
|
20805
21008
|
const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
|
|
20806
21009
|
refundTx.addInput({
|
|
20807
21010
|
txid: getTxId(rootTx),
|
|
@@ -20816,10 +21019,11 @@ var DepositService = class {
|
|
|
20816
21019
|
const refundPkScript = btc3.OutScript.encode(refundAddress);
|
|
20817
21020
|
refundTx.addOutput({
|
|
20818
21021
|
script: refundPkScript,
|
|
20819
|
-
amount
|
|
21022
|
+
amount: outputAmount
|
|
20820
21023
|
});
|
|
21024
|
+
refundTx.addOutput(getEphemeralAnchorOutput());
|
|
20821
21025
|
const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
20822
|
-
const refundTxSighash = getSigHashFromTx(refundTx, 0,
|
|
21026
|
+
const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
|
|
20823
21027
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
20824
21028
|
this.config.getCoordinatorAddress()
|
|
20825
21029
|
);
|
|
@@ -25327,7 +25531,7 @@ function isValidPublicKey(publicKey) {
|
|
|
25327
25531
|
}
|
|
25328
25532
|
|
|
25329
25533
|
// src/services/token-transactions.ts
|
|
25330
|
-
var MAX_TOKEN_OUTPUTS =
|
|
25534
|
+
var MAX_TOKEN_OUTPUTS = 500;
|
|
25331
25535
|
var TokenTransactionService = class {
|
|
25332
25536
|
config;
|
|
25333
25537
|
connectionManager;
|
|
@@ -25892,6 +26096,12 @@ var import_utils16 = require("@noble/curves/abstract/utils");
|
|
|
25892
26096
|
var import_sha212 = require("@noble/hashes/sha2");
|
|
25893
26097
|
var import_btc_signer5 = require("@scure/btc-signer");
|
|
25894
26098
|
var INITIAL_TIME_LOCK3 = 2e3;
|
|
26099
|
+
function maybeApplyFee3(amount) {
|
|
26100
|
+
if (amount > BigInt(DEFAULT_FEE_SATS)) {
|
|
26101
|
+
return amount - BigInt(DEFAULT_FEE_SATS);
|
|
26102
|
+
}
|
|
26103
|
+
return amount;
|
|
26104
|
+
}
|
|
25895
26105
|
var TreeCreationService = class {
|
|
25896
26106
|
config;
|
|
25897
26107
|
connectionManager;
|
|
@@ -26090,7 +26300,7 @@ var TreeCreationService = class {
|
|
|
26090
26300
|
refundTxSigningJob: void 0,
|
|
26091
26301
|
children: []
|
|
26092
26302
|
};
|
|
26093
|
-
const tx = new import_btc_signer5.Transaction();
|
|
26303
|
+
const tx = new import_btc_signer5.Transaction({ version: 3 });
|
|
26094
26304
|
tx.addInput({
|
|
26095
26305
|
txid: getTxId(parentTx),
|
|
26096
26306
|
index: vout
|
|
@@ -26102,6 +26312,7 @@ var TreeCreationService = class {
|
|
|
26102
26312
|
tx.addOutput({
|
|
26103
26313
|
script: parentTxOut.script,
|
|
26104
26314
|
amount: parentTxOut.amount
|
|
26315
|
+
// maybeApplyFee(parentTxOut.amount),
|
|
26105
26316
|
});
|
|
26106
26317
|
tx.addOutput(getEphemeralAnchorOutput());
|
|
26107
26318
|
const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
@@ -26118,7 +26329,7 @@ var TreeCreationService = class {
|
|
|
26118
26329
|
refundTxSigningJob: void 0,
|
|
26119
26330
|
children: []
|
|
26120
26331
|
};
|
|
26121
|
-
const childTx = new import_btc_signer5.Transaction();
|
|
26332
|
+
const childTx = new import_btc_signer5.Transaction({ version: 3 });
|
|
26122
26333
|
childTx.addInput({
|
|
26123
26334
|
txid: getTxId(tx),
|
|
26124
26335
|
index: 0,
|
|
@@ -26127,6 +26338,7 @@ var TreeCreationService = class {
|
|
|
26127
26338
|
childTx.addOutput({
|
|
26128
26339
|
script: parentTxOut.script,
|
|
26129
26340
|
amount: parentTxOut.amount
|
|
26341
|
+
// maybeApplyFee(parentTxOut.amount),
|
|
26130
26342
|
});
|
|
26131
26343
|
childTx.addOutput(getEphemeralAnchorOutput());
|
|
26132
26344
|
const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
@@ -26137,7 +26349,7 @@ var TreeCreationService = class {
|
|
|
26137
26349
|
};
|
|
26138
26350
|
childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
|
|
26139
26351
|
childCreationNode.nodeTxSigningJob = childSigningJob;
|
|
26140
|
-
const refundTx = new import_btc_signer5.Transaction();
|
|
26352
|
+
const refundTx = new import_btc_signer5.Transaction({ version: 3 });
|
|
26141
26353
|
refundTx.addInput({
|
|
26142
26354
|
txid: getTxId(childTx),
|
|
26143
26355
|
index: 0,
|
|
@@ -26153,8 +26365,9 @@ var TreeCreationService = class {
|
|
|
26153
26365
|
const refundPkScript = import_btc_signer5.OutScript.encode(refundAddress);
|
|
26154
26366
|
refundTx.addOutput({
|
|
26155
26367
|
script: refundPkScript,
|
|
26156
|
-
amount: parentTxOut.amount
|
|
26368
|
+
amount: maybeApplyFee3(parentTxOut.amount)
|
|
26157
26369
|
});
|
|
26370
|
+
refundTx.addOutput(getEphemeralAnchorOutput());
|
|
26158
26371
|
const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
26159
26372
|
const refundSigningJob = {
|
|
26160
26373
|
signingPublicKey: node.signingPublicKey,
|
|
@@ -26171,7 +26384,7 @@ var TreeCreationService = class {
|
|
|
26171
26384
|
if (!parentTxOutput?.script || !parentTxOutput?.amount) {
|
|
26172
26385
|
throw new Error("parentTxOutput is undefined");
|
|
26173
26386
|
}
|
|
26174
|
-
const rootNodeTx = new import_btc_signer5.Transaction();
|
|
26387
|
+
const rootNodeTx = new import_btc_signer5.Transaction({ version: 3 });
|
|
26175
26388
|
rootNodeTx.addInput({
|
|
26176
26389
|
txid: getTxId(parentTx),
|
|
26177
26390
|
index: vout
|
|
@@ -26186,10 +26399,10 @@ var TreeCreationService = class {
|
|
|
26186
26399
|
rootNodeTx.addOutput({
|
|
26187
26400
|
script: childPkScript,
|
|
26188
26401
|
amount: parentTxOutput.amount / 2n
|
|
26402
|
+
// feeAdjustedAmount / 2n,
|
|
26189
26403
|
});
|
|
26190
26404
|
}
|
|
26191
|
-
|
|
26192
|
-
rootNodeTx.addOutput(anchor);
|
|
26405
|
+
rootNodeTx.addOutput(getEphemeralAnchorOutput());
|
|
26193
26406
|
const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
26194
26407
|
const rootNodeSigningJob = {
|
|
26195
26408
|
signingPublicKey: root.signingPublicKey,
|
|
@@ -26340,7 +26553,7 @@ var TreeCreationService = class {
|
|
|
26340
26553
|
};
|
|
26341
26554
|
|
|
26342
26555
|
// src/spark-wallet/spark-wallet.ts
|
|
26343
|
-
var
|
|
26556
|
+
var import_lrc20_sdk3 = require("@buildonspark/lrc20-sdk");
|
|
26344
26557
|
var import_sha213 = require("@noble/hashes/sha2");
|
|
26345
26558
|
var import_eventemitter3 = require("eventemitter3");
|
|
26346
26559
|
|
|
@@ -27249,7 +27462,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
27249
27462
|
}
|
|
27250
27463
|
await this.initWalletFromSeed(seed, accountNumber);
|
|
27251
27464
|
const network = this.config.getNetwork();
|
|
27252
|
-
this.lrc20Wallet = await
|
|
27465
|
+
this.lrc20Wallet = await import_lrc20_sdk3.LRCWallet.create(
|
|
27253
27466
|
LRC_WALLET_NETWORK[network],
|
|
27254
27467
|
LRC_WALLET_NETWORK_TYPE[network],
|
|
27255
27468
|
this.config.lrc20ApiConfig,
|
|
@@ -27448,7 +27661,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
27448
27661
|
adaptorPrivateKey
|
|
27449
27662
|
);
|
|
27450
27663
|
}
|
|
27451
|
-
await this.transferService.
|
|
27664
|
+
await this.transferService.deliverTransferPackage(
|
|
27452
27665
|
transfer,
|
|
27453
27666
|
leafKeyTweaks,
|
|
27454
27667
|
signatureMap
|
|
@@ -29133,6 +29346,112 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
29133
29346
|
}
|
|
29134
29347
|
return this.config.signer.validateMessageWithIdentityKey(hash, signature);
|
|
29135
29348
|
}
|
|
29349
|
+
/**
|
|
29350
|
+
* Signs a transaction with wallet keys.
|
|
29351
|
+
*
|
|
29352
|
+
* @param {string} txHex - The transaction hex to sign
|
|
29353
|
+
* @param {string} keyType - The type of key to use for signing ("identity", "deposit", or "auto-detect")
|
|
29354
|
+
* @returns {Promise<string>} The signed transaction hex
|
|
29355
|
+
*/
|
|
29356
|
+
async signTransaction(txHex, keyType = "auto-detect") {
|
|
29357
|
+
try {
|
|
29358
|
+
const tx = import_btc_signer7.Transaction.fromRaw((0, import_utils21.hexToBytes)(txHex));
|
|
29359
|
+
let publicKey;
|
|
29360
|
+
switch (keyType.toLowerCase()) {
|
|
29361
|
+
case "identity":
|
|
29362
|
+
publicKey = await this.config.signer.getIdentityPublicKey();
|
|
29363
|
+
break;
|
|
29364
|
+
case "deposit":
|
|
29365
|
+
publicKey = await this.config.signer.getDepositSigningKey();
|
|
29366
|
+
break;
|
|
29367
|
+
case "auto-detect":
|
|
29368
|
+
default:
|
|
29369
|
+
const detectedKey = await this.detectKeyForTransaction(tx);
|
|
29370
|
+
if (detectedKey) {
|
|
29371
|
+
publicKey = detectedKey.publicKey;
|
|
29372
|
+
} else {
|
|
29373
|
+
publicKey = await this.config.signer.getIdentityPublicKey();
|
|
29374
|
+
}
|
|
29375
|
+
break;
|
|
29376
|
+
}
|
|
29377
|
+
let inputsSigned = 0;
|
|
29378
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
29379
|
+
const input = tx.getInput(i);
|
|
29380
|
+
if (!input?.witnessUtxo?.script) {
|
|
29381
|
+
continue;
|
|
29382
|
+
}
|
|
29383
|
+
const script = input.witnessUtxo.script;
|
|
29384
|
+
if (script.length === 1 && script[0] === 81) {
|
|
29385
|
+
continue;
|
|
29386
|
+
}
|
|
29387
|
+
const identityScript = getP2TRScriptFromPublicKey(
|
|
29388
|
+
publicKey,
|
|
29389
|
+
this.config.getNetwork()
|
|
29390
|
+
);
|
|
29391
|
+
if ((0, import_utils21.bytesToHex)(script) === (0, import_utils21.bytesToHex)(identityScript)) {
|
|
29392
|
+
try {
|
|
29393
|
+
this.config.signer.signTransactionIndex(tx, i, publicKey);
|
|
29394
|
+
inputsSigned++;
|
|
29395
|
+
} catch (error) {
|
|
29396
|
+
throw new ValidationError(`Failed to sign input ${i}: ${error}`, {
|
|
29397
|
+
field: "input",
|
|
29398
|
+
value: i
|
|
29399
|
+
});
|
|
29400
|
+
}
|
|
29401
|
+
}
|
|
29402
|
+
}
|
|
29403
|
+
if (inputsSigned === 0) {
|
|
29404
|
+
throw new Error(
|
|
29405
|
+
"No inputs were signed. Check that the transaction contains inputs controlled by this wallet."
|
|
29406
|
+
);
|
|
29407
|
+
}
|
|
29408
|
+
tx.finalize();
|
|
29409
|
+
const signedTxHex = tx.hex;
|
|
29410
|
+
return signedTxHex;
|
|
29411
|
+
} catch (error) {
|
|
29412
|
+
console.error("\u274C Error signing transaction:", error);
|
|
29413
|
+
throw error;
|
|
29414
|
+
}
|
|
29415
|
+
}
|
|
29416
|
+
/**
|
|
29417
|
+
* Helper method to auto-detect which key should be used for signing a transaction.
|
|
29418
|
+
*/
|
|
29419
|
+
async detectKeyForTransaction(tx) {
|
|
29420
|
+
try {
|
|
29421
|
+
const identityPubKey = await this.config.signer.getIdentityPublicKey();
|
|
29422
|
+
const depositPubKey = await this.config.signer.getDepositSigningKey();
|
|
29423
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
29424
|
+
const input = tx.getInput(i);
|
|
29425
|
+
if (input?.witnessUtxo?.script) {
|
|
29426
|
+
const script = input.witnessUtxo.script;
|
|
29427
|
+
const identityScript = getP2TRScriptFromPublicKey(
|
|
29428
|
+
identityPubKey,
|
|
29429
|
+
this.config.getNetwork()
|
|
29430
|
+
);
|
|
29431
|
+
const depositScript = getP2TRScriptFromPublicKey(
|
|
29432
|
+
depositPubKey,
|
|
29433
|
+
this.config.getNetwork()
|
|
29434
|
+
);
|
|
29435
|
+
if ((0, import_utils21.bytesToHex)(script) === (0, import_utils21.bytesToHex)(identityScript)) {
|
|
29436
|
+
return {
|
|
29437
|
+
publicKey: identityPubKey,
|
|
29438
|
+
keyType: "identity"
|
|
29439
|
+
};
|
|
29440
|
+
}
|
|
29441
|
+
if ((0, import_utils21.bytesToHex)(script) === (0, import_utils21.bytesToHex)(depositScript)) {
|
|
29442
|
+
return {
|
|
29443
|
+
publicKey: depositPubKey,
|
|
29444
|
+
keyType: "deposit"
|
|
29445
|
+
};
|
|
29446
|
+
}
|
|
29447
|
+
}
|
|
29448
|
+
}
|
|
29449
|
+
return null;
|
|
29450
|
+
} catch (error) {
|
|
29451
|
+
console.warn("Error during key auto-detection:", error);
|
|
29452
|
+
return null;
|
|
29453
|
+
}
|
|
29454
|
+
}
|
|
29136
29455
|
/**
|
|
29137
29456
|
* Get a Lightning receive request by ID.
|
|
29138
29457
|
*
|
|
@@ -29163,6 +29482,246 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
29163
29482
|
const sspClient = this.getSspClient();
|
|
29164
29483
|
return await sspClient.getCoopExitRequest(id);
|
|
29165
29484
|
}
|
|
29485
|
+
/**
|
|
29486
|
+
* Check the remaining timelock on a given node.
|
|
29487
|
+
*
|
|
29488
|
+
* @param {string} nodeId - The ID of the node to check
|
|
29489
|
+
* @returns {Promise<{nodeTimelock: number, refundTimelock: number}>} The remaining timelocks in blocks for both node and refund transactions
|
|
29490
|
+
*/
|
|
29491
|
+
async checkTimelock(nodeId) {
|
|
29492
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
29493
|
+
this.config.getCoordinatorAddress()
|
|
29494
|
+
);
|
|
29495
|
+
try {
|
|
29496
|
+
const response = await sparkClient.query_nodes({
|
|
29497
|
+
source: {
|
|
29498
|
+
$case: "nodeIds",
|
|
29499
|
+
nodeIds: {
|
|
29500
|
+
nodeIds: [nodeId]
|
|
29501
|
+
}
|
|
29502
|
+
},
|
|
29503
|
+
includeParents: false,
|
|
29504
|
+
network: NetworkToProto[this.config.getNetwork()]
|
|
29505
|
+
});
|
|
29506
|
+
const node = response.nodes[nodeId];
|
|
29507
|
+
if (!node) {
|
|
29508
|
+
throw new ValidationError("Node not found", {
|
|
29509
|
+
field: "nodeId",
|
|
29510
|
+
value: nodeId
|
|
29511
|
+
});
|
|
29512
|
+
}
|
|
29513
|
+
const isRootNode = !node.parentNodeId;
|
|
29514
|
+
if (!node.nodeTx || node.nodeTx.length === 0) {
|
|
29515
|
+
throw new ValidationError(
|
|
29516
|
+
`Node transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
|
|
29517
|
+
{
|
|
29518
|
+
field: "nodeTx",
|
|
29519
|
+
value: node.nodeTx?.length || 0
|
|
29520
|
+
}
|
|
29521
|
+
);
|
|
29522
|
+
}
|
|
29523
|
+
if (!node.refundTx || node.refundTx.length === 0) {
|
|
29524
|
+
throw new ValidationError(
|
|
29525
|
+
`Refund transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
|
|
29526
|
+
{
|
|
29527
|
+
field: "refundTx",
|
|
29528
|
+
value: node.refundTx?.length || 0
|
|
29529
|
+
}
|
|
29530
|
+
);
|
|
29531
|
+
}
|
|
29532
|
+
let nodeTx, refundTx;
|
|
29533
|
+
try {
|
|
29534
|
+
nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
29535
|
+
} catch (error) {
|
|
29536
|
+
throw new ValidationError(
|
|
29537
|
+
`Failed to parse node transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
|
|
29538
|
+
{
|
|
29539
|
+
field: "nodeTx",
|
|
29540
|
+
value: node.nodeTx.length
|
|
29541
|
+
}
|
|
29542
|
+
);
|
|
29543
|
+
}
|
|
29544
|
+
try {
|
|
29545
|
+
refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
29546
|
+
} catch (error) {
|
|
29547
|
+
throw new ValidationError(
|
|
29548
|
+
`Failed to parse refund transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
|
|
29549
|
+
{
|
|
29550
|
+
field: "refundTx",
|
|
29551
|
+
value: node.refundTx.length
|
|
29552
|
+
}
|
|
29553
|
+
);
|
|
29554
|
+
}
|
|
29555
|
+
const nodeInput = nodeTx.getInput(0);
|
|
29556
|
+
if (!nodeInput) {
|
|
29557
|
+
throw new ValidationError(
|
|
29558
|
+
`Node transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
|
|
29559
|
+
{
|
|
29560
|
+
field: "nodeInput",
|
|
29561
|
+
value: nodeTx.inputsLength
|
|
29562
|
+
}
|
|
29563
|
+
);
|
|
29564
|
+
}
|
|
29565
|
+
if (!nodeInput.sequence) {
|
|
29566
|
+
throw new ValidationError(
|
|
29567
|
+
`Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
|
|
29568
|
+
{
|
|
29569
|
+
field: "sequence",
|
|
29570
|
+
value: nodeInput.sequence
|
|
29571
|
+
}
|
|
29572
|
+
);
|
|
29573
|
+
}
|
|
29574
|
+
const refundInput = refundTx.getInput(0);
|
|
29575
|
+
if (!refundInput) {
|
|
29576
|
+
throw new ValidationError(
|
|
29577
|
+
`Refund transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
|
|
29578
|
+
{
|
|
29579
|
+
field: "refundInput",
|
|
29580
|
+
value: refundTx.inputsLength
|
|
29581
|
+
}
|
|
29582
|
+
);
|
|
29583
|
+
}
|
|
29584
|
+
if (!refundInput.sequence) {
|
|
29585
|
+
throw new ValidationError(
|
|
29586
|
+
`Refund transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
|
|
29587
|
+
{
|
|
29588
|
+
field: "sequence",
|
|
29589
|
+
value: refundInput.sequence
|
|
29590
|
+
}
|
|
29591
|
+
);
|
|
29592
|
+
}
|
|
29593
|
+
const nodeTimelock = nodeInput.sequence & 65535;
|
|
29594
|
+
const refundTimelock = refundInput.sequence & 65535;
|
|
29595
|
+
return {
|
|
29596
|
+
nodeTimelock,
|
|
29597
|
+
refundTimelock
|
|
29598
|
+
};
|
|
29599
|
+
} catch (error) {
|
|
29600
|
+
throw new NetworkError(
|
|
29601
|
+
`Failed to check timelock for node ${nodeId}`,
|
|
29602
|
+
{
|
|
29603
|
+
method: "query_nodes"
|
|
29604
|
+
},
|
|
29605
|
+
error
|
|
29606
|
+
);
|
|
29607
|
+
}
|
|
29608
|
+
}
|
|
29609
|
+
/**
|
|
29610
|
+
* Refresh the timelock of a specific node.
|
|
29611
|
+
*
|
|
29612
|
+
* @param {string} nodeId - The ID of the node to refresh
|
|
29613
|
+
* @returns {Promise<void>} Promise that resolves when the timelock is refreshed
|
|
29614
|
+
*/
|
|
29615
|
+
async testOnly_expireTimelock(nodeId) {
|
|
29616
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
29617
|
+
this.config.getCoordinatorAddress()
|
|
29618
|
+
);
|
|
29619
|
+
try {
|
|
29620
|
+
const response = await sparkClient.query_nodes({
|
|
29621
|
+
source: {
|
|
29622
|
+
$case: "nodeIds",
|
|
29623
|
+
nodeIds: {
|
|
29624
|
+
nodeIds: [nodeId]
|
|
29625
|
+
}
|
|
29626
|
+
},
|
|
29627
|
+
includeParents: true
|
|
29628
|
+
});
|
|
29629
|
+
const node = response.nodes[nodeId];
|
|
29630
|
+
if (!node) {
|
|
29631
|
+
throw new ValidationError("Node not found", {
|
|
29632
|
+
field: "nodeId",
|
|
29633
|
+
value: nodeId
|
|
29634
|
+
});
|
|
29635
|
+
}
|
|
29636
|
+
if (!node.parentNodeId) {
|
|
29637
|
+
throw new ValidationError("Node has no parent", {
|
|
29638
|
+
field: "parentNodeId",
|
|
29639
|
+
value: node.parentNodeId
|
|
29640
|
+
});
|
|
29641
|
+
}
|
|
29642
|
+
const parentNode = response.nodes[node.parentNodeId];
|
|
29643
|
+
if (!parentNode) {
|
|
29644
|
+
throw new ValidationError("Parent node not found", {
|
|
29645
|
+
field: "parentNodeId",
|
|
29646
|
+
value: node.parentNodeId
|
|
29647
|
+
});
|
|
29648
|
+
}
|
|
29649
|
+
const signingPubKey = await this.config.signer.generatePublicKey(
|
|
29650
|
+
(0, import_sha213.sha256)(node.id)
|
|
29651
|
+
);
|
|
29652
|
+
const result = await this.transferService.refreshTimelockNodes(
|
|
29653
|
+
[node],
|
|
29654
|
+
parentNode,
|
|
29655
|
+
signingPubKey
|
|
29656
|
+
);
|
|
29657
|
+
const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
|
|
29658
|
+
if (leafIndex !== -1 && result.nodes.length > 0) {
|
|
29659
|
+
const newNode = result.nodes[0];
|
|
29660
|
+
if (newNode) {
|
|
29661
|
+
this.leaves[leafIndex] = newNode;
|
|
29662
|
+
}
|
|
29663
|
+
}
|
|
29664
|
+
} catch (error) {
|
|
29665
|
+
throw new NetworkError(
|
|
29666
|
+
"Failed to refresh timelock",
|
|
29667
|
+
{
|
|
29668
|
+
method: "refresh_timelock"
|
|
29669
|
+
},
|
|
29670
|
+
error
|
|
29671
|
+
);
|
|
29672
|
+
}
|
|
29673
|
+
}
|
|
29674
|
+
/**
|
|
29675
|
+
* Refresh the timelock of a specific node's refund transaction only.
|
|
29676
|
+
*
|
|
29677
|
+
* @param {string} nodeId - The ID of the node whose refund transaction to refresh
|
|
29678
|
+
* @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
|
|
29679
|
+
*/
|
|
29680
|
+
async testOnly_expireTimelockRefundTx(nodeId) {
|
|
29681
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
29682
|
+
this.config.getCoordinatorAddress()
|
|
29683
|
+
);
|
|
29684
|
+
try {
|
|
29685
|
+
const response = await sparkClient.query_nodes({
|
|
29686
|
+
source: {
|
|
29687
|
+
$case: "nodeIds",
|
|
29688
|
+
nodeIds: {
|
|
29689
|
+
nodeIds: [nodeId]
|
|
29690
|
+
}
|
|
29691
|
+
},
|
|
29692
|
+
includeParents: false
|
|
29693
|
+
});
|
|
29694
|
+
const node = response.nodes[nodeId];
|
|
29695
|
+
if (!node) {
|
|
29696
|
+
throw new ValidationError("Node not found", {
|
|
29697
|
+
field: "nodeId",
|
|
29698
|
+
value: nodeId
|
|
29699
|
+
});
|
|
29700
|
+
}
|
|
29701
|
+
const signingPubKey = await this.config.signer.generatePublicKey(
|
|
29702
|
+
(0, import_sha213.sha256)(node.id)
|
|
29703
|
+
);
|
|
29704
|
+
const result = await this.transferService.refreshTimelockRefundTx(
|
|
29705
|
+
node,
|
|
29706
|
+
signingPubKey
|
|
29707
|
+
);
|
|
29708
|
+
const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
|
|
29709
|
+
if (leafIndex !== -1 && result.nodes.length > 0) {
|
|
29710
|
+
const newNode = result.nodes[0];
|
|
29711
|
+
if (newNode) {
|
|
29712
|
+
this.leaves[leafIndex] = newNode;
|
|
29713
|
+
}
|
|
29714
|
+
}
|
|
29715
|
+
} catch (error) {
|
|
29716
|
+
throw new NetworkError(
|
|
29717
|
+
"Failed to refresh refund timelock",
|
|
29718
|
+
{
|
|
29719
|
+
method: "refresh_timelock_refund_tx"
|
|
29720
|
+
},
|
|
29721
|
+
error
|
|
29722
|
+
);
|
|
29723
|
+
}
|
|
29724
|
+
}
|
|
29166
29725
|
cleanup() {
|
|
29167
29726
|
if (this.claimTransfersInterval) {
|
|
29168
29727
|
clearInterval(this.claimTransfersInterval);
|
|
@@ -29251,13 +29810,476 @@ async function getLatestDepositTxId(address2) {
|
|
|
29251
29810
|
}
|
|
29252
29811
|
return null;
|
|
29253
29812
|
}
|
|
29813
|
+
async function isTxBroadcast(txid, baseUrl, network) {
|
|
29814
|
+
const headers = {};
|
|
29815
|
+
if (network === 3 /* REGTEST */) {
|
|
29816
|
+
const auth = btoa(
|
|
29817
|
+
`${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
|
|
29818
|
+
);
|
|
29819
|
+
headers["Authorization"] = `Basic ${auth}`;
|
|
29820
|
+
}
|
|
29821
|
+
const response = await fetch(`${baseUrl}/tx/${txid}`, {
|
|
29822
|
+
headers
|
|
29823
|
+
});
|
|
29824
|
+
const tx = await response.json();
|
|
29825
|
+
if (tx.error) {
|
|
29826
|
+
return false;
|
|
29827
|
+
}
|
|
29828
|
+
return true;
|
|
29829
|
+
}
|
|
29254
29830
|
|
|
29255
29831
|
// src/utils/index.ts
|
|
29256
29832
|
init_buffer();
|
|
29833
|
+
|
|
29834
|
+
// src/utils/unilateral-exit.ts
|
|
29835
|
+
init_buffer();
|
|
29836
|
+
var import_utils22 = require("@noble/curves/abstract/utils");
|
|
29837
|
+
var import_legacy = require("@noble/hashes/legacy");
|
|
29838
|
+
var import_sha214 = require("@noble/hashes/sha2");
|
|
29839
|
+
var btc5 = __toESM(require("@scure/btc-signer"), 1);
|
|
29840
|
+
function isEphemeralAnchorOutput(script, amount) {
|
|
29841
|
+
return Boolean(
|
|
29842
|
+
amount === 0n && script && // Pattern 1: Bare OP_TRUE (single byte 0x51)
|
|
29843
|
+
(script.length === 1 && script[0] === 81 || // Pattern 2: Push OP_TRUE (two bytes 0x01 0x51) - MALFORMED but we detect it
|
|
29844
|
+
script.length === 2 && script[0] === 1 && script[1] === 81 || // Pattern 3: Bitcoin v29 ephemeral anchor script (7 bytes: 015152014e0173)
|
|
29845
|
+
script.length === 7 && script[0] === 1 && script[1] === 81 && script[2] === 82 && script[3] === 1 && script[4] === 78 && script[5] === 1 && script[6] === 115 || // Pattern 4: Bitcoin ephemeral anchor OP_1 + push 2 bytes (4 bytes: 51024e73)
|
|
29846
|
+
script.length === 4 && script[0] === 81 && script[1] === 2 && script[2] === 78 && script[3] === 115)
|
|
29847
|
+
);
|
|
29848
|
+
}
|
|
29849
|
+
async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
|
|
29850
|
+
const result = [];
|
|
29851
|
+
const nodes = nodeHexStrings.map((hex) => TreeNode.decode((0, import_utils22.hexToBytes)(hex)));
|
|
29852
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
29853
|
+
for (const node of nodes) {
|
|
29854
|
+
nodeMap.set(node.id, node);
|
|
29855
|
+
}
|
|
29856
|
+
for (const node of nodes) {
|
|
29857
|
+
const transactions = [];
|
|
29858
|
+
const chain = [];
|
|
29859
|
+
let currentNode = node;
|
|
29860
|
+
while (currentNode) {
|
|
29861
|
+
chain.unshift(currentNode);
|
|
29862
|
+
if (currentNode.parentNodeId) {
|
|
29863
|
+
let parentNode = nodeMap.get(currentNode.parentNodeId);
|
|
29864
|
+
if (!parentNode && sparkClient) {
|
|
29865
|
+
try {
|
|
29866
|
+
const response = await sparkClient.query_nodes({
|
|
29867
|
+
source: {
|
|
29868
|
+
$case: "nodeIds",
|
|
29869
|
+
nodeIds: {
|
|
29870
|
+
nodeIds: [currentNode.parentNodeId]
|
|
29871
|
+
}
|
|
29872
|
+
},
|
|
29873
|
+
includeParents: true,
|
|
29874
|
+
network: network || 0
|
|
29875
|
+
// Default to mainnet if not provided
|
|
29876
|
+
});
|
|
29877
|
+
parentNode = response.nodes[currentNode.parentNodeId];
|
|
29878
|
+
if (parentNode) {
|
|
29879
|
+
nodeMap.set(currentNode.parentNodeId, parentNode);
|
|
29880
|
+
}
|
|
29881
|
+
} catch (error) {
|
|
29882
|
+
console.warn(
|
|
29883
|
+
`Failed to query parent node ${currentNode.parentNodeId}: ${error}`
|
|
29884
|
+
);
|
|
29885
|
+
break;
|
|
29886
|
+
}
|
|
29887
|
+
}
|
|
29888
|
+
if (parentNode) {
|
|
29889
|
+
currentNode = parentNode;
|
|
29890
|
+
} else {
|
|
29891
|
+
if (!sparkClient) {
|
|
29892
|
+
console.warn(
|
|
29893
|
+
`Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
|
|
29894
|
+
);
|
|
29895
|
+
} else {
|
|
29896
|
+
console.warn(
|
|
29897
|
+
`Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
|
|
29898
|
+
);
|
|
29899
|
+
}
|
|
29900
|
+
break;
|
|
29901
|
+
}
|
|
29902
|
+
} else {
|
|
29903
|
+
break;
|
|
29904
|
+
}
|
|
29905
|
+
}
|
|
29906
|
+
for (const chainNode of chain) {
|
|
29907
|
+
const nodeTx = (0, import_utils22.bytesToHex)(chainNode.nodeTx);
|
|
29908
|
+
transactions.push(nodeTx);
|
|
29909
|
+
if (chainNode.id === node.id) {
|
|
29910
|
+
const refundTx = (0, import_utils22.bytesToHex)(chainNode.refundTx);
|
|
29911
|
+
transactions.push(refundTx);
|
|
29912
|
+
}
|
|
29913
|
+
}
|
|
29914
|
+
result.push({
|
|
29915
|
+
leafId: node.id,
|
|
29916
|
+
transactions
|
|
29917
|
+
});
|
|
29918
|
+
}
|
|
29919
|
+
return result;
|
|
29920
|
+
}
|
|
29921
|
+
async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, feeRate, electrsUrl, sparkClient, network) {
|
|
29922
|
+
const result = [];
|
|
29923
|
+
const availableUtxos = [...utxos].sort((a, b) => {
|
|
29924
|
+
if (a.value > b.value) return -1;
|
|
29925
|
+
if (a.value < b.value) return 1;
|
|
29926
|
+
return 0;
|
|
29927
|
+
});
|
|
29928
|
+
const nodes = [];
|
|
29929
|
+
for (let i = 0; i < nodeHexStrings.length; i++) {
|
|
29930
|
+
const hex = nodeHexStrings[i];
|
|
29931
|
+
try {
|
|
29932
|
+
if (!hex || hex.length === 0) {
|
|
29933
|
+
throw new Error(`Node hex string at index ${i} is empty`);
|
|
29934
|
+
}
|
|
29935
|
+
if (hex.startsWith("03000000") || hex.startsWith("02000000") || hex.startsWith("01000000")) {
|
|
29936
|
+
throw new Error(
|
|
29937
|
+
`Node hex string at index ${i} appears to be a raw transaction hex, not a TreeNode protobuf. Use 'leafidtohex' command to convert node IDs to proper hex strings.`
|
|
29938
|
+
);
|
|
29939
|
+
}
|
|
29940
|
+
const nodeBytes = (0, import_utils22.hexToBytes)(hex);
|
|
29941
|
+
const node = TreeNode.decode(nodeBytes);
|
|
29942
|
+
if (!node.id) {
|
|
29943
|
+
throw new Error(
|
|
29944
|
+
`Decoded TreeNode at index ${i} is missing required 'id' field`
|
|
29945
|
+
);
|
|
29946
|
+
}
|
|
29947
|
+
if (!node.nodeTx || node.nodeTx.length === 0) {
|
|
29948
|
+
throw new Error(
|
|
29949
|
+
`Decoded TreeNode at index ${i} is missing required 'nodeTx' field`
|
|
29950
|
+
);
|
|
29951
|
+
}
|
|
29952
|
+
nodes.push(node);
|
|
29953
|
+
} catch (decodeError) {
|
|
29954
|
+
throw new Error(
|
|
29955
|
+
`Failed to decode TreeNode hex string at index ${i}: ${decodeError}. Make sure you're providing TreeNode protobuf hex strings, not raw transaction hex. Use 'leafidtohex' command to get proper hex strings.`
|
|
29956
|
+
);
|
|
29957
|
+
}
|
|
29958
|
+
}
|
|
29959
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
29960
|
+
for (const node of nodes) {
|
|
29961
|
+
nodeMap.set(node.id, node);
|
|
29962
|
+
}
|
|
29963
|
+
const broadcastTxs = /* @__PURE__ */ new Map();
|
|
29964
|
+
for (const node of nodes) {
|
|
29965
|
+
const txPackages = [];
|
|
29966
|
+
let previousFeeBumpTx;
|
|
29967
|
+
const chain = [];
|
|
29968
|
+
let currentNode = node;
|
|
29969
|
+
while (currentNode) {
|
|
29970
|
+
chain.unshift(currentNode);
|
|
29971
|
+
if (currentNode.parentNodeId) {
|
|
29972
|
+
let parentNode = nodeMap.get(currentNode.parentNodeId);
|
|
29973
|
+
if (!parentNode && sparkClient) {
|
|
29974
|
+
try {
|
|
29975
|
+
const response = await sparkClient.query_nodes({
|
|
29976
|
+
source: {
|
|
29977
|
+
$case: "nodeIds",
|
|
29978
|
+
nodeIds: {
|
|
29979
|
+
nodeIds: [currentNode.parentNodeId]
|
|
29980
|
+
}
|
|
29981
|
+
},
|
|
29982
|
+
includeParents: true,
|
|
29983
|
+
network: network || 0
|
|
29984
|
+
// Default to mainnet if not provided
|
|
29985
|
+
});
|
|
29986
|
+
parentNode = response.nodes[currentNode.parentNodeId];
|
|
29987
|
+
if (parentNode) {
|
|
29988
|
+
nodeMap.set(currentNode.parentNodeId, parentNode);
|
|
29989
|
+
}
|
|
29990
|
+
} catch (error) {
|
|
29991
|
+
console.warn(
|
|
29992
|
+
`Failed to query parent node ${currentNode.parentNodeId}: ${error}`
|
|
29993
|
+
);
|
|
29994
|
+
break;
|
|
29995
|
+
}
|
|
29996
|
+
}
|
|
29997
|
+
if (parentNode) {
|
|
29998
|
+
currentNode = parentNode;
|
|
29999
|
+
} else {
|
|
30000
|
+
if (!sparkClient) {
|
|
30001
|
+
console.warn(
|
|
30002
|
+
`Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
|
|
30003
|
+
);
|
|
30004
|
+
} else {
|
|
30005
|
+
console.warn(
|
|
30006
|
+
`Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
|
|
30007
|
+
);
|
|
30008
|
+
}
|
|
30009
|
+
break;
|
|
30010
|
+
}
|
|
30011
|
+
} else {
|
|
30012
|
+
break;
|
|
30013
|
+
}
|
|
30014
|
+
}
|
|
30015
|
+
for (const chainNode of chain) {
|
|
30016
|
+
let nodeTxHex = (0, import_utils22.bytesToHex)(chainNode.nodeTx);
|
|
30017
|
+
try {
|
|
30018
|
+
const txObj = getTxFromRawTxHex(nodeTxHex);
|
|
30019
|
+
const txid = getTxId(txObj);
|
|
30020
|
+
if (broadcastTxs.get(txid)) {
|
|
30021
|
+
continue;
|
|
30022
|
+
}
|
|
30023
|
+
broadcastTxs.set(txid, true);
|
|
30024
|
+
const isBroadcast = await isTxBroadcast(txid, electrsUrl, network);
|
|
30025
|
+
if (isBroadcast) {
|
|
30026
|
+
continue;
|
|
30027
|
+
} else {
|
|
30028
|
+
}
|
|
30029
|
+
let anchorOutputScriptHex;
|
|
30030
|
+
for (let i = txObj.outputsLength - 1; i >= 0; i--) {
|
|
30031
|
+
const output = txObj.getOutput(i);
|
|
30032
|
+
if (output?.amount === 0n && output.script) {
|
|
30033
|
+
anchorOutputScriptHex = (0, import_utils22.bytesToHex)(output.script);
|
|
30034
|
+
break;
|
|
30035
|
+
}
|
|
30036
|
+
}
|
|
30037
|
+
} catch (parseError) {
|
|
30038
|
+
console.error(
|
|
30039
|
+
`\u274C Error parsing nodeTx for anchor check (node ${chainNode.id}): ${parseError}`
|
|
30040
|
+
);
|
|
30041
|
+
console.log(
|
|
30042
|
+
` This may indicate a corrupted transaction in the TreeNode.`
|
|
30043
|
+
);
|
|
30044
|
+
console.log(` Transaction hex: ${nodeTxHex}`);
|
|
30045
|
+
console.log(
|
|
30046
|
+
` Attempting to continue with original hex, but fee bump may fail.`
|
|
30047
|
+
);
|
|
30048
|
+
}
|
|
30049
|
+
const {
|
|
30050
|
+
feeBumpPsbt: nodeFeeBumpPsbt,
|
|
30051
|
+
usedUtxos,
|
|
30052
|
+
correctedParentTx
|
|
30053
|
+
} = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
|
|
30054
|
+
const feeBumpTx = btc5.Transaction.fromPSBT((0, import_utils22.hexToBytes)(nodeFeeBumpPsbt));
|
|
30055
|
+
var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
|
|
30056
|
+
var feeBumpOutPubKey = null;
|
|
30057
|
+
for (const usedUtxo of usedUtxos) {
|
|
30058
|
+
if (feeBumpOut && (0, import_utils22.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
|
|
30059
|
+
feeBumpOutPubKey = usedUtxo.publicKey;
|
|
30060
|
+
}
|
|
30061
|
+
const index = availableUtxos.findIndex(
|
|
30062
|
+
(u) => u.txid === usedUtxo.txid && u.vout === usedUtxo.vout
|
|
30063
|
+
);
|
|
30064
|
+
if (index !== -1) {
|
|
30065
|
+
availableUtxos.splice(index, 1);
|
|
30066
|
+
}
|
|
30067
|
+
}
|
|
30068
|
+
if (feeBumpOut)
|
|
30069
|
+
availableUtxos.unshift({
|
|
30070
|
+
txid: getTxId(feeBumpTx),
|
|
30071
|
+
vout: 0,
|
|
30072
|
+
value: feeBumpOut.amount,
|
|
30073
|
+
script: (0, import_utils22.bytesToHex)(feeBumpOut.script),
|
|
30074
|
+
publicKey: feeBumpOutPubKey
|
|
30075
|
+
});
|
|
30076
|
+
const finalNodeTx = correctedParentTx || nodeTxHex;
|
|
30077
|
+
txPackages.push({ tx: finalNodeTx, feeBumpPsbt: nodeFeeBumpPsbt });
|
|
30078
|
+
if (chainNode.id === node.id) {
|
|
30079
|
+
let refundTxHex = (0, import_utils22.bytesToHex)(chainNode.refundTx);
|
|
30080
|
+
try {
|
|
30081
|
+
const txObj = getTxFromRawTxHex(refundTxHex);
|
|
30082
|
+
let anchorOutputScriptHex;
|
|
30083
|
+
for (let i = txObj.outputsLength - 1; i >= 0; i--) {
|
|
30084
|
+
const output = txObj.getOutput(i);
|
|
30085
|
+
if (output?.amount === 0n && output.script) {
|
|
30086
|
+
anchorOutputScriptHex = (0, import_utils22.bytesToHex)(output.script);
|
|
30087
|
+
break;
|
|
30088
|
+
}
|
|
30089
|
+
}
|
|
30090
|
+
} catch (parseError) {
|
|
30091
|
+
console.error(
|
|
30092
|
+
`\u274C Error parsing refundTx for anchor check (node ${chainNode.id}): ${parseError}`
|
|
30093
|
+
);
|
|
30094
|
+
console.log(
|
|
30095
|
+
` This may indicate a corrupted refund transaction in the TreeNode.`
|
|
30096
|
+
);
|
|
30097
|
+
console.log(` Refund transaction hex: ${refundTxHex}`);
|
|
30098
|
+
console.log(
|
|
30099
|
+
` Attempting to continue with original refund hex, but this transaction may be invalid.`
|
|
30100
|
+
);
|
|
30101
|
+
}
|
|
30102
|
+
const refundFeeBump = constructFeeBumpTx(
|
|
30103
|
+
refundTxHex,
|
|
30104
|
+
availableUtxos,
|
|
30105
|
+
feeRate,
|
|
30106
|
+
void 0
|
|
30107
|
+
);
|
|
30108
|
+
txPackages.push({
|
|
30109
|
+
tx: refundTxHex,
|
|
30110
|
+
feeBumpPsbt: refundFeeBump.feeBumpPsbt
|
|
30111
|
+
});
|
|
30112
|
+
}
|
|
30113
|
+
}
|
|
30114
|
+
result.push({
|
|
30115
|
+
leafId: node.id,
|
|
30116
|
+
txPackages
|
|
30117
|
+
});
|
|
30118
|
+
}
|
|
30119
|
+
return result;
|
|
30120
|
+
}
|
|
30121
|
+
function hash160(data) {
|
|
30122
|
+
const sha256Hash = (0, import_sha214.sha256)(data);
|
|
30123
|
+
return (0, import_legacy.ripemd160)(sha256Hash);
|
|
30124
|
+
}
|
|
30125
|
+
function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
|
|
30126
|
+
if (!txHex || txHex.length === 0) {
|
|
30127
|
+
throw new Error("Transaction hex string is empty or undefined");
|
|
30128
|
+
}
|
|
30129
|
+
if (utxos.length === 0) {
|
|
30130
|
+
throw new Error("No UTXOs available for fee bump");
|
|
30131
|
+
}
|
|
30132
|
+
let correctedTxHex = txHex;
|
|
30133
|
+
let parentTx;
|
|
30134
|
+
try {
|
|
30135
|
+
parentTx = getTxFromRawTxHex(correctedTxHex);
|
|
30136
|
+
if (!parentTx) {
|
|
30137
|
+
throw new Error("getTxFromRawTxHex returned null/undefined");
|
|
30138
|
+
}
|
|
30139
|
+
} catch (parseError) {
|
|
30140
|
+
throw new Error(
|
|
30141
|
+
`Failed to parse parent transaction hex: ${parseError}. Transaction hex: ${correctedTxHex}`
|
|
30142
|
+
);
|
|
30143
|
+
}
|
|
30144
|
+
try {
|
|
30145
|
+
const outputsLength = parentTx.outputsLength;
|
|
30146
|
+
const inputsLength = parentTx.inputsLength;
|
|
30147
|
+
if (typeof outputsLength !== "number" || outputsLength < 0) {
|
|
30148
|
+
throw new Error(
|
|
30149
|
+
"Invalid transaction: outputsLength is not a valid number"
|
|
30150
|
+
);
|
|
30151
|
+
}
|
|
30152
|
+
if (typeof inputsLength !== "number" || inputsLength < 0) {
|
|
30153
|
+
throw new Error(
|
|
30154
|
+
"Invalid transaction: inputsLength is not a valid number"
|
|
30155
|
+
);
|
|
30156
|
+
}
|
|
30157
|
+
} catch (validationError) {
|
|
30158
|
+
throw new Error(
|
|
30159
|
+
`Transaction validation failed: ${validationError}. This may indicate a corrupted or malformed transaction.`
|
|
30160
|
+
);
|
|
30161
|
+
}
|
|
30162
|
+
const parentTxIdFromLib = parentTx.id;
|
|
30163
|
+
let ephemeralAnchorIndex = -1;
|
|
30164
|
+
for (let i = 0; i < parentTx.outputsLength; i++) {
|
|
30165
|
+
const output = parentTx.getOutput(i);
|
|
30166
|
+
const isEphemeralAnchor = isEphemeralAnchorOutput(
|
|
30167
|
+
output?.script,
|
|
30168
|
+
output?.amount
|
|
30169
|
+
);
|
|
30170
|
+
if (isEphemeralAnchor) {
|
|
30171
|
+
ephemeralAnchorIndex = i;
|
|
30172
|
+
break;
|
|
30173
|
+
}
|
|
30174
|
+
}
|
|
30175
|
+
if (ephemeralAnchorIndex === -1) {
|
|
30176
|
+
throw new Error(
|
|
30177
|
+
"No ephemeral anchor output found in parent transaction. Expected a 0-value output with OP_TRUE script (0x51), malformed OP_TRUE (0x0151), Bitcoin v29 ephemeral anchor script (015152014e0173), or Bitcoin OP_1 + push 2 bytes script (51024e73)."
|
|
30178
|
+
);
|
|
30179
|
+
}
|
|
30180
|
+
const ephemeralAnchorOutput = parentTx.getOutput(ephemeralAnchorIndex);
|
|
30181
|
+
if (!ephemeralAnchorOutput)
|
|
30182
|
+
throw new Error("No ephemeral anchor output found");
|
|
30183
|
+
if (!ephemeralAnchorOutput.script)
|
|
30184
|
+
throw new Error("No script found in ephemeral anchor output");
|
|
30185
|
+
if (utxos.length === 0) {
|
|
30186
|
+
throw new Error("No UTXOs available for fee bump");
|
|
30187
|
+
}
|
|
30188
|
+
const builder = new btc5.Transaction({
|
|
30189
|
+
version: 3,
|
|
30190
|
+
allowUnknown: true,
|
|
30191
|
+
allowLegacyWitnessUtxo: true
|
|
30192
|
+
});
|
|
30193
|
+
let totalValue = 0n;
|
|
30194
|
+
const processedUtxos = [];
|
|
30195
|
+
for (let i = 0; i < utxos.length; i++) {
|
|
30196
|
+
const fundingUtxo = utxos[i];
|
|
30197
|
+
if (!fundingUtxo) {
|
|
30198
|
+
throw new Error(`UTXO at index ${i} is undefined`);
|
|
30199
|
+
}
|
|
30200
|
+
const pubKeyHash = hash160((0, import_utils22.hexToBytes)(fundingUtxo.publicKey));
|
|
30201
|
+
const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
|
|
30202
|
+
const providedScript = (0, import_utils22.hexToBytes)(fundingUtxo.script);
|
|
30203
|
+
if ((0, import_utils22.bytesToHex)(scriptToUse) !== (0, import_utils22.bytesToHex)(providedScript)) {
|
|
30204
|
+
throw new Error(
|
|
30205
|
+
`\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
|
|
30206
|
+
);
|
|
30207
|
+
}
|
|
30208
|
+
builder.addInput({
|
|
30209
|
+
txid: fundingUtxo.txid,
|
|
30210
|
+
index: fundingUtxo.vout,
|
|
30211
|
+
sequence: 4294967295,
|
|
30212
|
+
witnessUtxo: {
|
|
30213
|
+
script: scriptToUse,
|
|
30214
|
+
// Always P2WPKH
|
|
30215
|
+
amount: fundingUtxo.value
|
|
30216
|
+
}
|
|
30217
|
+
});
|
|
30218
|
+
totalValue += fundingUtxo.value;
|
|
30219
|
+
processedUtxos.push({
|
|
30220
|
+
utxo: fundingUtxo,
|
|
30221
|
+
p2wpkhScript: scriptToUse
|
|
30222
|
+
});
|
|
30223
|
+
}
|
|
30224
|
+
builder.addInput({
|
|
30225
|
+
txid: parentTxIdFromLib,
|
|
30226
|
+
index: ephemeralAnchorIndex,
|
|
30227
|
+
sequence: 4294967295,
|
|
30228
|
+
witnessUtxo: {
|
|
30229
|
+
script: ephemeralAnchorOutput.script,
|
|
30230
|
+
// Use the original script directly (not P2WSH wrapped)
|
|
30231
|
+
amount: 0n
|
|
30232
|
+
}
|
|
30233
|
+
});
|
|
30234
|
+
const fee = 1500n;
|
|
30235
|
+
const remainingValue = totalValue - fee;
|
|
30236
|
+
if (remainingValue <= 0n) {
|
|
30237
|
+
throw new Error(
|
|
30238
|
+
`Insufficient funds for fee bump. Required fee: ${fee} sats, Available: ${totalValue} sats`
|
|
30239
|
+
);
|
|
30240
|
+
}
|
|
30241
|
+
if (processedUtxos.length === 0) {
|
|
30242
|
+
throw new Error("No processed UTXOs available for change output");
|
|
30243
|
+
}
|
|
30244
|
+
const firstProcessedUtxo = processedUtxos[0];
|
|
30245
|
+
if (!firstProcessedUtxo) {
|
|
30246
|
+
throw new Error("First processed UTXO is undefined");
|
|
30247
|
+
}
|
|
30248
|
+
builder.addOutput({
|
|
30249
|
+
script: firstProcessedUtxo.p2wpkhScript,
|
|
30250
|
+
amount: remainingValue
|
|
30251
|
+
});
|
|
30252
|
+
for (let i = 0; i < processedUtxos.length; i++) {
|
|
30253
|
+
const processed = processedUtxos[i];
|
|
30254
|
+
if (!processed) {
|
|
30255
|
+
throw new Error(`Processed UTXO at index ${i} is undefined`);
|
|
30256
|
+
}
|
|
30257
|
+
try {
|
|
30258
|
+
builder.updateInput(i, {
|
|
30259
|
+
witnessScript: processed.p2wpkhScript
|
|
30260
|
+
});
|
|
30261
|
+
builder.signIdx;
|
|
30262
|
+
} catch (error) {
|
|
30263
|
+
throw new Error(`Failed to handle funding UTXO input ${i + 1}: ${error}`);
|
|
30264
|
+
}
|
|
30265
|
+
}
|
|
30266
|
+
let psbtHex;
|
|
30267
|
+
try {
|
|
30268
|
+
psbtHex = (0, import_utils22.bytesToHex)(builder.toPSBT());
|
|
30269
|
+
} catch (error) {
|
|
30270
|
+
throw new Error(`Failed to extract transaction: ${error}`);
|
|
30271
|
+
}
|
|
30272
|
+
return {
|
|
30273
|
+
feeBumpPsbt: psbtHex,
|
|
30274
|
+
usedUtxos: utxos,
|
|
30275
|
+
correctedParentTx: correctedTxHex !== txHex ? correctedTxHex : void 0
|
|
30276
|
+
};
|
|
30277
|
+
}
|
|
29257
30278
|
// Annotate the CommonJS export names for ESM import in node:
|
|
29258
30279
|
0 && (module.exports = {
|
|
29259
30280
|
AuthenticationError,
|
|
29260
30281
|
ConfigurationError,
|
|
30282
|
+
DEFAULT_FEE_SATS,
|
|
29261
30283
|
DefaultSparkSigner,
|
|
29262
30284
|
InternalValidationError,
|
|
29263
30285
|
LRC_WALLET_NETWORK,
|
|
@@ -29281,6 +30303,9 @@ init_buffer();
|
|
|
29281
30303
|
collectResponses,
|
|
29282
30304
|
computeTaprootKeyNoScript,
|
|
29283
30305
|
computerLagrangeCoefficients,
|
|
30306
|
+
constructFeeBumpTx,
|
|
30307
|
+
constructUnilateralExitFeeBumpPackages,
|
|
30308
|
+
constructUnilateralExitTxs,
|
|
29284
30309
|
createRefundTx,
|
|
29285
30310
|
createSigningCommitment,
|
|
29286
30311
|
createSigningNonce,
|
|
@@ -29299,6 +30324,7 @@ init_buffer();
|
|
|
29299
30324
|
getLatestDepositTxId,
|
|
29300
30325
|
getNetwork,
|
|
29301
30326
|
getNetworkFromAddress,
|
|
30327
|
+
getNetworkFromString,
|
|
29302
30328
|
getNextTransactionSequence,
|
|
29303
30329
|
getP2TRAddressFromPkScript,
|
|
29304
30330
|
getP2TRAddressFromPublicKey,
|
|
@@ -29314,7 +30340,10 @@ init_buffer();
|
|
|
29314
30340
|
getTxFromRawTxHex,
|
|
29315
30341
|
getTxId,
|
|
29316
30342
|
getTxIdNoReverse,
|
|
30343
|
+
isEphemeralAnchorOutput,
|
|
30344
|
+
isTxBroadcast,
|
|
29317
30345
|
lastKeyWithTarget,
|
|
30346
|
+
maybeApplyFee,
|
|
29318
30347
|
modInverse,
|
|
29319
30348
|
proofOfPossessionMessageHashForDepositAddress,
|
|
29320
30349
|
recoverSecret,
|