@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/index.cjs
CHANGED
|
@@ -1289,6 +1289,7 @@ var index_exports = {};
|
|
|
1289
1289
|
__export(index_exports, {
|
|
1290
1290
|
AuthenticationError: () => AuthenticationError,
|
|
1291
1291
|
ConfigurationError: () => ConfigurationError,
|
|
1292
|
+
DEFAULT_FEE_SATS: () => DEFAULT_FEE_SATS,
|
|
1292
1293
|
InternalValidationError: () => InternalValidationError,
|
|
1293
1294
|
LRC_WALLET_NETWORK: () => LRC_WALLET_NETWORK,
|
|
1294
1295
|
LRC_WALLET_NETWORK_TYPE: () => LRC_WALLET_NETWORK_TYPE,
|
|
@@ -1310,6 +1311,9 @@ __export(index_exports, {
|
|
|
1310
1311
|
collectResponses: () => collectResponses,
|
|
1311
1312
|
computeTaprootKeyNoScript: () => computeTaprootKeyNoScript,
|
|
1312
1313
|
computerLagrangeCoefficients: () => computerLagrangeCoefficients,
|
|
1314
|
+
constructFeeBumpTx: () => constructFeeBumpTx,
|
|
1315
|
+
constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
|
|
1316
|
+
constructUnilateralExitTxs: () => constructUnilateralExitTxs,
|
|
1313
1317
|
createDummyTx: () => createDummyTx,
|
|
1314
1318
|
createRefundTx: () => createRefundTx,
|
|
1315
1319
|
createSigningCommitment: () => createSigningCommitment,
|
|
@@ -1329,6 +1333,7 @@ __export(index_exports, {
|
|
|
1329
1333
|
getLatestDepositTxId: () => getLatestDepositTxId,
|
|
1330
1334
|
getNetwork: () => getNetwork,
|
|
1331
1335
|
getNetworkFromAddress: () => getNetworkFromAddress,
|
|
1336
|
+
getNetworkFromString: () => getNetworkFromString,
|
|
1332
1337
|
getNextTransactionSequence: () => getNextTransactionSequence,
|
|
1333
1338
|
getP2TRAddressFromPkScript: () => getP2TRAddressFromPkScript,
|
|
1334
1339
|
getP2TRAddressFromPublicKey: () => getP2TRAddressFromPublicKey,
|
|
@@ -1344,7 +1349,10 @@ __export(index_exports, {
|
|
|
1344
1349
|
getTxFromRawTxHex: () => getTxFromRawTxHex,
|
|
1345
1350
|
getTxId: () => getTxId,
|
|
1346
1351
|
getTxIdNoReverse: () => getTxIdNoReverse,
|
|
1352
|
+
isEphemeralAnchorOutput: () => isEphemeralAnchorOutput,
|
|
1353
|
+
isTxBroadcast: () => isTxBroadcast,
|
|
1347
1354
|
lastKeyWithTarget: () => lastKeyWithTarget,
|
|
1355
|
+
maybeApplyFee: () => maybeApplyFee,
|
|
1348
1356
|
modInverse: () => modInverse,
|
|
1349
1357
|
proofOfPossessionMessageHashForDepositAddress: () => proofOfPossessionMessageHashForDepositAddress,
|
|
1350
1358
|
recoverSecret: () => recoverSecret,
|
|
@@ -17017,11 +17025,9 @@ function decodeBytesToSigningCommitment(bytes2) {
|
|
|
17017
17025
|
}
|
|
17018
17026
|
|
|
17019
17027
|
// src/signer/signer.ts
|
|
17020
|
-
var import_lrc20_sdk = require("@buildonspark/lrc20-sdk");
|
|
17021
|
-
var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
|
|
17022
17028
|
var import_secp256k16 = require("@bitcoinerlab/secp256k1");
|
|
17029
|
+
var import_lrc20_sdk = require("@buildonspark/lrc20-sdk");
|
|
17023
17030
|
var import_sha22 = require("@noble/hashes/sha2");
|
|
17024
|
-
var import_lrc20_sdk3 = require("@buildonspark/lrc20-sdk");
|
|
17025
17031
|
var sparkFrostModule = void 0;
|
|
17026
17032
|
var getSparkFrostModule = async () => {
|
|
17027
17033
|
if (isReactNative) {
|
|
@@ -17487,16 +17493,16 @@ var DefaultSparkSigner = class {
|
|
|
17487
17493
|
}
|
|
17488
17494
|
if (receipt) {
|
|
17489
17495
|
const receiptPrivateKey = this.getReceiptPrivateKey(receipt);
|
|
17490
|
-
const tweakedKeyPair = (0,
|
|
17496
|
+
const tweakedKeyPair = (0, import_lrc20_sdk.fromPrivateKey)(import_buffer.Buffer.from(receiptPrivateKey));
|
|
17491
17497
|
psbt.signInput(input, tweakedKeyPair, sighashTypes);
|
|
17492
17498
|
return psbt;
|
|
17493
17499
|
}
|
|
17494
|
-
const keypair = (0,
|
|
17500
|
+
const keypair = (0, import_lrc20_sdk.fromPrivateKey)(import_buffer.Buffer.from(this.identityKey.privateKey));
|
|
17495
17501
|
psbt.signInput(input, keypair, sighashTypes);
|
|
17496
17502
|
return psbt;
|
|
17497
17503
|
}
|
|
17498
17504
|
getReceiptPrivateKey(receipt) {
|
|
17499
|
-
const pxh =
|
|
17505
|
+
const pxh = import_lrc20_sdk.Receipt.receiptHash(receipt);
|
|
17500
17506
|
let innerKey = this.identityKey.publicKey;
|
|
17501
17507
|
let privateKey = this.identityKey.privateKey;
|
|
17502
17508
|
if (innerKey[0] === 3) {
|
|
@@ -17507,11 +17513,30 @@ var DefaultSparkSigner = class {
|
|
|
17507
17513
|
const receiptProof = (0, import_secp256k16.privateAdd)(privateKey, pxhPubkey);
|
|
17508
17514
|
return import_buffer.Buffer.from(receiptProof);
|
|
17509
17515
|
}
|
|
17516
|
+
signTransactionIndex(tx, index, publicKey) {
|
|
17517
|
+
let privateKey;
|
|
17518
|
+
if ((0, import_utils4.equalBytes)(publicKey, this.identityKey?.publicKey ?? new Uint8Array())) {
|
|
17519
|
+
privateKey = this.identityKey?.privateKey;
|
|
17520
|
+
} else if ((0, import_utils4.equalBytes)(publicKey, this.depositKey?.publicKey ?? new Uint8Array())) {
|
|
17521
|
+
privateKey = this.depositKey?.privateKey;
|
|
17522
|
+
} else {
|
|
17523
|
+
privateKey = (0, import_utils4.hexToBytes)(
|
|
17524
|
+
this.publicKeyToPrivateKeyMap.get((0, import_utils4.bytesToHex)(publicKey)) ?? ""
|
|
17525
|
+
);
|
|
17526
|
+
}
|
|
17527
|
+
if (!privateKey) {
|
|
17528
|
+
throw new ValidationError("Private key not found for public key", {
|
|
17529
|
+
field: "privateKey",
|
|
17530
|
+
value: (0, import_utils4.bytesToHex)(publicKey)
|
|
17531
|
+
});
|
|
17532
|
+
}
|
|
17533
|
+
tx.signIdx(privateKey, index);
|
|
17534
|
+
}
|
|
17510
17535
|
};
|
|
17511
17536
|
|
|
17512
17537
|
// src/utils/network.ts
|
|
17513
17538
|
init_buffer();
|
|
17514
|
-
var
|
|
17539
|
+
var import_lrc20_sdk2 = require("@buildonspark/lrc20-sdk");
|
|
17515
17540
|
var btc = __toESM(require("@scure/btc-signer"), 1);
|
|
17516
17541
|
var bitcoin = __toESM(require("bitcoinjs-lib"), 1);
|
|
17517
17542
|
var Network2 = /* @__PURE__ */ ((Network5) => {
|
|
@@ -17545,11 +17570,11 @@ var LRC_WALLET_NETWORK = Object.freeze({
|
|
|
17545
17570
|
[4 /* LOCAL */]: bitcoin.networks.regtest
|
|
17546
17571
|
});
|
|
17547
17572
|
var LRC_WALLET_NETWORK_TYPE = Object.freeze({
|
|
17548
|
-
[0 /* MAINNET */]:
|
|
17549
|
-
[1 /* TESTNET */]:
|
|
17550
|
-
[2 /* SIGNET */]:
|
|
17551
|
-
[3 /* REGTEST */]:
|
|
17552
|
-
[4 /* LOCAL */]:
|
|
17573
|
+
[0 /* MAINNET */]: import_lrc20_sdk2.NetworkType.MAINNET,
|
|
17574
|
+
[1 /* TESTNET */]: import_lrc20_sdk2.NetworkType.TESTNET,
|
|
17575
|
+
[2 /* SIGNET */]: import_lrc20_sdk2.NetworkType.TESTNET,
|
|
17576
|
+
[3 /* REGTEST */]: import_lrc20_sdk2.NetworkType.REGTEST,
|
|
17577
|
+
[4 /* LOCAL */]: import_lrc20_sdk2.NetworkType.LOCAL
|
|
17553
17578
|
});
|
|
17554
17579
|
function getNetworkFromAddress(address2) {
|
|
17555
17580
|
try {
|
|
@@ -17572,6 +17597,14 @@ function getNetworkFromAddress(address2) {
|
|
|
17572
17597
|
}
|
|
17573
17598
|
return null;
|
|
17574
17599
|
}
|
|
17600
|
+
function getNetworkFromString(network) {
|
|
17601
|
+
const net = (network ?? "REGTEST").toUpperCase();
|
|
17602
|
+
if (net === "MAINNET") return 0 /* MAINNET */;
|
|
17603
|
+
if (net === "TESTNET") return 1 /* TESTNET */;
|
|
17604
|
+
if (net === "SIGNET") return 2 /* SIGNET */;
|
|
17605
|
+
if (net === "LOCAL") return 4 /* LOCAL */;
|
|
17606
|
+
return 3 /* REGTEST */;
|
|
17607
|
+
}
|
|
17575
17608
|
|
|
17576
17609
|
// src/services/wallet-config.ts
|
|
17577
17610
|
init_buffer();
|
|
@@ -19129,8 +19162,20 @@ function getTxIdNoReverse(tx) {
|
|
|
19129
19162
|
init_buffer();
|
|
19130
19163
|
var import_btc_signer = require("@scure/btc-signer");
|
|
19131
19164
|
var TIME_LOCK_INTERVAL = 100;
|
|
19165
|
+
var ESTIMATED_TX_SIZE = 191;
|
|
19166
|
+
var DEFAULT_SATS_PER_VBYTE = 5;
|
|
19167
|
+
var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
|
|
19168
|
+
function maybeApplyFee(amount) {
|
|
19169
|
+
if (amount > BigInt(DEFAULT_FEE_SATS)) {
|
|
19170
|
+
return amount - BigInt(DEFAULT_FEE_SATS);
|
|
19171
|
+
}
|
|
19172
|
+
return amount;
|
|
19173
|
+
}
|
|
19132
19174
|
function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
|
|
19133
|
-
const newRefundTx = new import_btc_signer.Transaction({
|
|
19175
|
+
const newRefundTx = new import_btc_signer.Transaction({
|
|
19176
|
+
version: 3,
|
|
19177
|
+
allowUnknownOutputs: true
|
|
19178
|
+
});
|
|
19134
19179
|
newRefundTx.addInput({
|
|
19135
19180
|
...nodeOutPoint,
|
|
19136
19181
|
sequence
|
|
@@ -19159,7 +19204,7 @@ function getNextTransactionSequence(currSequence, forRefresh) {
|
|
|
19159
19204
|
needRefresh: true
|
|
19160
19205
|
};
|
|
19161
19206
|
}
|
|
19162
|
-
if (nextTimelock
|
|
19207
|
+
if (nextTimelock < 0) {
|
|
19163
19208
|
throw new ValidationError("timelock interval is less than or equal to 0", {
|
|
19164
19209
|
field: "nextTimelock",
|
|
19165
19210
|
value: nextTimelock
|
|
@@ -19270,6 +19315,32 @@ var BaseTransferService = class {
|
|
|
19270
19315
|
}
|
|
19271
19316
|
return updatedTransfer;
|
|
19272
19317
|
}
|
|
19318
|
+
async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
|
|
19319
|
+
const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
|
|
19320
|
+
transfer.id,
|
|
19321
|
+
transfer.receiverIdentityPublicKey,
|
|
19322
|
+
leaves,
|
|
19323
|
+
refundSignatureMap
|
|
19324
|
+
);
|
|
19325
|
+
const transferPackage = await this.prepareTransferPackage(
|
|
19326
|
+
transfer.id,
|
|
19327
|
+
keyTweakInputMap,
|
|
19328
|
+
leaves,
|
|
19329
|
+
transfer.receiverIdentityPublicKey
|
|
19330
|
+
);
|
|
19331
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
19332
|
+
this.config.getCoordinatorAddress()
|
|
19333
|
+
);
|
|
19334
|
+
const response = await sparkClient.finalize_transfer_with_transfer_package({
|
|
19335
|
+
transferId: transfer.id,
|
|
19336
|
+
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
19337
|
+
transferPackage
|
|
19338
|
+
});
|
|
19339
|
+
if (!response.transfer) {
|
|
19340
|
+
throw new ValidationError("No transfer response from operator");
|
|
19341
|
+
}
|
|
19342
|
+
return response.transfer;
|
|
19343
|
+
}
|
|
19273
19344
|
async sendTransferWithKeyTweaks(leaves, receiverIdentityPubkey) {
|
|
19274
19345
|
const transferID = (0, import_uuidv7.uuidv7)();
|
|
19275
19346
|
const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
|
|
@@ -20027,9 +20098,20 @@ var TransferService = class extends BaseTransferService {
|
|
|
20027
20098
|
if (!input) {
|
|
20028
20099
|
throw Error("Could not fetch tx input");
|
|
20029
20100
|
}
|
|
20030
|
-
const newTx = new import_btc_signer2.Transaction({ allowUnknownOutputs: true });
|
|
20031
|
-
|
|
20032
|
-
|
|
20101
|
+
const newTx = new import_btc_signer2.Transaction({ version: 3, allowUnknownOutputs: true });
|
|
20102
|
+
const originalOutput = nodeTx.getOutput(0);
|
|
20103
|
+
if (!originalOutput) {
|
|
20104
|
+
throw Error("Could not get original output");
|
|
20105
|
+
}
|
|
20106
|
+
newTx.addOutput({
|
|
20107
|
+
script: originalOutput.script,
|
|
20108
|
+
amount: originalOutput.amount
|
|
20109
|
+
});
|
|
20110
|
+
for (let j = 1; j < nodeTx.outputsLength; j++) {
|
|
20111
|
+
const additionalOutput = nodeTx.getOutput(j);
|
|
20112
|
+
if (additionalOutput) {
|
|
20113
|
+
newTx.addOutput(additionalOutput);
|
|
20114
|
+
}
|
|
20033
20115
|
}
|
|
20034
20116
|
if (i === 0) {
|
|
20035
20117
|
const currSequence = input.sequence;
|
|
@@ -20056,9 +20138,23 @@ var TransferService = class extends BaseTransferService {
|
|
|
20056
20138
|
throw Error("leaf does not have refund tx");
|
|
20057
20139
|
}
|
|
20058
20140
|
const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
|
|
20059
|
-
const newRefundTx = new import_btc_signer2.Transaction({
|
|
20060
|
-
|
|
20061
|
-
|
|
20141
|
+
const newRefundTx = new import_btc_signer2.Transaction({
|
|
20142
|
+
version: 3,
|
|
20143
|
+
allowUnknownOutputs: true
|
|
20144
|
+
});
|
|
20145
|
+
const originalRefundOutput = refundTx.getOutput(0);
|
|
20146
|
+
if (!originalRefundOutput) {
|
|
20147
|
+
throw Error("Could not get original refund output");
|
|
20148
|
+
}
|
|
20149
|
+
newRefundTx.addOutput({
|
|
20150
|
+
script: originalRefundOutput.script,
|
|
20151
|
+
amount: originalRefundOutput.amount
|
|
20152
|
+
});
|
|
20153
|
+
for (let j = 1; j < refundTx.outputsLength; j++) {
|
|
20154
|
+
const additionalOutput = refundTx.getOutput(j);
|
|
20155
|
+
if (additionalOutput) {
|
|
20156
|
+
newRefundTx.addOutput(additionalOutput);
|
|
20157
|
+
}
|
|
20062
20158
|
}
|
|
20063
20159
|
const refundTxInput = refundTx.getInput(0);
|
|
20064
20160
|
if (!refundTxInput) {
|
|
@@ -20167,10 +20263,11 @@ var TransferService = class extends BaseTransferService {
|
|
|
20167
20263
|
nodeTxSignature: leafSignature,
|
|
20168
20264
|
refundTxSignature: refundSignature
|
|
20169
20265
|
});
|
|
20170
|
-
|
|
20266
|
+
const result = await sparkClient.finalize_node_signatures({
|
|
20171
20267
|
intent: 3 /* REFRESH */,
|
|
20172
20268
|
nodeSignatures
|
|
20173
20269
|
});
|
|
20270
|
+
return result;
|
|
20174
20271
|
}
|
|
20175
20272
|
async extendTimelock(node, signingPubKey) {
|
|
20176
20273
|
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
@@ -20181,9 +20278,20 @@ var TransferService = class extends BaseTransferService {
|
|
|
20181
20278
|
index: 0
|
|
20182
20279
|
};
|
|
20183
20280
|
const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
|
|
20184
|
-
const newNodeTx = new import_btc_signer2.Transaction({
|
|
20281
|
+
const newNodeTx = new import_btc_signer2.Transaction({
|
|
20282
|
+
version: 3,
|
|
20283
|
+
allowUnknownOutputs: true
|
|
20284
|
+
});
|
|
20185
20285
|
newNodeTx.addInput({ ...newNodeOutPoint, sequence: newNodeSequence });
|
|
20186
|
-
|
|
20286
|
+
const originalOutput = nodeTx.getOutput(0);
|
|
20287
|
+
if (!originalOutput) {
|
|
20288
|
+
throw Error("Could not get original node output");
|
|
20289
|
+
}
|
|
20290
|
+
newNodeTx.addOutput({
|
|
20291
|
+
script: originalOutput.script,
|
|
20292
|
+
amount: originalOutput.amount
|
|
20293
|
+
// feeReducedAmount,
|
|
20294
|
+
});
|
|
20187
20295
|
newNodeTx.addOutput(getEphemeralAnchorOutput());
|
|
20188
20296
|
const newRefundOutPoint = {
|
|
20189
20297
|
txid: (0, import_utils7.hexToBytes)(getTxId(newNodeTx)),
|
|
@@ -20197,11 +20305,16 @@ var TransferService = class extends BaseTransferService {
|
|
|
20197
20305
|
initialSequence(),
|
|
20198
20306
|
newRefundOutPoint,
|
|
20199
20307
|
amountSats,
|
|
20308
|
+
// feeReducedRefundAmount,
|
|
20200
20309
|
signingPubKey,
|
|
20201
20310
|
this.config.getNetwork()
|
|
20202
20311
|
);
|
|
20203
20312
|
const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
|
|
20204
|
-
const refundSighash = getSigHashFromTx(
|
|
20313
|
+
const refundSighash = getSigHashFromTx(
|
|
20314
|
+
newRefundTx,
|
|
20315
|
+
0,
|
|
20316
|
+
newNodeTx.getOutput(0)
|
|
20317
|
+
);
|
|
20205
20318
|
const newNodeSigningJob = {
|
|
20206
20319
|
signingPublicKey: signingPubKey,
|
|
20207
20320
|
rawTx: newNodeTx.toBytes(),
|
|
@@ -20275,6 +20388,94 @@ var TransferService = class extends BaseTransferService {
|
|
|
20275
20388
|
]
|
|
20276
20389
|
});
|
|
20277
20390
|
}
|
|
20391
|
+
async refreshTimelockRefundTx(node, signingPubKey) {
|
|
20392
|
+
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
20393
|
+
const refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
20394
|
+
const currSequence = refundTx.getInput(0).sequence || 0;
|
|
20395
|
+
const { nextSequence } = getNextTransactionSequence(currSequence);
|
|
20396
|
+
const newRefundTx = new import_btc_signer2.Transaction({
|
|
20397
|
+
version: 3,
|
|
20398
|
+
allowUnknownOutputs: true
|
|
20399
|
+
});
|
|
20400
|
+
const originalRefundOutput = refundTx.getOutput(0);
|
|
20401
|
+
if (!originalRefundOutput) {
|
|
20402
|
+
throw Error("Could not get original refund output");
|
|
20403
|
+
}
|
|
20404
|
+
newRefundTx.addOutput({
|
|
20405
|
+
script: originalRefundOutput.script,
|
|
20406
|
+
amount: originalRefundOutput.amount
|
|
20407
|
+
});
|
|
20408
|
+
for (let j = 1; j < refundTx.outputsLength; j++) {
|
|
20409
|
+
const additionalOutput = refundTx.getOutput(j);
|
|
20410
|
+
if (additionalOutput) {
|
|
20411
|
+
newRefundTx.addOutput(additionalOutput);
|
|
20412
|
+
}
|
|
20413
|
+
}
|
|
20414
|
+
const refundTxInput = refundTx.getInput(0);
|
|
20415
|
+
if (!refundTxInput) {
|
|
20416
|
+
throw Error("refund tx doesn't have input");
|
|
20417
|
+
}
|
|
20418
|
+
newRefundTx.addInput({
|
|
20419
|
+
...refundTxInput,
|
|
20420
|
+
sequence: nextSequence
|
|
20421
|
+
});
|
|
20422
|
+
const refundSigningJob = {
|
|
20423
|
+
signingPublicKey: signingPubKey,
|
|
20424
|
+
rawTx: newRefundTx.toBytes(),
|
|
20425
|
+
signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
|
|
20426
|
+
};
|
|
20427
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
20428
|
+
this.config.getCoordinatorAddress()
|
|
20429
|
+
);
|
|
20430
|
+
const response = await sparkClient.refresh_timelock({
|
|
20431
|
+
leafId: node.id,
|
|
20432
|
+
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
20433
|
+
signingJobs: [refundSigningJob]
|
|
20434
|
+
});
|
|
20435
|
+
if (response.signingResults.length !== 1) {
|
|
20436
|
+
throw Error(
|
|
20437
|
+
`Expected 1 signing result, got ${response.signingResults.length}`
|
|
20438
|
+
);
|
|
20439
|
+
}
|
|
20440
|
+
const signingResult = response.signingResults[0];
|
|
20441
|
+
if (!signingResult || !refundSigningJob.signingNonceCommitment) {
|
|
20442
|
+
throw Error("Signing result or nonce commitment does not exist");
|
|
20443
|
+
}
|
|
20444
|
+
const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
|
|
20445
|
+
const txOut = nodeTx.getOutput(0);
|
|
20446
|
+
const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
|
|
20447
|
+
const userSignature = await this.config.signer.signFrost({
|
|
20448
|
+
message: rawTxSighash,
|
|
20449
|
+
privateAsPubKey: signingPubKey,
|
|
20450
|
+
publicKey: signingPubKey,
|
|
20451
|
+
verifyingKey: signingResult.verifyingKey,
|
|
20452
|
+
selfCommitment: refundSigningJob.signingNonceCommitment,
|
|
20453
|
+
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
20454
|
+
adaptorPubKey: new Uint8Array()
|
|
20455
|
+
});
|
|
20456
|
+
const signature = await this.config.signer.aggregateFrost({
|
|
20457
|
+
message: rawTxSighash,
|
|
20458
|
+
statechainSignatures: signingResult.signingResult?.signatureShares,
|
|
20459
|
+
statechainPublicKeys: signingResult.signingResult?.publicKeys,
|
|
20460
|
+
verifyingKey: signingResult.verifyingKey,
|
|
20461
|
+
statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
|
|
20462
|
+
selfCommitment: refundSigningJob.signingNonceCommitment,
|
|
20463
|
+
publicKey: signingPubKey,
|
|
20464
|
+
selfSignature: userSignature,
|
|
20465
|
+
adaptorPubKey: new Uint8Array()
|
|
20466
|
+
});
|
|
20467
|
+
const result = await sparkClient.finalize_node_signatures({
|
|
20468
|
+
intent: 3 /* REFRESH */,
|
|
20469
|
+
nodeSignatures: [
|
|
20470
|
+
{
|
|
20471
|
+
nodeId: node.id,
|
|
20472
|
+
nodeTxSignature: new Uint8Array(),
|
|
20473
|
+
refundTxSignature: signature
|
|
20474
|
+
}
|
|
20475
|
+
]
|
|
20476
|
+
});
|
|
20477
|
+
return result;
|
|
20478
|
+
}
|
|
20278
20479
|
};
|
|
20279
20480
|
|
|
20280
20481
|
// src/services/coop-exit.ts
|
|
@@ -20578,7 +20779,7 @@ var DepositService = class {
|
|
|
20578
20779
|
depositTx,
|
|
20579
20780
|
vout
|
|
20580
20781
|
}) {
|
|
20581
|
-
const rootTx = new import_btc_signer4.Transaction();
|
|
20782
|
+
const rootTx = new import_btc_signer4.Transaction({ version: 3 });
|
|
20582
20783
|
const output = depositTx.getOutput(vout);
|
|
20583
20784
|
if (!output) {
|
|
20584
20785
|
throw new ValidationError("Invalid deposit transaction output", {
|
|
@@ -20596,17 +20797,19 @@ var DepositService = class {
|
|
|
20596
20797
|
expected: "Output with script and amount"
|
|
20597
20798
|
});
|
|
20598
20799
|
}
|
|
20800
|
+
let outputAmount = amount;
|
|
20599
20801
|
rootTx.addInput({
|
|
20600
20802
|
txid: getTxId(depositTx),
|
|
20601
20803
|
index: vout
|
|
20602
20804
|
});
|
|
20603
20805
|
rootTx.addOutput({
|
|
20604
20806
|
script,
|
|
20605
|
-
amount
|
|
20807
|
+
amount: outputAmount
|
|
20606
20808
|
});
|
|
20809
|
+
rootTx.addOutput(getEphemeralAnchorOutput());
|
|
20607
20810
|
const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
20608
20811
|
const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
|
|
20609
|
-
const refundTx = new import_btc_signer4.Transaction();
|
|
20812
|
+
const refundTx = new import_btc_signer4.Transaction({ version: 3 });
|
|
20610
20813
|
const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
|
|
20611
20814
|
refundTx.addInput({
|
|
20612
20815
|
txid: getTxId(rootTx),
|
|
@@ -20621,10 +20824,11 @@ var DepositService = class {
|
|
|
20621
20824
|
const refundPkScript = btc3.OutScript.encode(refundAddress);
|
|
20622
20825
|
refundTx.addOutput({
|
|
20623
20826
|
script: refundPkScript,
|
|
20624
|
-
amount
|
|
20827
|
+
amount: outputAmount
|
|
20625
20828
|
});
|
|
20829
|
+
refundTx.addOutput(getEphemeralAnchorOutput());
|
|
20626
20830
|
const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
20627
|
-
const refundTxSighash = getSigHashFromTx(refundTx, 0,
|
|
20831
|
+
const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
|
|
20628
20832
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
20629
20833
|
this.config.getCoordinatorAddress()
|
|
20630
20834
|
);
|
|
@@ -25132,7 +25336,7 @@ function isValidPublicKey(publicKey) {
|
|
|
25132
25336
|
}
|
|
25133
25337
|
|
|
25134
25338
|
// src/services/token-transactions.ts
|
|
25135
|
-
var MAX_TOKEN_OUTPUTS =
|
|
25339
|
+
var MAX_TOKEN_OUTPUTS = 500;
|
|
25136
25340
|
var TokenTransactionService = class {
|
|
25137
25341
|
config;
|
|
25138
25342
|
connectionManager;
|
|
@@ -25697,6 +25901,12 @@ var import_utils15 = require("@noble/curves/abstract/utils");
|
|
|
25697
25901
|
var import_sha212 = require("@noble/hashes/sha2");
|
|
25698
25902
|
var import_btc_signer5 = require("@scure/btc-signer");
|
|
25699
25903
|
var INITIAL_TIME_LOCK3 = 2e3;
|
|
25904
|
+
function maybeApplyFee3(amount) {
|
|
25905
|
+
if (amount > BigInt(DEFAULT_FEE_SATS)) {
|
|
25906
|
+
return amount - BigInt(DEFAULT_FEE_SATS);
|
|
25907
|
+
}
|
|
25908
|
+
return amount;
|
|
25909
|
+
}
|
|
25700
25910
|
var TreeCreationService = class {
|
|
25701
25911
|
config;
|
|
25702
25912
|
connectionManager;
|
|
@@ -25895,7 +26105,7 @@ var TreeCreationService = class {
|
|
|
25895
26105
|
refundTxSigningJob: void 0,
|
|
25896
26106
|
children: []
|
|
25897
26107
|
};
|
|
25898
|
-
const tx = new import_btc_signer5.Transaction();
|
|
26108
|
+
const tx = new import_btc_signer5.Transaction({ version: 3 });
|
|
25899
26109
|
tx.addInput({
|
|
25900
26110
|
txid: getTxId(parentTx),
|
|
25901
26111
|
index: vout
|
|
@@ -25907,6 +26117,7 @@ var TreeCreationService = class {
|
|
|
25907
26117
|
tx.addOutput({
|
|
25908
26118
|
script: parentTxOut.script,
|
|
25909
26119
|
amount: parentTxOut.amount
|
|
26120
|
+
// maybeApplyFee(parentTxOut.amount),
|
|
25910
26121
|
});
|
|
25911
26122
|
tx.addOutput(getEphemeralAnchorOutput());
|
|
25912
26123
|
const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
@@ -25923,7 +26134,7 @@ var TreeCreationService = class {
|
|
|
25923
26134
|
refundTxSigningJob: void 0,
|
|
25924
26135
|
children: []
|
|
25925
26136
|
};
|
|
25926
|
-
const childTx = new import_btc_signer5.Transaction();
|
|
26137
|
+
const childTx = new import_btc_signer5.Transaction({ version: 3 });
|
|
25927
26138
|
childTx.addInput({
|
|
25928
26139
|
txid: getTxId(tx),
|
|
25929
26140
|
index: 0,
|
|
@@ -25932,6 +26143,7 @@ var TreeCreationService = class {
|
|
|
25932
26143
|
childTx.addOutput({
|
|
25933
26144
|
script: parentTxOut.script,
|
|
25934
26145
|
amount: parentTxOut.amount
|
|
26146
|
+
// maybeApplyFee(parentTxOut.amount),
|
|
25935
26147
|
});
|
|
25936
26148
|
childTx.addOutput(getEphemeralAnchorOutput());
|
|
25937
26149
|
const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
@@ -25942,7 +26154,7 @@ var TreeCreationService = class {
|
|
|
25942
26154
|
};
|
|
25943
26155
|
childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
|
|
25944
26156
|
childCreationNode.nodeTxSigningJob = childSigningJob;
|
|
25945
|
-
const refundTx = new import_btc_signer5.Transaction();
|
|
26157
|
+
const refundTx = new import_btc_signer5.Transaction({ version: 3 });
|
|
25946
26158
|
refundTx.addInput({
|
|
25947
26159
|
txid: getTxId(childTx),
|
|
25948
26160
|
index: 0,
|
|
@@ -25958,8 +26170,9 @@ var TreeCreationService = class {
|
|
|
25958
26170
|
const refundPkScript = import_btc_signer5.OutScript.encode(refundAddress);
|
|
25959
26171
|
refundTx.addOutput({
|
|
25960
26172
|
script: refundPkScript,
|
|
25961
|
-
amount: parentTxOut.amount
|
|
26173
|
+
amount: maybeApplyFee3(parentTxOut.amount)
|
|
25962
26174
|
});
|
|
26175
|
+
refundTx.addOutput(getEphemeralAnchorOutput());
|
|
25963
26176
|
const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
25964
26177
|
const refundSigningJob = {
|
|
25965
26178
|
signingPublicKey: node.signingPublicKey,
|
|
@@ -25976,7 +26189,7 @@ var TreeCreationService = class {
|
|
|
25976
26189
|
if (!parentTxOutput?.script || !parentTxOutput?.amount) {
|
|
25977
26190
|
throw new Error("parentTxOutput is undefined");
|
|
25978
26191
|
}
|
|
25979
|
-
const rootNodeTx = new import_btc_signer5.Transaction();
|
|
26192
|
+
const rootNodeTx = new import_btc_signer5.Transaction({ version: 3 });
|
|
25980
26193
|
rootNodeTx.addInput({
|
|
25981
26194
|
txid: getTxId(parentTx),
|
|
25982
26195
|
index: vout
|
|
@@ -25991,10 +26204,10 @@ var TreeCreationService = class {
|
|
|
25991
26204
|
rootNodeTx.addOutput({
|
|
25992
26205
|
script: childPkScript,
|
|
25993
26206
|
amount: parentTxOutput.amount / 2n
|
|
26207
|
+
// feeAdjustedAmount / 2n,
|
|
25994
26208
|
});
|
|
25995
26209
|
}
|
|
25996
|
-
|
|
25997
|
-
rootNodeTx.addOutput(anchor);
|
|
26210
|
+
rootNodeTx.addOutput(getEphemeralAnchorOutput());
|
|
25998
26211
|
const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
|
|
25999
26212
|
const rootNodeSigningJob = {
|
|
26000
26213
|
signingPublicKey: root.signingPublicKey,
|
|
@@ -26145,7 +26358,7 @@ var TreeCreationService = class {
|
|
|
26145
26358
|
};
|
|
26146
26359
|
|
|
26147
26360
|
// src/spark-wallet/spark-wallet.ts
|
|
26148
|
-
var
|
|
26361
|
+
var import_lrc20_sdk3 = require("@buildonspark/lrc20-sdk");
|
|
26149
26362
|
var import_sha213 = require("@noble/hashes/sha2");
|
|
26150
26363
|
var import_eventemitter3 = require("eventemitter3");
|
|
26151
26364
|
|
|
@@ -27054,7 +27267,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
27054
27267
|
}
|
|
27055
27268
|
await this.initWalletFromSeed(seed, accountNumber);
|
|
27056
27269
|
const network = this.config.getNetwork();
|
|
27057
|
-
this.lrc20Wallet = await
|
|
27270
|
+
this.lrc20Wallet = await import_lrc20_sdk3.LRCWallet.create(
|
|
27058
27271
|
LRC_WALLET_NETWORK[network],
|
|
27059
27272
|
LRC_WALLET_NETWORK_TYPE[network],
|
|
27060
27273
|
this.config.lrc20ApiConfig,
|
|
@@ -27253,7 +27466,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
27253
27466
|
adaptorPrivateKey
|
|
27254
27467
|
);
|
|
27255
27468
|
}
|
|
27256
|
-
await this.transferService.
|
|
27469
|
+
await this.transferService.deliverTransferPackage(
|
|
27257
27470
|
transfer,
|
|
27258
27471
|
leafKeyTweaks,
|
|
27259
27472
|
signatureMap
|
|
@@ -28938,6 +29151,112 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
28938
29151
|
}
|
|
28939
29152
|
return this.config.signer.validateMessageWithIdentityKey(hash, signature);
|
|
28940
29153
|
}
|
|
29154
|
+
/**
|
|
29155
|
+
* Signs a transaction with wallet keys.
|
|
29156
|
+
*
|
|
29157
|
+
* @param {string} txHex - The transaction hex to sign
|
|
29158
|
+
* @param {string} keyType - The type of key to use for signing ("identity", "deposit", or "auto-detect")
|
|
29159
|
+
* @returns {Promise<string>} The signed transaction hex
|
|
29160
|
+
*/
|
|
29161
|
+
async signTransaction(txHex, keyType = "auto-detect") {
|
|
29162
|
+
try {
|
|
29163
|
+
const tx = import_btc_signer7.Transaction.fromRaw((0, import_utils20.hexToBytes)(txHex));
|
|
29164
|
+
let publicKey;
|
|
29165
|
+
switch (keyType.toLowerCase()) {
|
|
29166
|
+
case "identity":
|
|
29167
|
+
publicKey = await this.config.signer.getIdentityPublicKey();
|
|
29168
|
+
break;
|
|
29169
|
+
case "deposit":
|
|
29170
|
+
publicKey = await this.config.signer.getDepositSigningKey();
|
|
29171
|
+
break;
|
|
29172
|
+
case "auto-detect":
|
|
29173
|
+
default:
|
|
29174
|
+
const detectedKey = await this.detectKeyForTransaction(tx);
|
|
29175
|
+
if (detectedKey) {
|
|
29176
|
+
publicKey = detectedKey.publicKey;
|
|
29177
|
+
} else {
|
|
29178
|
+
publicKey = await this.config.signer.getIdentityPublicKey();
|
|
29179
|
+
}
|
|
29180
|
+
break;
|
|
29181
|
+
}
|
|
29182
|
+
let inputsSigned = 0;
|
|
29183
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
29184
|
+
const input = tx.getInput(i);
|
|
29185
|
+
if (!input?.witnessUtxo?.script) {
|
|
29186
|
+
continue;
|
|
29187
|
+
}
|
|
29188
|
+
const script = input.witnessUtxo.script;
|
|
29189
|
+
if (script.length === 1 && script[0] === 81) {
|
|
29190
|
+
continue;
|
|
29191
|
+
}
|
|
29192
|
+
const identityScript = getP2TRScriptFromPublicKey(
|
|
29193
|
+
publicKey,
|
|
29194
|
+
this.config.getNetwork()
|
|
29195
|
+
);
|
|
29196
|
+
if ((0, import_utils20.bytesToHex)(script) === (0, import_utils20.bytesToHex)(identityScript)) {
|
|
29197
|
+
try {
|
|
29198
|
+
this.config.signer.signTransactionIndex(tx, i, publicKey);
|
|
29199
|
+
inputsSigned++;
|
|
29200
|
+
} catch (error) {
|
|
29201
|
+
throw new ValidationError(`Failed to sign input ${i}: ${error}`, {
|
|
29202
|
+
field: "input",
|
|
29203
|
+
value: i
|
|
29204
|
+
});
|
|
29205
|
+
}
|
|
29206
|
+
}
|
|
29207
|
+
}
|
|
29208
|
+
if (inputsSigned === 0) {
|
|
29209
|
+
throw new Error(
|
|
29210
|
+
"No inputs were signed. Check that the transaction contains inputs controlled by this wallet."
|
|
29211
|
+
);
|
|
29212
|
+
}
|
|
29213
|
+
tx.finalize();
|
|
29214
|
+
const signedTxHex = tx.hex;
|
|
29215
|
+
return signedTxHex;
|
|
29216
|
+
} catch (error) {
|
|
29217
|
+
console.error("\u274C Error signing transaction:", error);
|
|
29218
|
+
throw error;
|
|
29219
|
+
}
|
|
29220
|
+
}
|
|
29221
|
+
/**
|
|
29222
|
+
* Helper method to auto-detect which key should be used for signing a transaction.
|
|
29223
|
+
*/
|
|
29224
|
+
async detectKeyForTransaction(tx) {
|
|
29225
|
+
try {
|
|
29226
|
+
const identityPubKey = await this.config.signer.getIdentityPublicKey();
|
|
29227
|
+
const depositPubKey = await this.config.signer.getDepositSigningKey();
|
|
29228
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
29229
|
+
const input = tx.getInput(i);
|
|
29230
|
+
if (input?.witnessUtxo?.script) {
|
|
29231
|
+
const script = input.witnessUtxo.script;
|
|
29232
|
+
const identityScript = getP2TRScriptFromPublicKey(
|
|
29233
|
+
identityPubKey,
|
|
29234
|
+
this.config.getNetwork()
|
|
29235
|
+
);
|
|
29236
|
+
const depositScript = getP2TRScriptFromPublicKey(
|
|
29237
|
+
depositPubKey,
|
|
29238
|
+
this.config.getNetwork()
|
|
29239
|
+
);
|
|
29240
|
+
if ((0, import_utils20.bytesToHex)(script) === (0, import_utils20.bytesToHex)(identityScript)) {
|
|
29241
|
+
return {
|
|
29242
|
+
publicKey: identityPubKey,
|
|
29243
|
+
keyType: "identity"
|
|
29244
|
+
};
|
|
29245
|
+
}
|
|
29246
|
+
if ((0, import_utils20.bytesToHex)(script) === (0, import_utils20.bytesToHex)(depositScript)) {
|
|
29247
|
+
return {
|
|
29248
|
+
publicKey: depositPubKey,
|
|
29249
|
+
keyType: "deposit"
|
|
29250
|
+
};
|
|
29251
|
+
}
|
|
29252
|
+
}
|
|
29253
|
+
}
|
|
29254
|
+
return null;
|
|
29255
|
+
} catch (error) {
|
|
29256
|
+
console.warn("Error during key auto-detection:", error);
|
|
29257
|
+
return null;
|
|
29258
|
+
}
|
|
29259
|
+
}
|
|
28941
29260
|
/**
|
|
28942
29261
|
* Get a Lightning receive request by ID.
|
|
28943
29262
|
*
|
|
@@ -28968,6 +29287,246 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
|
|
|
28968
29287
|
const sspClient = this.getSspClient();
|
|
28969
29288
|
return await sspClient.getCoopExitRequest(id);
|
|
28970
29289
|
}
|
|
29290
|
+
/**
|
|
29291
|
+
* Check the remaining timelock on a given node.
|
|
29292
|
+
*
|
|
29293
|
+
* @param {string} nodeId - The ID of the node to check
|
|
29294
|
+
* @returns {Promise<{nodeTimelock: number, refundTimelock: number}>} The remaining timelocks in blocks for both node and refund transactions
|
|
29295
|
+
*/
|
|
29296
|
+
async checkTimelock(nodeId) {
|
|
29297
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
29298
|
+
this.config.getCoordinatorAddress()
|
|
29299
|
+
);
|
|
29300
|
+
try {
|
|
29301
|
+
const response = await sparkClient.query_nodes({
|
|
29302
|
+
source: {
|
|
29303
|
+
$case: "nodeIds",
|
|
29304
|
+
nodeIds: {
|
|
29305
|
+
nodeIds: [nodeId]
|
|
29306
|
+
}
|
|
29307
|
+
},
|
|
29308
|
+
includeParents: false,
|
|
29309
|
+
network: NetworkToProto[this.config.getNetwork()]
|
|
29310
|
+
});
|
|
29311
|
+
const node = response.nodes[nodeId];
|
|
29312
|
+
if (!node) {
|
|
29313
|
+
throw new ValidationError("Node not found", {
|
|
29314
|
+
field: "nodeId",
|
|
29315
|
+
value: nodeId
|
|
29316
|
+
});
|
|
29317
|
+
}
|
|
29318
|
+
const isRootNode = !node.parentNodeId;
|
|
29319
|
+
if (!node.nodeTx || node.nodeTx.length === 0) {
|
|
29320
|
+
throw new ValidationError(
|
|
29321
|
+
`Node transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
|
|
29322
|
+
{
|
|
29323
|
+
field: "nodeTx",
|
|
29324
|
+
value: node.nodeTx?.length || 0
|
|
29325
|
+
}
|
|
29326
|
+
);
|
|
29327
|
+
}
|
|
29328
|
+
if (!node.refundTx || node.refundTx.length === 0) {
|
|
29329
|
+
throw new ValidationError(
|
|
29330
|
+
`Refund transaction data is missing or empty for ${isRootNode ? "root" : "non-root"} node`,
|
|
29331
|
+
{
|
|
29332
|
+
field: "refundTx",
|
|
29333
|
+
value: node.refundTx?.length || 0
|
|
29334
|
+
}
|
|
29335
|
+
);
|
|
29336
|
+
}
|
|
29337
|
+
let nodeTx, refundTx;
|
|
29338
|
+
try {
|
|
29339
|
+
nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
29340
|
+
} catch (error) {
|
|
29341
|
+
throw new ValidationError(
|
|
29342
|
+
`Failed to parse node transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
|
|
29343
|
+
{
|
|
29344
|
+
field: "nodeTx",
|
|
29345
|
+
value: node.nodeTx.length
|
|
29346
|
+
}
|
|
29347
|
+
);
|
|
29348
|
+
}
|
|
29349
|
+
try {
|
|
29350
|
+
refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
29351
|
+
} catch (error) {
|
|
29352
|
+
throw new ValidationError(
|
|
29353
|
+
`Failed to parse refund transaction for ${isRootNode ? "root" : "non-root"} node: ${error instanceof Error ? error.message : String(error)}`,
|
|
29354
|
+
{
|
|
29355
|
+
field: "refundTx",
|
|
29356
|
+
value: node.refundTx.length
|
|
29357
|
+
}
|
|
29358
|
+
);
|
|
29359
|
+
}
|
|
29360
|
+
const nodeInput = nodeTx.getInput(0);
|
|
29361
|
+
if (!nodeInput) {
|
|
29362
|
+
throw new ValidationError(
|
|
29363
|
+
`Node transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
|
|
29364
|
+
{
|
|
29365
|
+
field: "nodeInput",
|
|
29366
|
+
value: nodeTx.inputsLength
|
|
29367
|
+
}
|
|
29368
|
+
);
|
|
29369
|
+
}
|
|
29370
|
+
if (!nodeInput.sequence) {
|
|
29371
|
+
throw new ValidationError(
|
|
29372
|
+
`Node transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
|
|
29373
|
+
{
|
|
29374
|
+
field: "sequence",
|
|
29375
|
+
value: nodeInput.sequence
|
|
29376
|
+
}
|
|
29377
|
+
);
|
|
29378
|
+
}
|
|
29379
|
+
const refundInput = refundTx.getInput(0);
|
|
29380
|
+
if (!refundInput) {
|
|
29381
|
+
throw new ValidationError(
|
|
29382
|
+
`Refund transaction has no inputs for ${isRootNode ? "root" : "non-root"} node`,
|
|
29383
|
+
{
|
|
29384
|
+
field: "refundInput",
|
|
29385
|
+
value: refundTx.inputsLength
|
|
29386
|
+
}
|
|
29387
|
+
);
|
|
29388
|
+
}
|
|
29389
|
+
if (!refundInput.sequence) {
|
|
29390
|
+
throw new ValidationError(
|
|
29391
|
+
`Refund transaction has no sequence for ${isRootNode ? "root" : "non-root"} node`,
|
|
29392
|
+
{
|
|
29393
|
+
field: "sequence",
|
|
29394
|
+
value: refundInput.sequence
|
|
29395
|
+
}
|
|
29396
|
+
);
|
|
29397
|
+
}
|
|
29398
|
+
const nodeTimelock = nodeInput.sequence & 65535;
|
|
29399
|
+
const refundTimelock = refundInput.sequence & 65535;
|
|
29400
|
+
return {
|
|
29401
|
+
nodeTimelock,
|
|
29402
|
+
refundTimelock
|
|
29403
|
+
};
|
|
29404
|
+
} catch (error) {
|
|
29405
|
+
throw new NetworkError(
|
|
29406
|
+
`Failed to check timelock for node ${nodeId}`,
|
|
29407
|
+
{
|
|
29408
|
+
method: "query_nodes"
|
|
29409
|
+
},
|
|
29410
|
+
error
|
|
29411
|
+
);
|
|
29412
|
+
}
|
|
29413
|
+
}
|
|
29414
|
+
/**
|
|
29415
|
+
* Refresh the timelock of a specific node.
|
|
29416
|
+
*
|
|
29417
|
+
* @param {string} nodeId - The ID of the node to refresh
|
|
29418
|
+
* @returns {Promise<void>} Promise that resolves when the timelock is refreshed
|
|
29419
|
+
*/
|
|
29420
|
+
async testOnly_expireTimelock(nodeId) {
|
|
29421
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
29422
|
+
this.config.getCoordinatorAddress()
|
|
29423
|
+
);
|
|
29424
|
+
try {
|
|
29425
|
+
const response = await sparkClient.query_nodes({
|
|
29426
|
+
source: {
|
|
29427
|
+
$case: "nodeIds",
|
|
29428
|
+
nodeIds: {
|
|
29429
|
+
nodeIds: [nodeId]
|
|
29430
|
+
}
|
|
29431
|
+
},
|
|
29432
|
+
includeParents: true
|
|
29433
|
+
});
|
|
29434
|
+
const node = response.nodes[nodeId];
|
|
29435
|
+
if (!node) {
|
|
29436
|
+
throw new ValidationError("Node not found", {
|
|
29437
|
+
field: "nodeId",
|
|
29438
|
+
value: nodeId
|
|
29439
|
+
});
|
|
29440
|
+
}
|
|
29441
|
+
if (!node.parentNodeId) {
|
|
29442
|
+
throw new ValidationError("Node has no parent", {
|
|
29443
|
+
field: "parentNodeId",
|
|
29444
|
+
value: node.parentNodeId
|
|
29445
|
+
});
|
|
29446
|
+
}
|
|
29447
|
+
const parentNode = response.nodes[node.parentNodeId];
|
|
29448
|
+
if (!parentNode) {
|
|
29449
|
+
throw new ValidationError("Parent node not found", {
|
|
29450
|
+
field: "parentNodeId",
|
|
29451
|
+
value: node.parentNodeId
|
|
29452
|
+
});
|
|
29453
|
+
}
|
|
29454
|
+
const signingPubKey = await this.config.signer.generatePublicKey(
|
|
29455
|
+
(0, import_sha213.sha256)(node.id)
|
|
29456
|
+
);
|
|
29457
|
+
const result = await this.transferService.refreshTimelockNodes(
|
|
29458
|
+
[node],
|
|
29459
|
+
parentNode,
|
|
29460
|
+
signingPubKey
|
|
29461
|
+
);
|
|
29462
|
+
const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
|
|
29463
|
+
if (leafIndex !== -1 && result.nodes.length > 0) {
|
|
29464
|
+
const newNode = result.nodes[0];
|
|
29465
|
+
if (newNode) {
|
|
29466
|
+
this.leaves[leafIndex] = newNode;
|
|
29467
|
+
}
|
|
29468
|
+
}
|
|
29469
|
+
} catch (error) {
|
|
29470
|
+
throw new NetworkError(
|
|
29471
|
+
"Failed to refresh timelock",
|
|
29472
|
+
{
|
|
29473
|
+
method: "refresh_timelock"
|
|
29474
|
+
},
|
|
29475
|
+
error
|
|
29476
|
+
);
|
|
29477
|
+
}
|
|
29478
|
+
}
|
|
29479
|
+
/**
|
|
29480
|
+
* Refresh the timelock of a specific node's refund transaction only.
|
|
29481
|
+
*
|
|
29482
|
+
* @param {string} nodeId - The ID of the node whose refund transaction to refresh
|
|
29483
|
+
* @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
|
|
29484
|
+
*/
|
|
29485
|
+
async testOnly_expireTimelockRefundTx(nodeId) {
|
|
29486
|
+
const sparkClient = await this.connectionManager.createSparkClient(
|
|
29487
|
+
this.config.getCoordinatorAddress()
|
|
29488
|
+
);
|
|
29489
|
+
try {
|
|
29490
|
+
const response = await sparkClient.query_nodes({
|
|
29491
|
+
source: {
|
|
29492
|
+
$case: "nodeIds",
|
|
29493
|
+
nodeIds: {
|
|
29494
|
+
nodeIds: [nodeId]
|
|
29495
|
+
}
|
|
29496
|
+
},
|
|
29497
|
+
includeParents: false
|
|
29498
|
+
});
|
|
29499
|
+
const node = response.nodes[nodeId];
|
|
29500
|
+
if (!node) {
|
|
29501
|
+
throw new ValidationError("Node not found", {
|
|
29502
|
+
field: "nodeId",
|
|
29503
|
+
value: nodeId
|
|
29504
|
+
});
|
|
29505
|
+
}
|
|
29506
|
+
const signingPubKey = await this.config.signer.generatePublicKey(
|
|
29507
|
+
(0, import_sha213.sha256)(node.id)
|
|
29508
|
+
);
|
|
29509
|
+
const result = await this.transferService.refreshTimelockRefundTx(
|
|
29510
|
+
node,
|
|
29511
|
+
signingPubKey
|
|
29512
|
+
);
|
|
29513
|
+
const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
|
|
29514
|
+
if (leafIndex !== -1 && result.nodes.length > 0) {
|
|
29515
|
+
const newNode = result.nodes[0];
|
|
29516
|
+
if (newNode) {
|
|
29517
|
+
this.leaves[leafIndex] = newNode;
|
|
29518
|
+
}
|
|
29519
|
+
}
|
|
29520
|
+
} catch (error) {
|
|
29521
|
+
throw new NetworkError(
|
|
29522
|
+
"Failed to refresh refund timelock",
|
|
29523
|
+
{
|
|
29524
|
+
method: "refresh_timelock_refund_tx"
|
|
29525
|
+
},
|
|
29526
|
+
error
|
|
29527
|
+
);
|
|
29528
|
+
}
|
|
29529
|
+
}
|
|
28971
29530
|
cleanup() {
|
|
28972
29531
|
if (this.claimTransfersInterval) {
|
|
28973
29532
|
clearInterval(this.claimTransfersInterval);
|
|
@@ -29062,10 +29621,473 @@ async function getLatestDepositTxId(address2) {
|
|
|
29062
29621
|
}
|
|
29063
29622
|
return null;
|
|
29064
29623
|
}
|
|
29624
|
+
async function isTxBroadcast(txid, baseUrl, network) {
|
|
29625
|
+
const headers = {};
|
|
29626
|
+
if (network === 3 /* REGTEST */) {
|
|
29627
|
+
const auth = btoa(
|
|
29628
|
+
`${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
|
|
29629
|
+
);
|
|
29630
|
+
headers["Authorization"] = `Basic ${auth}`;
|
|
29631
|
+
}
|
|
29632
|
+
const response = await fetch(`${baseUrl}/tx/${txid}`, {
|
|
29633
|
+
headers
|
|
29634
|
+
});
|
|
29635
|
+
const tx = await response.json();
|
|
29636
|
+
if (tx.error) {
|
|
29637
|
+
return false;
|
|
29638
|
+
}
|
|
29639
|
+
return true;
|
|
29640
|
+
}
|
|
29641
|
+
|
|
29642
|
+
// src/utils/unilateral-exit.ts
|
|
29643
|
+
init_buffer();
|
|
29644
|
+
var import_utils21 = require("@noble/curves/abstract/utils");
|
|
29645
|
+
var import_legacy = require("@noble/hashes/legacy");
|
|
29646
|
+
var import_sha214 = require("@noble/hashes/sha2");
|
|
29647
|
+
var btc5 = __toESM(require("@scure/btc-signer"), 1);
|
|
29648
|
+
function isEphemeralAnchorOutput(script, amount) {
|
|
29649
|
+
return Boolean(
|
|
29650
|
+
amount === 0n && script && // Pattern 1: Bare OP_TRUE (single byte 0x51)
|
|
29651
|
+
(script.length === 1 && script[0] === 81 || // Pattern 2: Push OP_TRUE (two bytes 0x01 0x51) - MALFORMED but we detect it
|
|
29652
|
+
script.length === 2 && script[0] === 1 && script[1] === 81 || // Pattern 3: Bitcoin v29 ephemeral anchor script (7 bytes: 015152014e0173)
|
|
29653
|
+
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)
|
|
29654
|
+
script.length === 4 && script[0] === 81 && script[1] === 2 && script[2] === 78 && script[3] === 115)
|
|
29655
|
+
);
|
|
29656
|
+
}
|
|
29657
|
+
async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
|
|
29658
|
+
const result = [];
|
|
29659
|
+
const nodes = nodeHexStrings.map((hex) => TreeNode.decode((0, import_utils21.hexToBytes)(hex)));
|
|
29660
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
29661
|
+
for (const node of nodes) {
|
|
29662
|
+
nodeMap.set(node.id, node);
|
|
29663
|
+
}
|
|
29664
|
+
for (const node of nodes) {
|
|
29665
|
+
const transactions = [];
|
|
29666
|
+
const chain = [];
|
|
29667
|
+
let currentNode = node;
|
|
29668
|
+
while (currentNode) {
|
|
29669
|
+
chain.unshift(currentNode);
|
|
29670
|
+
if (currentNode.parentNodeId) {
|
|
29671
|
+
let parentNode = nodeMap.get(currentNode.parentNodeId);
|
|
29672
|
+
if (!parentNode && sparkClient) {
|
|
29673
|
+
try {
|
|
29674
|
+
const response = await sparkClient.query_nodes({
|
|
29675
|
+
source: {
|
|
29676
|
+
$case: "nodeIds",
|
|
29677
|
+
nodeIds: {
|
|
29678
|
+
nodeIds: [currentNode.parentNodeId]
|
|
29679
|
+
}
|
|
29680
|
+
},
|
|
29681
|
+
includeParents: true,
|
|
29682
|
+
network: network || 0
|
|
29683
|
+
// Default to mainnet if not provided
|
|
29684
|
+
});
|
|
29685
|
+
parentNode = response.nodes[currentNode.parentNodeId];
|
|
29686
|
+
if (parentNode) {
|
|
29687
|
+
nodeMap.set(currentNode.parentNodeId, parentNode);
|
|
29688
|
+
}
|
|
29689
|
+
} catch (error) {
|
|
29690
|
+
console.warn(
|
|
29691
|
+
`Failed to query parent node ${currentNode.parentNodeId}: ${error}`
|
|
29692
|
+
);
|
|
29693
|
+
break;
|
|
29694
|
+
}
|
|
29695
|
+
}
|
|
29696
|
+
if (parentNode) {
|
|
29697
|
+
currentNode = parentNode;
|
|
29698
|
+
} else {
|
|
29699
|
+
if (!sparkClient) {
|
|
29700
|
+
console.warn(
|
|
29701
|
+
`Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
|
|
29702
|
+
);
|
|
29703
|
+
} else {
|
|
29704
|
+
console.warn(
|
|
29705
|
+
`Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
|
|
29706
|
+
);
|
|
29707
|
+
}
|
|
29708
|
+
break;
|
|
29709
|
+
}
|
|
29710
|
+
} else {
|
|
29711
|
+
break;
|
|
29712
|
+
}
|
|
29713
|
+
}
|
|
29714
|
+
for (const chainNode of chain) {
|
|
29715
|
+
const nodeTx = (0, import_utils21.bytesToHex)(chainNode.nodeTx);
|
|
29716
|
+
transactions.push(nodeTx);
|
|
29717
|
+
if (chainNode.id === node.id) {
|
|
29718
|
+
const refundTx = (0, import_utils21.bytesToHex)(chainNode.refundTx);
|
|
29719
|
+
transactions.push(refundTx);
|
|
29720
|
+
}
|
|
29721
|
+
}
|
|
29722
|
+
result.push({
|
|
29723
|
+
leafId: node.id,
|
|
29724
|
+
transactions
|
|
29725
|
+
});
|
|
29726
|
+
}
|
|
29727
|
+
return result;
|
|
29728
|
+
}
|
|
29729
|
+
async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, feeRate, electrsUrl, sparkClient, network) {
|
|
29730
|
+
const result = [];
|
|
29731
|
+
const availableUtxos = [...utxos].sort((a, b) => {
|
|
29732
|
+
if (a.value > b.value) return -1;
|
|
29733
|
+
if (a.value < b.value) return 1;
|
|
29734
|
+
return 0;
|
|
29735
|
+
});
|
|
29736
|
+
const nodes = [];
|
|
29737
|
+
for (let i = 0; i < nodeHexStrings.length; i++) {
|
|
29738
|
+
const hex = nodeHexStrings[i];
|
|
29739
|
+
try {
|
|
29740
|
+
if (!hex || hex.length === 0) {
|
|
29741
|
+
throw new Error(`Node hex string at index ${i} is empty`);
|
|
29742
|
+
}
|
|
29743
|
+
if (hex.startsWith("03000000") || hex.startsWith("02000000") || hex.startsWith("01000000")) {
|
|
29744
|
+
throw new Error(
|
|
29745
|
+
`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.`
|
|
29746
|
+
);
|
|
29747
|
+
}
|
|
29748
|
+
const nodeBytes = (0, import_utils21.hexToBytes)(hex);
|
|
29749
|
+
const node = TreeNode.decode(nodeBytes);
|
|
29750
|
+
if (!node.id) {
|
|
29751
|
+
throw new Error(
|
|
29752
|
+
`Decoded TreeNode at index ${i} is missing required 'id' field`
|
|
29753
|
+
);
|
|
29754
|
+
}
|
|
29755
|
+
if (!node.nodeTx || node.nodeTx.length === 0) {
|
|
29756
|
+
throw new Error(
|
|
29757
|
+
`Decoded TreeNode at index ${i} is missing required 'nodeTx' field`
|
|
29758
|
+
);
|
|
29759
|
+
}
|
|
29760
|
+
nodes.push(node);
|
|
29761
|
+
} catch (decodeError) {
|
|
29762
|
+
throw new Error(
|
|
29763
|
+
`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.`
|
|
29764
|
+
);
|
|
29765
|
+
}
|
|
29766
|
+
}
|
|
29767
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
29768
|
+
for (const node of nodes) {
|
|
29769
|
+
nodeMap.set(node.id, node);
|
|
29770
|
+
}
|
|
29771
|
+
const broadcastTxs = /* @__PURE__ */ new Map();
|
|
29772
|
+
for (const node of nodes) {
|
|
29773
|
+
const txPackages = [];
|
|
29774
|
+
let previousFeeBumpTx;
|
|
29775
|
+
const chain = [];
|
|
29776
|
+
let currentNode = node;
|
|
29777
|
+
while (currentNode) {
|
|
29778
|
+
chain.unshift(currentNode);
|
|
29779
|
+
if (currentNode.parentNodeId) {
|
|
29780
|
+
let parentNode = nodeMap.get(currentNode.parentNodeId);
|
|
29781
|
+
if (!parentNode && sparkClient) {
|
|
29782
|
+
try {
|
|
29783
|
+
const response = await sparkClient.query_nodes({
|
|
29784
|
+
source: {
|
|
29785
|
+
$case: "nodeIds",
|
|
29786
|
+
nodeIds: {
|
|
29787
|
+
nodeIds: [currentNode.parentNodeId]
|
|
29788
|
+
}
|
|
29789
|
+
},
|
|
29790
|
+
includeParents: true,
|
|
29791
|
+
network: network || 0
|
|
29792
|
+
// Default to mainnet if not provided
|
|
29793
|
+
});
|
|
29794
|
+
parentNode = response.nodes[currentNode.parentNodeId];
|
|
29795
|
+
if (parentNode) {
|
|
29796
|
+
nodeMap.set(currentNode.parentNodeId, parentNode);
|
|
29797
|
+
}
|
|
29798
|
+
} catch (error) {
|
|
29799
|
+
console.warn(
|
|
29800
|
+
`Failed to query parent node ${currentNode.parentNodeId}: ${error}`
|
|
29801
|
+
);
|
|
29802
|
+
break;
|
|
29803
|
+
}
|
|
29804
|
+
}
|
|
29805
|
+
if (parentNode) {
|
|
29806
|
+
currentNode = parentNode;
|
|
29807
|
+
} else {
|
|
29808
|
+
if (!sparkClient) {
|
|
29809
|
+
console.warn(
|
|
29810
|
+
`Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
|
|
29811
|
+
);
|
|
29812
|
+
} else {
|
|
29813
|
+
console.warn(
|
|
29814
|
+
`Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
|
|
29815
|
+
);
|
|
29816
|
+
}
|
|
29817
|
+
break;
|
|
29818
|
+
}
|
|
29819
|
+
} else {
|
|
29820
|
+
break;
|
|
29821
|
+
}
|
|
29822
|
+
}
|
|
29823
|
+
for (const chainNode of chain) {
|
|
29824
|
+
let nodeTxHex = (0, import_utils21.bytesToHex)(chainNode.nodeTx);
|
|
29825
|
+
try {
|
|
29826
|
+
const txObj = getTxFromRawTxHex(nodeTxHex);
|
|
29827
|
+
const txid = getTxId(txObj);
|
|
29828
|
+
if (broadcastTxs.get(txid)) {
|
|
29829
|
+
continue;
|
|
29830
|
+
}
|
|
29831
|
+
broadcastTxs.set(txid, true);
|
|
29832
|
+
const isBroadcast = await isTxBroadcast(txid, electrsUrl, network);
|
|
29833
|
+
if (isBroadcast) {
|
|
29834
|
+
continue;
|
|
29835
|
+
} else {
|
|
29836
|
+
}
|
|
29837
|
+
let anchorOutputScriptHex;
|
|
29838
|
+
for (let i = txObj.outputsLength - 1; i >= 0; i--) {
|
|
29839
|
+
const output = txObj.getOutput(i);
|
|
29840
|
+
if (output?.amount === 0n && output.script) {
|
|
29841
|
+
anchorOutputScriptHex = (0, import_utils21.bytesToHex)(output.script);
|
|
29842
|
+
break;
|
|
29843
|
+
}
|
|
29844
|
+
}
|
|
29845
|
+
} catch (parseError) {
|
|
29846
|
+
console.error(
|
|
29847
|
+
`\u274C Error parsing nodeTx for anchor check (node ${chainNode.id}): ${parseError}`
|
|
29848
|
+
);
|
|
29849
|
+
console.log(
|
|
29850
|
+
` This may indicate a corrupted transaction in the TreeNode.`
|
|
29851
|
+
);
|
|
29852
|
+
console.log(` Transaction hex: ${nodeTxHex}`);
|
|
29853
|
+
console.log(
|
|
29854
|
+
` Attempting to continue with original hex, but fee bump may fail.`
|
|
29855
|
+
);
|
|
29856
|
+
}
|
|
29857
|
+
const {
|
|
29858
|
+
feeBumpPsbt: nodeFeeBumpPsbt,
|
|
29859
|
+
usedUtxos,
|
|
29860
|
+
correctedParentTx
|
|
29861
|
+
} = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
|
|
29862
|
+
const feeBumpTx = btc5.Transaction.fromPSBT((0, import_utils21.hexToBytes)(nodeFeeBumpPsbt));
|
|
29863
|
+
var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
|
|
29864
|
+
var feeBumpOutPubKey = null;
|
|
29865
|
+
for (const usedUtxo of usedUtxos) {
|
|
29866
|
+
if (feeBumpOut && (0, import_utils21.bytesToHex)(feeBumpOut.script) == usedUtxo.script) {
|
|
29867
|
+
feeBumpOutPubKey = usedUtxo.publicKey;
|
|
29868
|
+
}
|
|
29869
|
+
const index = availableUtxos.findIndex(
|
|
29870
|
+
(u) => u.txid === usedUtxo.txid && u.vout === usedUtxo.vout
|
|
29871
|
+
);
|
|
29872
|
+
if (index !== -1) {
|
|
29873
|
+
availableUtxos.splice(index, 1);
|
|
29874
|
+
}
|
|
29875
|
+
}
|
|
29876
|
+
if (feeBumpOut)
|
|
29877
|
+
availableUtxos.unshift({
|
|
29878
|
+
txid: getTxId(feeBumpTx),
|
|
29879
|
+
vout: 0,
|
|
29880
|
+
value: feeBumpOut.amount,
|
|
29881
|
+
script: (0, import_utils21.bytesToHex)(feeBumpOut.script),
|
|
29882
|
+
publicKey: feeBumpOutPubKey
|
|
29883
|
+
});
|
|
29884
|
+
const finalNodeTx = correctedParentTx || nodeTxHex;
|
|
29885
|
+
txPackages.push({ tx: finalNodeTx, feeBumpPsbt: nodeFeeBumpPsbt });
|
|
29886
|
+
if (chainNode.id === node.id) {
|
|
29887
|
+
let refundTxHex = (0, import_utils21.bytesToHex)(chainNode.refundTx);
|
|
29888
|
+
try {
|
|
29889
|
+
const txObj = getTxFromRawTxHex(refundTxHex);
|
|
29890
|
+
let anchorOutputScriptHex;
|
|
29891
|
+
for (let i = txObj.outputsLength - 1; i >= 0; i--) {
|
|
29892
|
+
const output = txObj.getOutput(i);
|
|
29893
|
+
if (output?.amount === 0n && output.script) {
|
|
29894
|
+
anchorOutputScriptHex = (0, import_utils21.bytesToHex)(output.script);
|
|
29895
|
+
break;
|
|
29896
|
+
}
|
|
29897
|
+
}
|
|
29898
|
+
} catch (parseError) {
|
|
29899
|
+
console.error(
|
|
29900
|
+
`\u274C Error parsing refundTx for anchor check (node ${chainNode.id}): ${parseError}`
|
|
29901
|
+
);
|
|
29902
|
+
console.log(
|
|
29903
|
+
` This may indicate a corrupted refund transaction in the TreeNode.`
|
|
29904
|
+
);
|
|
29905
|
+
console.log(` Refund transaction hex: ${refundTxHex}`);
|
|
29906
|
+
console.log(
|
|
29907
|
+
` Attempting to continue with original refund hex, but this transaction may be invalid.`
|
|
29908
|
+
);
|
|
29909
|
+
}
|
|
29910
|
+
const refundFeeBump = constructFeeBumpTx(
|
|
29911
|
+
refundTxHex,
|
|
29912
|
+
availableUtxos,
|
|
29913
|
+
feeRate,
|
|
29914
|
+
void 0
|
|
29915
|
+
);
|
|
29916
|
+
txPackages.push({
|
|
29917
|
+
tx: refundTxHex,
|
|
29918
|
+
feeBumpPsbt: refundFeeBump.feeBumpPsbt
|
|
29919
|
+
});
|
|
29920
|
+
}
|
|
29921
|
+
}
|
|
29922
|
+
result.push({
|
|
29923
|
+
leafId: node.id,
|
|
29924
|
+
txPackages
|
|
29925
|
+
});
|
|
29926
|
+
}
|
|
29927
|
+
return result;
|
|
29928
|
+
}
|
|
29929
|
+
function hash160(data) {
|
|
29930
|
+
const sha256Hash = (0, import_sha214.sha256)(data);
|
|
29931
|
+
return (0, import_legacy.ripemd160)(sha256Hash);
|
|
29932
|
+
}
|
|
29933
|
+
function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
|
|
29934
|
+
if (!txHex || txHex.length === 0) {
|
|
29935
|
+
throw new Error("Transaction hex string is empty or undefined");
|
|
29936
|
+
}
|
|
29937
|
+
if (utxos.length === 0) {
|
|
29938
|
+
throw new Error("No UTXOs available for fee bump");
|
|
29939
|
+
}
|
|
29940
|
+
let correctedTxHex = txHex;
|
|
29941
|
+
let parentTx;
|
|
29942
|
+
try {
|
|
29943
|
+
parentTx = getTxFromRawTxHex(correctedTxHex);
|
|
29944
|
+
if (!parentTx) {
|
|
29945
|
+
throw new Error("getTxFromRawTxHex returned null/undefined");
|
|
29946
|
+
}
|
|
29947
|
+
} catch (parseError) {
|
|
29948
|
+
throw new Error(
|
|
29949
|
+
`Failed to parse parent transaction hex: ${parseError}. Transaction hex: ${correctedTxHex}`
|
|
29950
|
+
);
|
|
29951
|
+
}
|
|
29952
|
+
try {
|
|
29953
|
+
const outputsLength = parentTx.outputsLength;
|
|
29954
|
+
const inputsLength = parentTx.inputsLength;
|
|
29955
|
+
if (typeof outputsLength !== "number" || outputsLength < 0) {
|
|
29956
|
+
throw new Error(
|
|
29957
|
+
"Invalid transaction: outputsLength is not a valid number"
|
|
29958
|
+
);
|
|
29959
|
+
}
|
|
29960
|
+
if (typeof inputsLength !== "number" || inputsLength < 0) {
|
|
29961
|
+
throw new Error(
|
|
29962
|
+
"Invalid transaction: inputsLength is not a valid number"
|
|
29963
|
+
);
|
|
29964
|
+
}
|
|
29965
|
+
} catch (validationError) {
|
|
29966
|
+
throw new Error(
|
|
29967
|
+
`Transaction validation failed: ${validationError}. This may indicate a corrupted or malformed transaction.`
|
|
29968
|
+
);
|
|
29969
|
+
}
|
|
29970
|
+
const parentTxIdFromLib = parentTx.id;
|
|
29971
|
+
let ephemeralAnchorIndex = -1;
|
|
29972
|
+
for (let i = 0; i < parentTx.outputsLength; i++) {
|
|
29973
|
+
const output = parentTx.getOutput(i);
|
|
29974
|
+
const isEphemeralAnchor = isEphemeralAnchorOutput(
|
|
29975
|
+
output?.script,
|
|
29976
|
+
output?.amount
|
|
29977
|
+
);
|
|
29978
|
+
if (isEphemeralAnchor) {
|
|
29979
|
+
ephemeralAnchorIndex = i;
|
|
29980
|
+
break;
|
|
29981
|
+
}
|
|
29982
|
+
}
|
|
29983
|
+
if (ephemeralAnchorIndex === -1) {
|
|
29984
|
+
throw new Error(
|
|
29985
|
+
"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)."
|
|
29986
|
+
);
|
|
29987
|
+
}
|
|
29988
|
+
const ephemeralAnchorOutput = parentTx.getOutput(ephemeralAnchorIndex);
|
|
29989
|
+
if (!ephemeralAnchorOutput)
|
|
29990
|
+
throw new Error("No ephemeral anchor output found");
|
|
29991
|
+
if (!ephemeralAnchorOutput.script)
|
|
29992
|
+
throw new Error("No script found in ephemeral anchor output");
|
|
29993
|
+
if (utxos.length === 0) {
|
|
29994
|
+
throw new Error("No UTXOs available for fee bump");
|
|
29995
|
+
}
|
|
29996
|
+
const builder = new btc5.Transaction({
|
|
29997
|
+
version: 3,
|
|
29998
|
+
allowUnknown: true,
|
|
29999
|
+
allowLegacyWitnessUtxo: true
|
|
30000
|
+
});
|
|
30001
|
+
let totalValue = 0n;
|
|
30002
|
+
const processedUtxos = [];
|
|
30003
|
+
for (let i = 0; i < utxos.length; i++) {
|
|
30004
|
+
const fundingUtxo = utxos[i];
|
|
30005
|
+
if (!fundingUtxo) {
|
|
30006
|
+
throw new Error(`UTXO at index ${i} is undefined`);
|
|
30007
|
+
}
|
|
30008
|
+
const pubKeyHash = hash160((0, import_utils21.hexToBytes)(fundingUtxo.publicKey));
|
|
30009
|
+
const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
|
|
30010
|
+
const providedScript = (0, import_utils21.hexToBytes)(fundingUtxo.script);
|
|
30011
|
+
if ((0, import_utils21.bytesToHex)(scriptToUse) !== (0, import_utils21.bytesToHex)(providedScript)) {
|
|
30012
|
+
throw new Error(
|
|
30013
|
+
`\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
|
|
30014
|
+
);
|
|
30015
|
+
}
|
|
30016
|
+
builder.addInput({
|
|
30017
|
+
txid: fundingUtxo.txid,
|
|
30018
|
+
index: fundingUtxo.vout,
|
|
30019
|
+
sequence: 4294967295,
|
|
30020
|
+
witnessUtxo: {
|
|
30021
|
+
script: scriptToUse,
|
|
30022
|
+
// Always P2WPKH
|
|
30023
|
+
amount: fundingUtxo.value
|
|
30024
|
+
}
|
|
30025
|
+
});
|
|
30026
|
+
totalValue += fundingUtxo.value;
|
|
30027
|
+
processedUtxos.push({
|
|
30028
|
+
utxo: fundingUtxo,
|
|
30029
|
+
p2wpkhScript: scriptToUse
|
|
30030
|
+
});
|
|
30031
|
+
}
|
|
30032
|
+
builder.addInput({
|
|
30033
|
+
txid: parentTxIdFromLib,
|
|
30034
|
+
index: ephemeralAnchorIndex,
|
|
30035
|
+
sequence: 4294967295,
|
|
30036
|
+
witnessUtxo: {
|
|
30037
|
+
script: ephemeralAnchorOutput.script,
|
|
30038
|
+
// Use the original script directly (not P2WSH wrapped)
|
|
30039
|
+
amount: 0n
|
|
30040
|
+
}
|
|
30041
|
+
});
|
|
30042
|
+
const fee = 1500n;
|
|
30043
|
+
const remainingValue = totalValue - fee;
|
|
30044
|
+
if (remainingValue <= 0n) {
|
|
30045
|
+
throw new Error(
|
|
30046
|
+
`Insufficient funds for fee bump. Required fee: ${fee} sats, Available: ${totalValue} sats`
|
|
30047
|
+
);
|
|
30048
|
+
}
|
|
30049
|
+
if (processedUtxos.length === 0) {
|
|
30050
|
+
throw new Error("No processed UTXOs available for change output");
|
|
30051
|
+
}
|
|
30052
|
+
const firstProcessedUtxo = processedUtxos[0];
|
|
30053
|
+
if (!firstProcessedUtxo) {
|
|
30054
|
+
throw new Error("First processed UTXO is undefined");
|
|
30055
|
+
}
|
|
30056
|
+
builder.addOutput({
|
|
30057
|
+
script: firstProcessedUtxo.p2wpkhScript,
|
|
30058
|
+
amount: remainingValue
|
|
30059
|
+
});
|
|
30060
|
+
for (let i = 0; i < processedUtxos.length; i++) {
|
|
30061
|
+
const processed = processedUtxos[i];
|
|
30062
|
+
if (!processed) {
|
|
30063
|
+
throw new Error(`Processed UTXO at index ${i} is undefined`);
|
|
30064
|
+
}
|
|
30065
|
+
try {
|
|
30066
|
+
builder.updateInput(i, {
|
|
30067
|
+
witnessScript: processed.p2wpkhScript
|
|
30068
|
+
});
|
|
30069
|
+
builder.signIdx;
|
|
30070
|
+
} catch (error) {
|
|
30071
|
+
throw new Error(`Failed to handle funding UTXO input ${i + 1}: ${error}`);
|
|
30072
|
+
}
|
|
30073
|
+
}
|
|
30074
|
+
let psbtHex;
|
|
30075
|
+
try {
|
|
30076
|
+
psbtHex = (0, import_utils21.bytesToHex)(builder.toPSBT());
|
|
30077
|
+
} catch (error) {
|
|
30078
|
+
throw new Error(`Failed to extract transaction: ${error}`);
|
|
30079
|
+
}
|
|
30080
|
+
return {
|
|
30081
|
+
feeBumpPsbt: psbtHex,
|
|
30082
|
+
usedUtxos: utxos,
|
|
30083
|
+
correctedParentTx: correctedTxHex !== txHex ? correctedTxHex : void 0
|
|
30084
|
+
};
|
|
30085
|
+
}
|
|
29065
30086
|
// Annotate the CommonJS export names for ESM import in node:
|
|
29066
30087
|
0 && (module.exports = {
|
|
29067
30088
|
AuthenticationError,
|
|
29068
30089
|
ConfigurationError,
|
|
30090
|
+
DEFAULT_FEE_SATS,
|
|
29069
30091
|
InternalValidationError,
|
|
29070
30092
|
LRC_WALLET_NETWORK,
|
|
29071
30093
|
LRC_WALLET_NETWORK_TYPE,
|
|
@@ -29087,6 +30109,9 @@ async function getLatestDepositTxId(address2) {
|
|
|
29087
30109
|
collectResponses,
|
|
29088
30110
|
computeTaprootKeyNoScript,
|
|
29089
30111
|
computerLagrangeCoefficients,
|
|
30112
|
+
constructFeeBumpTx,
|
|
30113
|
+
constructUnilateralExitFeeBumpPackages,
|
|
30114
|
+
constructUnilateralExitTxs,
|
|
29090
30115
|
createDummyTx,
|
|
29091
30116
|
createRefundTx,
|
|
29092
30117
|
createSigningCommitment,
|
|
@@ -29106,6 +30131,7 @@ async function getLatestDepositTxId(address2) {
|
|
|
29106
30131
|
getLatestDepositTxId,
|
|
29107
30132
|
getNetwork,
|
|
29108
30133
|
getNetworkFromAddress,
|
|
30134
|
+
getNetworkFromString,
|
|
29109
30135
|
getNextTransactionSequence,
|
|
29110
30136
|
getP2TRAddressFromPkScript,
|
|
29111
30137
|
getP2TRAddressFromPublicKey,
|
|
@@ -29121,7 +30147,10 @@ async function getLatestDepositTxId(address2) {
|
|
|
29121
30147
|
getTxFromRawTxHex,
|
|
29122
30148
|
getTxId,
|
|
29123
30149
|
getTxIdNoReverse,
|
|
30150
|
+
isEphemeralAnchorOutput,
|
|
30151
|
+
isTxBroadcast,
|
|
29124
30152
|
lastKeyWithTarget,
|
|
30153
|
+
maybeApplyFee,
|
|
29125
30154
|
modInverse,
|
|
29126
30155
|
proofOfPossessionMessageHashForDepositAddress,
|
|
29127
30156
|
recoverSecret,
|