@arkade-os/boltz-swap 0.3.45 → 0.3.47

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.
@@ -1,5 +1,5 @@
1
1
  import { IWallet, ArkProvider, IndexerProvider, ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
2
- import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-CS62-oEy.cjs';
2
+ import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-BKEkNZxK.js';
3
3
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
4
4
 
5
5
  /**
@@ -313,6 +313,9 @@ declare class ArkadeSwaps {
313
313
  }>;
314
314
  /**
315
315
  * Claim sats on BTC chain by claiming the HTLC.
316
+ *
317
+ * The claim output is `swapOutput.amount − max(feeToDeliverExactAmount, targetFee)`.
318
+ *
316
319
  * @param pendingSwap - The pending chain swap with BTC transaction hex.
317
320
  * @returns The BTC transaction ID of the claim.
318
321
  */
@@ -415,6 +418,13 @@ declare class ArkadeSwaps {
415
418
  swap: BoltzChainSwap;
416
419
  arkInfo: ArkInfo;
417
420
  }): Promise<boolean>;
421
+ /**
422
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
423
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
424
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
425
+ * direction-specific keys, leaf version, and absolute refund CLTV.
426
+ */
427
+ private verifyBtcChainHtlc;
418
428
  /**
419
429
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
420
430
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -1,5 +1,5 @@
1
1
  import { IWallet, ArkProvider, IndexerProvider, ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
2
- import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-CS62-oEy.js';
2
+ import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-BKEkNZxK.cjs';
3
3
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
4
4
 
5
5
  /**
@@ -313,6 +313,9 @@ declare class ArkadeSwaps {
313
313
  }>;
314
314
  /**
315
315
  * Claim sats on BTC chain by claiming the HTLC.
316
+ *
317
+ * The claim output is `swapOutput.amount − max(feeToDeliverExactAmount, targetFee)`.
318
+ *
316
319
  * @param pendingSwap - The pending chain swap with BTC transaction hex.
317
320
  * @returns The BTC transaction ID of the claim.
318
321
  */
@@ -415,6 +418,13 @@ declare class ArkadeSwaps {
415
418
  swap: BoltzChainSwap;
416
419
  arkInfo: ArkInfo;
417
420
  }): Promise<boolean>;
421
+ /**
422
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
423
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
424
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
425
+ * direction-specific keys, leaf version, and absolute refund CLTV.
426
+ */
427
+ private verifyBtcChainHtlc;
418
428
  /**
419
429
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
420
430
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -7,7 +7,7 @@ import {
7
7
  isSubmarineFinalStatus,
8
8
  isSubmarineSwapRefundable,
9
9
  logger
10
- } from "./chunk-5GBBRBFS.js";
10
+ } from "./chunk-VKHFXGKL.js";
11
11
 
12
12
  // src/expo/swapsPollProcessor.ts
13
13
  var SWAP_POLL_TASK_TYPE = "swap-poll";
@@ -1001,6 +1001,9 @@ var decodeInvoice = (invoice) => {
1001
1001
  const millisats = BigInt(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1002
1002
  return {
1003
1003
  expiry: decoded.expiry ?? 3600,
1004
+ // The timestamp fallback to 0 is just to satisfy the type checker.
1005
+ // In practice, the timestamp is always present in well-formatted BOLT11 invoices.
1006
+ timestamp: decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0,
1004
1007
  amountSats: Number(millisats / 1000n),
1005
1008
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1006
1009
  // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
@@ -2376,14 +2379,16 @@ var IndexedDbSwapRepository = class {
2376
2379
  // src/arkade-swaps.ts
2377
2380
  import {
2378
2381
  ArkAddress as ArkAddress2,
2379
- isRecoverable
2382
+ isRecoverable,
2383
+ getNetwork as getNetwork2
2380
2384
  } from "@arkade-os/sdk";
2381
2385
  import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
2386
+ import { ripemd160 as ripemd1602 } from "@noble/hashes/legacy.js";
2382
2387
  import { hex as hex8 } from "@scure/base";
2383
2388
  import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
2384
2389
  import { randomBytes } from "@noble/hashes/utils.js";
2385
2390
  import { Address as Address2, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction6 } from "@scure/btc-signer";
2386
- import { NETWORK } from "@scure/btc-signer/utils.js";
2391
+ import { equalBytes as equalBytes3 } from "@scure/btc-signer/utils.js";
2387
2392
 
2388
2393
  // src/utils/musig.ts
2389
2394
  import { secp256k1 } from "@noble/curves/secp256k1.js";
@@ -2605,21 +2610,9 @@ var create = (privateKey, publicKeys) => {
2605
2610
  // src/utils/boltz-swap-tx.ts
2606
2611
  import { schnorr } from "@noble/curves/secp256k1.js";
2607
2612
  import { hex as hex4 } from "@scure/base";
2608
- import { Script, Transaction as Transaction2 } from "@scure/btc-signer";
2613
+ import { Script, ScriptNum, Transaction as Transaction2 } from "@scure/btc-signer";
2609
2614
  import { tapLeafHash } from "@scure/btc-signer/payment.js";
2610
2615
  import { compareBytes, equalBytes as equalBytes2 } from "@scure/btc-signer/utils.js";
2611
- var REGTEST_NETWORK = {
2612
- bech32: "bcrt",
2613
- pubKeyHash: 111,
2614
- scriptHash: 196,
2615
- wif: 239
2616
- };
2617
- var MUTINYNET_NETWORK = {
2618
- bech32: "tb",
2619
- pubKeyHash: 111,
2620
- scriptHash: 196,
2621
- wif: 239
2622
- };
2623
2616
  var deserializeLeaf = (leaf) => ({
2624
2617
  version: leaf.version,
2625
2618
  output: hex4.decode(leaf.output)
@@ -2653,6 +2646,19 @@ var deserializeSwapTree = (tree) => {
2653
2646
  ])
2654
2647
  };
2655
2648
  };
2649
+ var TAPLEAF_V1 = 192;
2650
+ var PUSH_32 = Uint8Array.of(32);
2651
+ var decodeScriptNum = (data) => data instanceof Uint8Array && data.length > 0 ? Number(ScriptNum(5, true).decode(data)) : void 0;
2652
+ var assertChainHtlcLeaves = (tree, expected) => {
2653
+ if (tree.claimLeaf.version !== TAPLEAF_V1 || tree.refundLeaf.version !== TAPLEAF_V1)
2654
+ throw new Error("unexpected leaf version");
2655
+ const claim = Script.decode(tree.claimLeaf.output);
2656
+ if (claim.length !== 8 || claim[0] !== "SIZE" || !(claim[1] instanceof Uint8Array) || !equalBytes2(claim[1], PUSH_32) || claim[2] !== "EQUALVERIFY" || claim[3] !== "HASH160" || !(claim[4] instanceof Uint8Array) || !equalBytes2(claim[4], expected.preimageHash160) || claim[5] !== "EQUALVERIFY" || !(claim[6] instanceof Uint8Array) || !equalBytes2(claim[6], expected.claimXOnly) || claim[7] !== "CHECKSIG")
2657
+ throw new Error("unexpected claim leaf");
2658
+ const refund = Script.decode(tree.refundLeaf.output);
2659
+ if (refund.length !== 4 || !(refund[0] instanceof Uint8Array) || !equalBytes2(refund[0], expected.refundXOnly) || refund[1] !== "CHECKSIGVERIFY" || decodeScriptNum(refund[2]) !== expected.timeoutBlockHeight || refund[3] !== "CHECKLOCKTIMEVERIFY")
2660
+ throw new Error("unexpected refund leaf");
2661
+ };
2656
2662
  var taprootHashTree = (tree) => {
2657
2663
  if (!Array.isArray(tree)) {
2658
2664
  return {
@@ -2720,8 +2726,8 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
2720
2726
  return tx;
2721
2727
  };
2722
2728
  var targetFee = (satPerVbyte, constructTx) => {
2723
- const tx = constructTx(BigInt(1));
2724
- return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
2729
+ const probe = constructTx(BigInt(1));
2730
+ return constructTx(BigInt(Math.ceil(probe.vsize * satPerVbyte)));
2725
2731
  };
2726
2732
 
2727
2733
  // src/utils/restoration.ts
@@ -2804,6 +2810,7 @@ import {
2804
2810
  buildOffchainTx,
2805
2811
  getSequence as getSequence2,
2806
2812
  Intent,
2813
+ getNetwork,
2807
2814
  networks,
2808
2815
  VHTLC,
2809
2816
  VtxoScript as VtxoScript2,
@@ -2990,7 +2997,7 @@ var createVHTLCScript = (args) => {
2990
2997
  )
2991
2998
  });
2992
2999
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2993
- const hrp = network === "bitcoin" ? "ark" : "tark";
3000
+ const hrp = getNetwork(network).hrp;
2994
3001
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2995
3002
  return { vhtlcScript, vhtlcAddress };
2996
3003
  };
@@ -3425,6 +3432,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3425
3432
  return {
3426
3433
  amount: pendingSwap.response.onchainAmount,
3427
3434
  expiry: decodedInvoice.expiry,
3435
+ timestamp: decodedInvoice.timestamp,
3428
3436
  invoice: pendingSwap.response.invoice,
3429
3437
  paymentHash: decodedInvoice.paymentHash,
3430
3438
  pendingSwap,
@@ -4392,6 +4400,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4392
4400
  }
4393
4401
  /**
4394
4402
  * Claim sats on BTC chain by claiming the HTLC.
4403
+ *
4404
+ * The claim output is `swapOutput.amount − max(feeToDeliverExactAmount, targetFee)`.
4405
+ *
4395
4406
  * @param pendingSwap - The pending chain swap with BTC transaction hex.
4396
4407
  * @returns The BTC transaction ID of the claim.
4397
4408
  */
@@ -4407,7 +4418,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4407
4418
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4408
4419
  const lockupTx = Transaction6.fromRaw(hex8.decode(swapStatus.transaction.hex));
4409
4420
  const arkInfo = await this.arkProvider.getInfo();
4410
- const network = arkInfo.network === "bitcoin" ? NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4421
+ const network = getNetwork2(arkInfo.network);
4411
4422
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4412
4423
  const musig = tweakMusig(
4413
4424
  create(hex8.decode(pendingSwap.ephemeralKey), [
@@ -4947,8 +4958,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
4947
4958
  message: "Boltz is trying to scam us (invalid address)"
4948
4959
  });
4949
4960
  }
4961
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
4950
4962
  return true;
4951
4963
  }
4964
+ /**
4965
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
4966
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
4967
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
4968
+ * direction-specific keys, leaf version, and absolute refund CLTV.
4969
+ */
4970
+ verifyBtcChainHtlc(args) {
4971
+ const { to, swap, arkNetwork } = args;
4972
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
4973
+ if (!btcDetails.swapTree)
4974
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
4975
+ if (!btcDetails.serverPublicKey)
4976
+ throw new SwapError({
4977
+ message: `Swap ${swap.id}: missing server public key in BTC details`
4978
+ });
4979
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
4980
+ throw new SwapError({
4981
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
4982
+ });
4983
+ const network = getNetwork2(arkNetwork);
4984
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
4985
+ const ephemeralPub = secp256k12.getPublicKey(hex8.decode(swap.ephemeralKey));
4986
+ const musig = tweakMusig(
4987
+ create(hex8.decode(swap.ephemeralKey), [
4988
+ hex8.decode(btcDetails.serverPublicKey),
4989
+ ephemeralPub
4990
+ ]),
4991
+ swapTree.tree
4992
+ );
4993
+ const expectedScript = OutScript2.encode(Address2(network).decode(btcDetails.lockupAddress));
4994
+ if (!equalBytes3(p2trScript(musig.aggPubkey), expectedScript))
4995
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
4996
+ const boltzXOnly = toXOnly(hex8.decode(btcDetails.serverPublicKey));
4997
+ const userXOnly = toXOnly(ephemeralPub);
4998
+ try {
4999
+ assertChainHtlcLeaves(swapTree, {
5000
+ preimageHash160: ripemd1602(hex8.decode(swap.request.preimageHash)),
5001
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
5002
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
5003
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
5004
+ });
5005
+ } catch (err) {
5006
+ throw new SwapError({
5007
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
5008
+ });
5009
+ }
5010
+ }
4952
5011
  /**
4953
5012
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
4954
5013
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -969,6 +969,7 @@ var BoltzSwapProvider = class {
969
969
  // src/arkade-swaps.ts
970
970
  var import_sdk8 = require("@arkade-os/sdk");
971
971
  var import_sha23 = require("@noble/hashes/sha2.js");
972
+ var import_legacy2 = require("@noble/hashes/legacy.js");
972
973
  var import_base9 = require("@scure/base");
973
974
  var import_secp256k13 = require("@noble/curves/secp256k1.js");
974
975
  var import_utils3 = require("@noble/hashes/utils.js");
@@ -1192,18 +1193,6 @@ var import_base3 = require("@scure/base");
1192
1193
  var import_btc_signer = require("@scure/btc-signer");
1193
1194
  var import_payment = require("@scure/btc-signer/payment.js");
1194
1195
  var import_utils2 = require("@scure/btc-signer/utils.js");
1195
- var REGTEST_NETWORK = {
1196
- bech32: "bcrt",
1197
- pubKeyHash: 111,
1198
- scriptHash: 196,
1199
- wif: 239
1200
- };
1201
- var MUTINYNET_NETWORK = {
1202
- bech32: "tb",
1203
- pubKeyHash: 111,
1204
- scriptHash: 196,
1205
- wif: 239
1206
- };
1207
1196
  var deserializeLeaf = (leaf) => ({
1208
1197
  version: leaf.version,
1209
1198
  output: import_base3.hex.decode(leaf.output)
@@ -1237,6 +1226,19 @@ var deserializeSwapTree = (tree) => {
1237
1226
  ])
1238
1227
  };
1239
1228
  };
1229
+ var TAPLEAF_V1 = 192;
1230
+ var PUSH_32 = Uint8Array.of(32);
1231
+ var decodeScriptNum = (data) => data instanceof Uint8Array && data.length > 0 ? Number((0, import_btc_signer.ScriptNum)(5, true).decode(data)) : void 0;
1232
+ var assertChainHtlcLeaves = (tree, expected) => {
1233
+ if (tree.claimLeaf.version !== TAPLEAF_V1 || tree.refundLeaf.version !== TAPLEAF_V1)
1234
+ throw new Error("unexpected leaf version");
1235
+ const claim = import_btc_signer.Script.decode(tree.claimLeaf.output);
1236
+ if (claim.length !== 8 || claim[0] !== "SIZE" || !(claim[1] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[1], PUSH_32) || claim[2] !== "EQUALVERIFY" || claim[3] !== "HASH160" || !(claim[4] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[4], expected.preimageHash160) || claim[5] !== "EQUALVERIFY" || !(claim[6] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[6], expected.claimXOnly) || claim[7] !== "CHECKSIG")
1237
+ throw new Error("unexpected claim leaf");
1238
+ const refund = import_btc_signer.Script.decode(tree.refundLeaf.output);
1239
+ if (refund.length !== 4 || !(refund[0] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(refund[0], expected.refundXOnly) || refund[1] !== "CHECKSIGVERIFY" || decodeScriptNum(refund[2]) !== expected.timeoutBlockHeight || refund[3] !== "CHECKLOCKTIMEVERIFY")
1240
+ throw new Error("unexpected refund leaf");
1241
+ };
1240
1242
  var taprootHashTree = (tree) => {
1241
1243
  if (!Array.isArray(tree)) {
1242
1244
  return {
@@ -1304,8 +1306,8 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
1304
1306
  return tx;
1305
1307
  };
1306
1308
  var targetFee = (satPerVbyte, constructTx) => {
1307
- const tx = constructTx(BigInt(1));
1308
- return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
1309
+ const probe = constructTx(BigInt(1));
1310
+ return constructTx(BigInt(Math.ceil(probe.vsize * satPerVbyte)));
1309
1311
  };
1310
1312
 
1311
1313
  // src/utils/decoding.ts
@@ -1316,6 +1318,9 @@ var decodeInvoice = (invoice) => {
1316
1318
  const millisats = BigInt(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1317
1319
  return {
1318
1320
  expiry: decoded.expiry ?? 3600,
1321
+ // The timestamp fallback to 0 is just to satisfy the type checker.
1322
+ // In practice, the timestamp is always present in well-formatted BOLT11 invoices.
1323
+ timestamp: decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0,
1319
1324
  amountSats: Number(millisats / 1000n),
1320
1325
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1321
1326
  // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
@@ -2933,7 +2938,7 @@ var createVHTLCScript = (args) => {
2933
2938
  )
2934
2939
  });
2935
2940
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2936
- const hrp = network === "bitcoin" ? "ark" : "tark";
2941
+ const hrp = (0, import_sdk7.getNetwork)(network).hrp;
2937
2942
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2938
2943
  return { vhtlcScript, vhtlcAddress };
2939
2944
  };
@@ -3368,6 +3373,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3368
3373
  return {
3369
3374
  amount: pendingSwap.response.onchainAmount,
3370
3375
  expiry: decodedInvoice.expiry,
3376
+ timestamp: decodedInvoice.timestamp,
3371
3377
  invoice: pendingSwap.response.invoice,
3372
3378
  paymentHash: decodedInvoice.paymentHash,
3373
3379
  pendingSwap,
@@ -4335,6 +4341,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4335
4341
  }
4336
4342
  /**
4337
4343
  * Claim sats on BTC chain by claiming the HTLC.
4344
+ *
4345
+ * The claim output is `swapOutput.amount − max(feeToDeliverExactAmount, targetFee)`.
4346
+ *
4338
4347
  * @param pendingSwap - The pending chain swap with BTC transaction hex.
4339
4348
  * @returns The BTC transaction ID of the claim.
4340
4349
  */
@@ -4350,7 +4359,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4350
4359
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4351
4360
  const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4352
4361
  const arkInfo = await this.arkProvider.getInfo();
4353
- const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4362
+ const network = (0, import_sdk8.getNetwork)(arkInfo.network);
4354
4363
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4355
4364
  const musig = tweakMusig(
4356
4365
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
@@ -4890,8 +4899,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
4890
4899
  message: "Boltz is trying to scam us (invalid address)"
4891
4900
  });
4892
4901
  }
4902
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
4893
4903
  return true;
4894
4904
  }
4905
+ /**
4906
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
4907
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
4908
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
4909
+ * direction-specific keys, leaf version, and absolute refund CLTV.
4910
+ */
4911
+ verifyBtcChainHtlc(args) {
4912
+ const { to, swap, arkNetwork } = args;
4913
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
4914
+ if (!btcDetails.swapTree)
4915
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
4916
+ if (!btcDetails.serverPublicKey)
4917
+ throw new SwapError({
4918
+ message: `Swap ${swap.id}: missing server public key in BTC details`
4919
+ });
4920
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
4921
+ throw new SwapError({
4922
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
4923
+ });
4924
+ const network = (0, import_sdk8.getNetwork)(arkNetwork);
4925
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
4926
+ const ephemeralPub = import_secp256k13.secp256k1.getPublicKey(import_base9.hex.decode(swap.ephemeralKey));
4927
+ const musig = tweakMusig(
4928
+ create(import_base9.hex.decode(swap.ephemeralKey), [
4929
+ import_base9.hex.decode(btcDetails.serverPublicKey),
4930
+ ephemeralPub
4931
+ ]),
4932
+ swapTree.tree
4933
+ );
4934
+ const expectedScript = import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(btcDetails.lockupAddress));
4935
+ if (!(0, import_utils4.equalBytes)(p2trScript(musig.aggPubkey), expectedScript))
4936
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
4937
+ const boltzXOnly = toXOnly(import_base9.hex.decode(btcDetails.serverPublicKey));
4938
+ const userXOnly = toXOnly(ephemeralPub);
4939
+ try {
4940
+ assertChainHtlcLeaves(swapTree, {
4941
+ preimageHash160: (0, import_legacy2.ripemd160)(import_base9.hex.decode(swap.request.preimageHash)),
4942
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
4943
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
4944
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
4945
+ });
4946
+ } catch (err) {
4947
+ throw new SwapError({
4948
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
4949
+ });
4950
+ }
4951
+ }
4895
4952
  /**
4896
4953
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
4897
4954
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -5549,14 +5606,19 @@ function createBackgroundWalletShim(args) {
5549
5606
  getVtxos: async () => notImplemented("getVtxos"),
5550
5607
  getBoardingUtxos: async () => notImplemented("getBoardingUtxos"),
5551
5608
  getTransactionHistory: async () => notImplemented("getTransactionHistory"),
5609
+ getActivityHistory: async () => notImplemented("getActivityHistory"),
5552
5610
  getContractManager: async () => notImplemented("getContractManager"),
5553
5611
  getDelegateManager: async () => notImplemented("getDelegateManager"),
5554
5612
  getDelegatorManager: async () => notImplemented("getDelegatorManager"),
5555
5613
  sendBitcoin: async () => notImplemented("sendBitcoin"),
5556
5614
  send: async () => notImplemented("send"),
5557
5615
  settle: async () => notImplemented("settle"),
5616
+ clear: async () => notImplemented("clear"),
5558
5617
  assetManager: new Proxy({}, {
5559
5618
  get: () => notImplemented("assetManager")
5619
+ }),
5620
+ activity: new Proxy({}, {
5621
+ get: () => notImplemented("activity")
5560
5622
  })
5561
5623
  };
5562
5624
  }
@@ -5578,13 +5640,13 @@ function defineExpoSwapBackgroundTask(taskName, options) {
5578
5640
  const wallet = createBackgroundWalletShim({
5579
5641
  identity,
5580
5642
  getAddress: async () => {
5581
- const { ArkAddress: ArkAddress3 } = await import("@arkade-os/sdk");
5643
+ const { ArkAddress: ArkAddress3, getNetwork: getNetwork3 } = await import("@arkade-os/sdk");
5582
5644
  const { hex: hex9 } = await import("@scure/base");
5583
5645
  const info = await arkProvider.getInfo();
5584
5646
  const pubkey = await identity.xOnlyPublicKey();
5585
5647
  const serverPubKey = hex9.decode(info.signerPubkey);
5586
5648
  const xOnlyServerPubKey = serverPubKey.length === 33 ? serverPubKey.slice(1) : serverPubKey;
5587
- const hrp = info.network === "bitcoin" ? "ark" : "tark";
5649
+ const hrp = getNetwork3(info.network).hrp;
5588
5650
  return new ArkAddress3(xOnlyServerPubKey, pubkey, hrp).encode();
5589
5651
  }
5590
5652
  });
@@ -1,8 +1,8 @@
1
- import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-B98F6MQF.cjs';
2
- export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-B98F6MQF.cjs';
1
+ import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-CIqTFkV1.cjs';
2
+ export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-CIqTFkV1.cjs';
3
3
  import '@arkade-os/sdk/worker/expo';
4
4
  import '@arkade-os/sdk';
5
- import '../types-CS62-oEy.cjs';
5
+ import '../types-BKEkNZxK.cjs';
6
6
 
7
7
  /**
8
8
  * Define the Expo background task handler for swap polling.
@@ -1,8 +1,8 @@
1
- import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-Ov-wp17v.js';
2
- export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-Ov-wp17v.js';
1
+ import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-C4HQHP9Z.js';
2
+ export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-C4HQHP9Z.js';
3
3
  import '@arkade-os/sdk/worker/expo';
4
4
  import '@arkade-os/sdk';
5
- import '../types-CS62-oEy.js';
5
+ import '../types-BKEkNZxK.js';
6
6
 
7
7
  /**
8
8
  * Define the Expo background task handler for swap polling.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  SWAP_POLL_TASK_TYPE,
3
3
  swapsPollProcessor
4
- } from "../chunk-WPAI2ECT.js";
4
+ } from "../chunk-HIG2OOAN.js";
5
5
  import {
6
6
  BoltzSwapProvider
7
- } from "../chunk-5GBBRBFS.js";
7
+ } from "../chunk-VKHFXGKL.js";
8
8
  import "../chunk-SJQJQO7P.js";
9
9
 
10
10
  // src/expo/background.ts
@@ -27,14 +27,19 @@ function createBackgroundWalletShim(args) {
27
27
  getVtxos: async () => notImplemented("getVtxos"),
28
28
  getBoardingUtxos: async () => notImplemented("getBoardingUtxos"),
29
29
  getTransactionHistory: async () => notImplemented("getTransactionHistory"),
30
+ getActivityHistory: async () => notImplemented("getActivityHistory"),
30
31
  getContractManager: async () => notImplemented("getContractManager"),
31
32
  getDelegateManager: async () => notImplemented("getDelegateManager"),
32
33
  getDelegatorManager: async () => notImplemented("getDelegatorManager"),
33
34
  sendBitcoin: async () => notImplemented("sendBitcoin"),
34
35
  send: async () => notImplemented("send"),
35
36
  settle: async () => notImplemented("settle"),
37
+ clear: async () => notImplemented("clear"),
36
38
  assetManager: new Proxy({}, {
37
39
  get: () => notImplemented("assetManager")
40
+ }),
41
+ activity: new Proxy({}, {
42
+ get: () => notImplemented("activity")
38
43
  })
39
44
  };
40
45
  }
@@ -56,13 +61,13 @@ function defineExpoSwapBackgroundTask(taskName, options) {
56
61
  const wallet = createBackgroundWalletShim({
57
62
  identity,
58
63
  getAddress: async () => {
59
- const { ArkAddress } = await import("@arkade-os/sdk");
64
+ const { ArkAddress, getNetwork } = await import("@arkade-os/sdk");
60
65
  const { hex } = await import("@scure/base");
61
66
  const info = await arkProvider.getInfo();
62
67
  const pubkey = await identity.xOnlyPublicKey();
63
68
  const serverPubKey = hex.decode(info.signerPubkey);
64
69
  const xOnlyServerPubKey = serverPubKey.length === 33 ? serverPubKey.slice(1) : serverPubKey;
65
- const hrp = info.network === "bitcoin" ? "ark" : "tark";
70
+ const hrp = getNetwork(info.network).hrp;
66
71
  return new ArkAddress(xOnlyServerPubKey, pubkey, hrp).encode();
67
72
  }
68
73
  });
@@ -959,6 +959,7 @@ var BoltzSwapProvider = class {
959
959
 
960
960
  // src/arkade-swaps.ts
961
961
  var import_sha23 = require("@noble/hashes/sha2.js");
962
+ var import_legacy2 = require("@noble/hashes/legacy.js");
962
963
  var import_base9 = require("@scure/base");
963
964
  var import_secp256k13 = require("@noble/curves/secp256k1.js");
964
965
  var import_utils3 = require("@noble/hashes/utils.js");
@@ -1182,18 +1183,6 @@ var import_base3 = require("@scure/base");
1182
1183
  var import_btc_signer = require("@scure/btc-signer");
1183
1184
  var import_payment = require("@scure/btc-signer/payment.js");
1184
1185
  var import_utils2 = require("@scure/btc-signer/utils.js");
1185
- var REGTEST_NETWORK = {
1186
- bech32: "bcrt",
1187
- pubKeyHash: 111,
1188
- scriptHash: 196,
1189
- wif: 239
1190
- };
1191
- var MUTINYNET_NETWORK = {
1192
- bech32: "tb",
1193
- pubKeyHash: 111,
1194
- scriptHash: 196,
1195
- wif: 239
1196
- };
1197
1186
  var deserializeLeaf = (leaf) => ({
1198
1187
  version: leaf.version,
1199
1188
  output: import_base3.hex.decode(leaf.output)
@@ -1227,6 +1216,19 @@ var deserializeSwapTree = (tree) => {
1227
1216
  ])
1228
1217
  };
1229
1218
  };
1219
+ var TAPLEAF_V1 = 192;
1220
+ var PUSH_32 = Uint8Array.of(32);
1221
+ var decodeScriptNum = (data) => data instanceof Uint8Array && data.length > 0 ? Number((0, import_btc_signer.ScriptNum)(5, true).decode(data)) : void 0;
1222
+ var assertChainHtlcLeaves = (tree, expected) => {
1223
+ if (tree.claimLeaf.version !== TAPLEAF_V1 || tree.refundLeaf.version !== TAPLEAF_V1)
1224
+ throw new Error("unexpected leaf version");
1225
+ const claim = import_btc_signer.Script.decode(tree.claimLeaf.output);
1226
+ if (claim.length !== 8 || claim[0] !== "SIZE" || !(claim[1] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[1], PUSH_32) || claim[2] !== "EQUALVERIFY" || claim[3] !== "HASH160" || !(claim[4] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[4], expected.preimageHash160) || claim[5] !== "EQUALVERIFY" || !(claim[6] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[6], expected.claimXOnly) || claim[7] !== "CHECKSIG")
1227
+ throw new Error("unexpected claim leaf");
1228
+ const refund = import_btc_signer.Script.decode(tree.refundLeaf.output);
1229
+ if (refund.length !== 4 || !(refund[0] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(refund[0], expected.refundXOnly) || refund[1] !== "CHECKSIGVERIFY" || decodeScriptNum(refund[2]) !== expected.timeoutBlockHeight || refund[3] !== "CHECKLOCKTIMEVERIFY")
1230
+ throw new Error("unexpected refund leaf");
1231
+ };
1230
1232
  var taprootHashTree = (tree) => {
1231
1233
  if (!Array.isArray(tree)) {
1232
1234
  return {
@@ -1294,8 +1296,8 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
1294
1296
  return tx;
1295
1297
  };
1296
1298
  var targetFee = (satPerVbyte, constructTx) => {
1297
- const tx = constructTx(BigInt(1));
1298
- return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
1299
+ const probe = constructTx(BigInt(1));
1300
+ return constructTx(BigInt(Math.ceil(probe.vsize * satPerVbyte)));
1299
1301
  };
1300
1302
 
1301
1303
  // src/utils/decoding.ts
@@ -1306,6 +1308,9 @@ var decodeInvoice = (invoice) => {
1306
1308
  const millisats = BigInt(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1307
1309
  return {
1308
1310
  expiry: decoded.expiry ?? 3600,
1311
+ // The timestamp fallback to 0 is just to satisfy the type checker.
1312
+ // In practice, the timestamp is always present in well-formatted BOLT11 invoices.
1313
+ timestamp: decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0,
1309
1314
  amountSats: Number(millisats / 1000n),
1310
1315
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1311
1316
  // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
@@ -2923,7 +2928,7 @@ var createVHTLCScript = (args) => {
2923
2928
  )
2924
2929
  });
2925
2930
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2926
- const hrp = network === "bitcoin" ? "ark" : "tark";
2931
+ const hrp = (0, import_sdk7.getNetwork)(network).hrp;
2927
2932
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2928
2933
  return { vhtlcScript, vhtlcAddress };
2929
2934
  };
@@ -3358,6 +3363,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3358
3363
  return {
3359
3364
  amount: pendingSwap.response.onchainAmount,
3360
3365
  expiry: decodedInvoice.expiry,
3366
+ timestamp: decodedInvoice.timestamp,
3361
3367
  invoice: pendingSwap.response.invoice,
3362
3368
  paymentHash: decodedInvoice.paymentHash,
3363
3369
  pendingSwap,
@@ -4325,6 +4331,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4325
4331
  }
4326
4332
  /**
4327
4333
  * Claim sats on BTC chain by claiming the HTLC.
4334
+ *
4335
+ * The claim output is `swapOutput.amount − max(feeToDeliverExactAmount, targetFee)`.
4336
+ *
4328
4337
  * @param pendingSwap - The pending chain swap with BTC transaction hex.
4329
4338
  * @returns The BTC transaction ID of the claim.
4330
4339
  */
@@ -4340,7 +4349,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4340
4349
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4341
4350
  const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4342
4351
  const arkInfo = await this.arkProvider.getInfo();
4343
- const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4352
+ const network = (0, import_sdk8.getNetwork)(arkInfo.network);
4344
4353
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4345
4354
  const musig = tweakMusig(
4346
4355
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
@@ -4880,8 +4889,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
4880
4889
  message: "Boltz is trying to scam us (invalid address)"
4881
4890
  });
4882
4891
  }
4892
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
4883
4893
  return true;
4884
4894
  }
4895
+ /**
4896
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
4897
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
4898
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
4899
+ * direction-specific keys, leaf version, and absolute refund CLTV.
4900
+ */
4901
+ verifyBtcChainHtlc(args) {
4902
+ const { to, swap, arkNetwork } = args;
4903
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
4904
+ if (!btcDetails.swapTree)
4905
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
4906
+ if (!btcDetails.serverPublicKey)
4907
+ throw new SwapError({
4908
+ message: `Swap ${swap.id}: missing server public key in BTC details`
4909
+ });
4910
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
4911
+ throw new SwapError({
4912
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
4913
+ });
4914
+ const network = (0, import_sdk8.getNetwork)(arkNetwork);
4915
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
4916
+ const ephemeralPub = import_secp256k13.secp256k1.getPublicKey(import_base9.hex.decode(swap.ephemeralKey));
4917
+ const musig = tweakMusig(
4918
+ create(import_base9.hex.decode(swap.ephemeralKey), [
4919
+ import_base9.hex.decode(btcDetails.serverPublicKey),
4920
+ ephemeralPub
4921
+ ]),
4922
+ swapTree.tree
4923
+ );
4924
+ const expectedScript = import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(btcDetails.lockupAddress));
4925
+ if (!(0, import_utils4.equalBytes)(p2trScript(musig.aggPubkey), expectedScript))
4926
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
4927
+ const boltzXOnly = toXOnly(import_base9.hex.decode(btcDetails.serverPublicKey));
4928
+ const userXOnly = toXOnly(ephemeralPub);
4929
+ try {
4930
+ assertChainHtlcLeaves(swapTree, {
4931
+ preimageHash160: (0, import_legacy2.ripemd160)(import_base9.hex.decode(swap.request.preimageHash)),
4932
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
4933
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
4934
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
4935
+ });
4936
+ } catch (err) {
4937
+ throw new SwapError({
4938
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
4939
+ });
4940
+ }
4941
+ }
4885
4942
  /**
4886
4943
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
4887
4944
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -1,7 +1,7 @@
1
- import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-C8CmzknM.cjs';
2
- import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-CS62-oEy.cjs';
3
- import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-B98F6MQF.cjs';
4
- export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor-B98F6MQF.cjs';
1
+ import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-BG3xEK31.cjs';
2
+ import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-BKEkNZxK.cjs';
3
+ import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-CIqTFkV1.cjs';
4
+ export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor-CIqTFkV1.cjs';
5
5
  import { ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
6
6
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
7
7
  import '@arkade-os/sdk/worker/expo';
@@ -1,7 +1,7 @@
1
- import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-gXHRbR09.js';
2
- import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-CS62-oEy.js';
3
- import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-Ov-wp17v.js';
4
- export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor-Ov-wp17v.js';
1
+ import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-A180YN8g.js';
2
+ import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-BKEkNZxK.js';
3
+ import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-C4HQHP9Z.js';
4
+ export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor-C4HQHP9Z.js';
5
5
  import { ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
6
6
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
7
7
  import '@arkade-os/sdk/worker/expo';
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  SWAP_POLL_TASK_TYPE
3
- } from "../chunk-WPAI2ECT.js";
3
+ } from "../chunk-HIG2OOAN.js";
4
4
  import {
5
5
  ArkadeSwaps
6
- } from "../chunk-5GBBRBFS.js";
6
+ } from "../chunk-VKHFXGKL.js";
7
7
  import "../chunk-SJQJQO7P.js";
8
8
 
9
9
  // src/expo/arkade-lightning.ts
package/dist/index.cjs CHANGED
@@ -95,7 +95,7 @@ __export(index_exports, {
95
95
  module.exports = __toCommonJS(index_exports);
96
96
 
97
97
  // package.json
98
- var version = "0.3.45";
98
+ var version = "0.3.47";
99
99
 
100
100
  // src/errors.ts
101
101
  var SwapError = class extends Error {
@@ -1091,6 +1091,7 @@ var BoltzSwapProvider = class {
1091
1091
 
1092
1092
  // src/arkade-swaps.ts
1093
1093
  var import_sha23 = require("@noble/hashes/sha2.js");
1094
+ var import_legacy2 = require("@noble/hashes/legacy.js");
1094
1095
  var import_base9 = require("@scure/base");
1095
1096
  var import_secp256k13 = require("@noble/curves/secp256k1.js");
1096
1097
  var import_utils3 = require("@noble/hashes/utils.js");
@@ -1314,18 +1315,6 @@ var import_base3 = require("@scure/base");
1314
1315
  var import_btc_signer = require("@scure/btc-signer");
1315
1316
  var import_payment = require("@scure/btc-signer/payment.js");
1316
1317
  var import_utils2 = require("@scure/btc-signer/utils.js");
1317
- var REGTEST_NETWORK = {
1318
- bech32: "bcrt",
1319
- pubKeyHash: 111,
1320
- scriptHash: 196,
1321
- wif: 239
1322
- };
1323
- var MUTINYNET_NETWORK = {
1324
- bech32: "tb",
1325
- pubKeyHash: 111,
1326
- scriptHash: 196,
1327
- wif: 239
1328
- };
1329
1318
  var deserializeLeaf = (leaf) => ({
1330
1319
  version: leaf.version,
1331
1320
  output: import_base3.hex.decode(leaf.output)
@@ -1359,6 +1348,19 @@ var deserializeSwapTree = (tree) => {
1359
1348
  ])
1360
1349
  };
1361
1350
  };
1351
+ var TAPLEAF_V1 = 192;
1352
+ var PUSH_32 = Uint8Array.of(32);
1353
+ var decodeScriptNum = (data) => data instanceof Uint8Array && data.length > 0 ? Number((0, import_btc_signer.ScriptNum)(5, true).decode(data)) : void 0;
1354
+ var assertChainHtlcLeaves = (tree, expected) => {
1355
+ if (tree.claimLeaf.version !== TAPLEAF_V1 || tree.refundLeaf.version !== TAPLEAF_V1)
1356
+ throw new Error("unexpected leaf version");
1357
+ const claim = import_btc_signer.Script.decode(tree.claimLeaf.output);
1358
+ if (claim.length !== 8 || claim[0] !== "SIZE" || !(claim[1] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[1], PUSH_32) || claim[2] !== "EQUALVERIFY" || claim[3] !== "HASH160" || !(claim[4] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[4], expected.preimageHash160) || claim[5] !== "EQUALVERIFY" || !(claim[6] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(claim[6], expected.claimXOnly) || claim[7] !== "CHECKSIG")
1359
+ throw new Error("unexpected claim leaf");
1360
+ const refund = import_btc_signer.Script.decode(tree.refundLeaf.output);
1361
+ if (refund.length !== 4 || !(refund[0] instanceof Uint8Array) || !(0, import_utils2.equalBytes)(refund[0], expected.refundXOnly) || refund[1] !== "CHECKSIGVERIFY" || decodeScriptNum(refund[2]) !== expected.timeoutBlockHeight || refund[3] !== "CHECKLOCKTIMEVERIFY")
1362
+ throw new Error("unexpected refund leaf");
1363
+ };
1362
1364
  var taprootHashTree = (tree) => {
1363
1365
  if (!Array.isArray(tree)) {
1364
1366
  return {
@@ -1426,8 +1428,8 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
1426
1428
  return tx;
1427
1429
  };
1428
1430
  var targetFee = (satPerVbyte, constructTx) => {
1429
- const tx = constructTx(BigInt(1));
1430
- return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
1431
+ const probe = constructTx(BigInt(1));
1432
+ return constructTx(BigInt(Math.ceil(probe.vsize * satPerVbyte)));
1431
1433
  };
1432
1434
 
1433
1435
  // src/utils/decoding.ts
@@ -1438,6 +1440,9 @@ var decodeInvoice = (invoice) => {
1438
1440
  const millisats = BigInt(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
1439
1441
  return {
1440
1442
  expiry: decoded.expiry ?? 3600,
1443
+ // The timestamp fallback to 0 is just to satisfy the type checker.
1444
+ // In practice, the timestamp is always present in well-formatted BOLT11 invoices.
1445
+ timestamp: decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0,
1441
1446
  amountSats: Number(millisats / 1000n),
1442
1447
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1443
1448
  // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
@@ -3076,7 +3081,7 @@ var createVHTLCScript = (args) => {
3076
3081
  )
3077
3082
  });
3078
3083
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
3079
- const hrp = network === "bitcoin" ? "ark" : "tark";
3084
+ const hrp = (0, import_sdk7.getNetwork)(network).hrp;
3080
3085
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
3081
3086
  return { vhtlcScript, vhtlcAddress };
3082
3087
  };
@@ -3511,6 +3516,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3511
3516
  return {
3512
3517
  amount: pendingSwap.response.onchainAmount,
3513
3518
  expiry: decodedInvoice.expiry,
3519
+ timestamp: decodedInvoice.timestamp,
3514
3520
  invoice: pendingSwap.response.invoice,
3515
3521
  paymentHash: decodedInvoice.paymentHash,
3516
3522
  pendingSwap,
@@ -4478,6 +4484,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
4478
4484
  }
4479
4485
  /**
4480
4486
  * Claim sats on BTC chain by claiming the HTLC.
4487
+ *
4488
+ * The claim output is `swapOutput.amount − max(feeToDeliverExactAmount, targetFee)`.
4489
+ *
4481
4490
  * @param pendingSwap - The pending chain swap with BTC transaction hex.
4482
4491
  * @returns The BTC transaction ID of the claim.
4483
4492
  */
@@ -4493,7 +4502,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4493
4502
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4494
4503
  const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4495
4504
  const arkInfo = await this.arkProvider.getInfo();
4496
- const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4505
+ const network = (0, import_sdk8.getNetwork)(arkInfo.network);
4497
4506
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4498
4507
  const musig = tweakMusig(
4499
4508
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
@@ -5033,8 +5042,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
5033
5042
  message: "Boltz is trying to scam us (invalid address)"
5034
5043
  });
5035
5044
  }
5045
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
5036
5046
  return true;
5037
5047
  }
5048
+ /**
5049
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
5050
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
5051
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
5052
+ * direction-specific keys, leaf version, and absolute refund CLTV.
5053
+ */
5054
+ verifyBtcChainHtlc(args) {
5055
+ const { to, swap, arkNetwork } = args;
5056
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
5057
+ if (!btcDetails.swapTree)
5058
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
5059
+ if (!btcDetails.serverPublicKey)
5060
+ throw new SwapError({
5061
+ message: `Swap ${swap.id}: missing server public key in BTC details`
5062
+ });
5063
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
5064
+ throw new SwapError({
5065
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
5066
+ });
5067
+ const network = (0, import_sdk8.getNetwork)(arkNetwork);
5068
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
5069
+ const ephemeralPub = import_secp256k13.secp256k1.getPublicKey(import_base9.hex.decode(swap.ephemeralKey));
5070
+ const musig = tweakMusig(
5071
+ create(import_base9.hex.decode(swap.ephemeralKey), [
5072
+ import_base9.hex.decode(btcDetails.serverPublicKey),
5073
+ ephemeralPub
5074
+ ]),
5075
+ swapTree.tree
5076
+ );
5077
+ const expectedScript = import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(btcDetails.lockupAddress));
5078
+ if (!(0, import_utils4.equalBytes)(p2trScript(musig.aggPubkey), expectedScript))
5079
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
5080
+ const boltzXOnly = toXOnly(import_base9.hex.decode(btcDetails.serverPublicKey));
5081
+ const userXOnly = toXOnly(ephemeralPub);
5082
+ try {
5083
+ assertChainHtlcLeaves(swapTree, {
5084
+ preimageHash160: (0, import_legacy2.ripemd160)(import_base9.hex.decode(swap.request.preimageHash)),
5085
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
5086
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
5087
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
5088
+ });
5089
+ } catch (err) {
5090
+ throw new SwapError({
5091
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
5092
+ });
5093
+ }
5094
+ }
5038
5095
  /**
5039
5096
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
5040
5097
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { Q as QuoteSwapOptions, I as IArkadeSwaps, V as VhtlcTimeouts } from './arkade-swaps-C8CmzknM.cjs';
2
- export { A as ArkadeSwaps } from './arkade-swaps-C8CmzknM.cjs';
3
- import { B as BoltzSwap, D as DecodedInvoice, a as BoltzChainSwap, b as BoltzReverseSwap, c as BoltzSubmarineSwap, A as ArkadeSwapsConfig, N as Network, C as CreateLightningInvoiceRequest, S as SendLightningPaymentRequest, F as FeesResponse, d as Chain, e as CreateLightningInvoiceResponse, O as OptimisticSendLightningPaymentResponse, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, j as ArkToBtcResponse, k as BtcToArkResponse, l as ChainArkRefundOutcome, m as SwapRepository, n as SwapManagerClient, o as SendLightningPaymentResponse, p as GetSwapsFilter } from './types-CS62-oEy.cjs';
4
- export { q as ArkadeSwapsCreateConfig, r as BoltzSwapProvider, s as BoltzSwapStatus, I as IncomingPaymentSubscription, P as PendingChainSwap, t as PendingReverseSwap, u as PendingSubmarineSwap, v as PendingSwap, w as SubmarineProgressionStatus, x as SubmarineRecoveryStatus, y as SwapManager, z as SwapManagerCallbacks, E as SwapManagerConfig, H as SwapManagerEvents, V as Vtxo, J as hasSubmarineStatusReached, K as isChainClaimableStatus, M as isChainFailedStatus, Q as isChainFinalStatus, R as isChainPendingStatus, T as isChainRefundableStatus, U as isChainSignableStatus, W as isChainSuccessStatus, X as isChainSwapClaimable, Y as isChainSwapRefundable, Z as isPendingChainSwap, _ as isPendingReverseSwap, $ as isPendingSubmarineSwap, a0 as isReverseClaimableStatus, a1 as isReverseFailedStatus, a2 as isReverseFinalStatus, a3 as isReversePendingStatus, a4 as isReverseSuccessStatus, a5 as isReverseSwapClaimable, a6 as isSubmarineFailedStatus, a7 as isSubmarineFinalStatus, a8 as isSubmarinePendingStatus, a9 as isSubmarineRefundableStatus, aa as isSubmarineSuccessStatus, ab as isSubmarineSwapRefundable } from './types-CS62-oEy.cjs';
1
+ import { Q as QuoteSwapOptions, I as IArkadeSwaps, V as VhtlcTimeouts } from './arkade-swaps-BG3xEK31.cjs';
2
+ export { A as ArkadeSwaps } from './arkade-swaps-BG3xEK31.cjs';
3
+ import { B as BoltzSwap, D as DecodedInvoice, a as BoltzChainSwap, b as BoltzReverseSwap, c as BoltzSubmarineSwap, A as ArkadeSwapsConfig, N as Network, C as CreateLightningInvoiceRequest, S as SendLightningPaymentRequest, F as FeesResponse, d as Chain, e as CreateLightningInvoiceResponse, O as OptimisticSendLightningPaymentResponse, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, j as ArkToBtcResponse, k as BtcToArkResponse, l as ChainArkRefundOutcome, m as SwapRepository, n as SwapManagerClient, o as SendLightningPaymentResponse, p as GetSwapsFilter } from './types-BKEkNZxK.cjs';
4
+ export { q as ArkadeSwapsCreateConfig, r as BoltzSwapProvider, s as BoltzSwapStatus, I as IncomingPaymentSubscription, P as PendingChainSwap, t as PendingReverseSwap, u as PendingSubmarineSwap, v as PendingSwap, w as SubmarineProgressionStatus, x as SubmarineRecoveryStatus, y as SwapManager, z as SwapManagerCallbacks, E as SwapManagerConfig, H as SwapManagerEvents, V as Vtxo, J as hasSubmarineStatusReached, K as isChainClaimableStatus, M as isChainFailedStatus, Q as isChainFinalStatus, R as isChainPendingStatus, T as isChainRefundableStatus, U as isChainSignableStatus, W as isChainSuccessStatus, X as isChainSwapClaimable, Y as isChainSwapRefundable, Z as isPendingChainSwap, _ as isPendingReverseSwap, $ as isPendingSubmarineSwap, a0 as isReverseClaimableStatus, a1 as isReverseFailedStatus, a2 as isReverseFinalStatus, a3 as isReversePendingStatus, a4 as isReverseSuccessStatus, a5 as isReverseSwapClaimable, a6 as isSubmarineFailedStatus, a7 as isSubmarineFinalStatus, a8 as isSubmarinePendingStatus, a9 as isSubmarineRefundableStatus, aa as isSubmarineSuccessStatus, ab as isSubmarineSwapRefundable } from './types-BKEkNZxK.cjs';
5
5
  import { Transaction } from '@scure/btc-signer';
6
6
  import { MessageHandler, RequestEnvelope, ArkInfo, ResponseEnvelope, IWallet, IReadonlyWallet, VHTLC, Identity, ArkTxInput } from '@arkade-os/sdk';
7
7
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { Q as QuoteSwapOptions, I as IArkadeSwaps, V as VhtlcTimeouts } from './arkade-swaps-gXHRbR09.js';
2
- export { A as ArkadeSwaps } from './arkade-swaps-gXHRbR09.js';
3
- import { B as BoltzSwap, D as DecodedInvoice, a as BoltzChainSwap, b as BoltzReverseSwap, c as BoltzSubmarineSwap, A as ArkadeSwapsConfig, N as Network, C as CreateLightningInvoiceRequest, S as SendLightningPaymentRequest, F as FeesResponse, d as Chain, e as CreateLightningInvoiceResponse, O as OptimisticSendLightningPaymentResponse, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, j as ArkToBtcResponse, k as BtcToArkResponse, l as ChainArkRefundOutcome, m as SwapRepository, n as SwapManagerClient, o as SendLightningPaymentResponse, p as GetSwapsFilter } from './types-CS62-oEy.js';
4
- export { q as ArkadeSwapsCreateConfig, r as BoltzSwapProvider, s as BoltzSwapStatus, I as IncomingPaymentSubscription, P as PendingChainSwap, t as PendingReverseSwap, u as PendingSubmarineSwap, v as PendingSwap, w as SubmarineProgressionStatus, x as SubmarineRecoveryStatus, y as SwapManager, z as SwapManagerCallbacks, E as SwapManagerConfig, H as SwapManagerEvents, V as Vtxo, J as hasSubmarineStatusReached, K as isChainClaimableStatus, M as isChainFailedStatus, Q as isChainFinalStatus, R as isChainPendingStatus, T as isChainRefundableStatus, U as isChainSignableStatus, W as isChainSuccessStatus, X as isChainSwapClaimable, Y as isChainSwapRefundable, Z as isPendingChainSwap, _ as isPendingReverseSwap, $ as isPendingSubmarineSwap, a0 as isReverseClaimableStatus, a1 as isReverseFailedStatus, a2 as isReverseFinalStatus, a3 as isReversePendingStatus, a4 as isReverseSuccessStatus, a5 as isReverseSwapClaimable, a6 as isSubmarineFailedStatus, a7 as isSubmarineFinalStatus, a8 as isSubmarinePendingStatus, a9 as isSubmarineRefundableStatus, aa as isSubmarineSuccessStatus, ab as isSubmarineSwapRefundable } from './types-CS62-oEy.js';
1
+ import { Q as QuoteSwapOptions, I as IArkadeSwaps, V as VhtlcTimeouts } from './arkade-swaps-A180YN8g.js';
2
+ export { A as ArkadeSwaps } from './arkade-swaps-A180YN8g.js';
3
+ import { B as BoltzSwap, D as DecodedInvoice, a as BoltzChainSwap, b as BoltzReverseSwap, c as BoltzSubmarineSwap, A as ArkadeSwapsConfig, N as Network, C as CreateLightningInvoiceRequest, S as SendLightningPaymentRequest, F as FeesResponse, d as Chain, e as CreateLightningInvoiceResponse, O as OptimisticSendLightningPaymentResponse, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, j as ArkToBtcResponse, k as BtcToArkResponse, l as ChainArkRefundOutcome, m as SwapRepository, n as SwapManagerClient, o as SendLightningPaymentResponse, p as GetSwapsFilter } from './types-BKEkNZxK.js';
4
+ export { q as ArkadeSwapsCreateConfig, r as BoltzSwapProvider, s as BoltzSwapStatus, I as IncomingPaymentSubscription, P as PendingChainSwap, t as PendingReverseSwap, u as PendingSubmarineSwap, v as PendingSwap, w as SubmarineProgressionStatus, x as SubmarineRecoveryStatus, y as SwapManager, z as SwapManagerCallbacks, E as SwapManagerConfig, H as SwapManagerEvents, V as Vtxo, J as hasSubmarineStatusReached, K as isChainClaimableStatus, M as isChainFailedStatus, Q as isChainFinalStatus, R as isChainPendingStatus, T as isChainRefundableStatus, U as isChainSignableStatus, W as isChainSuccessStatus, X as isChainSwapClaimable, Y as isChainSwapRefundable, Z as isPendingChainSwap, _ as isPendingReverseSwap, $ as isPendingSubmarineSwap, a0 as isReverseClaimableStatus, a1 as isReverseFailedStatus, a2 as isReverseFinalStatus, a3 as isReversePendingStatus, a4 as isReverseSuccessStatus, a5 as isReverseSwapClaimable, a6 as isSubmarineFailedStatus, a7 as isSubmarineFinalStatus, a8 as isSubmarinePendingStatus, a9 as isSubmarineRefundableStatus, aa as isSubmarineSuccessStatus, ab as isSubmarineSwapRefundable } from './types-BKEkNZxK.js';
5
5
  import { Transaction } from '@scure/btc-signer';
6
6
  import { MessageHandler, RequestEnvelope, ArkInfo, ResponseEnvelope, IWallet, IReadonlyWallet, VHTLC, Identity, ArkTxInput } from '@arkade-os/sdk';
7
7
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
package/dist/index.js CHANGED
@@ -53,14 +53,14 @@ import {
53
53
  updateReverseSwapStatus,
54
54
  updateSubmarineSwapStatus,
55
55
  verifySignatures
56
- } from "./chunk-5GBBRBFS.js";
56
+ } from "./chunk-VKHFXGKL.js";
57
57
  import {
58
58
  applyCreatedAtOrder,
59
59
  applySwapsFilter
60
60
  } from "./chunk-SJQJQO7P.js";
61
61
 
62
62
  // package.json
63
- var version = "0.3.45";
63
+ var version = "0.3.47";
64
64
 
65
65
  // src/serviceWorker/arkade-swaps-message-handler.ts
66
66
  import { RestArkProvider, RestIndexerProvider } from "@arkade-os/sdk";
@@ -1,5 +1,5 @@
1
1
  import { RealmLike } from '@arkade-os/sdk/repositories/realm';
2
- import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.cjs';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-BKEkNZxK.cjs';
3
3
  import '@arkade-os/sdk';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { RealmLike } from '@arkade-os/sdk/repositories/realm';
2
- import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.js';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-BKEkNZxK.js';
3
3
  import '@arkade-os/sdk';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { SQLExecutor } from '@arkade-os/sdk/repositories/sqlite';
2
- import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.cjs';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-BKEkNZxK.cjs';
3
3
  import '@arkade-os/sdk';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { SQLExecutor } from '@arkade-os/sdk/repositories/sqlite';
2
- import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.js';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-BKEkNZxK.js';
3
3
  import '@arkade-os/sdk';
4
4
 
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  import { AsyncStorageTaskQueue, TaskProcessor } from '@arkade-os/sdk/worker/expo';
2
2
  import { Identity, ArkProvider, IndexerProvider, IWallet } from '@arkade-os/sdk';
3
- import { m as SwapRepository, N as Network, r as BoltzSwapProvider, A as ArkadeSwapsConfig } from './types-CS62-oEy.js';
3
+ import { m as SwapRepository, N as Network, r as BoltzSwapProvider, A as ArkadeSwapsConfig } from './types-BKEkNZxK.js';
4
4
 
5
5
  /**
6
6
  * Dependencies injected into every swap processor at runtime.
@@ -1,6 +1,6 @@
1
1
  import { AsyncStorageTaskQueue, TaskProcessor } from '@arkade-os/sdk/worker/expo';
2
2
  import { Identity, ArkProvider, IndexerProvider, IWallet } from '@arkade-os/sdk';
3
- import { m as SwapRepository, N as Network, r as BoltzSwapProvider, A as ArkadeSwapsConfig } from './types-CS62-oEy.cjs';
3
+ import { m as SwapRepository, N as Network, r as BoltzSwapProvider, A as ArkadeSwapsConfig } from './types-BKEkNZxK.cjs';
4
4
 
5
5
  /**
6
6
  * Dependencies injected into every swap processor at runtime.
@@ -935,8 +935,10 @@ interface CreateLightningInvoiceRequest {
935
935
  interface CreateLightningInvoiceResponse {
936
936
  /** The on-chain amount in satoshis (after Boltz fees). */
937
937
  amount: number;
938
- /** Invoice expiry timestamp (Unix seconds). */
938
+ /** Invoice expiry: seconds from invoice creation (timestamp) until it expires. */
939
939
  expiry: number;
940
+ /** Invoice creation timestamp (Unix seconds). Always present in a valid BOLT11 invoice; `0` should not occur in practice. */
941
+ timestamp: number;
940
942
  /** The BOLT11-encoded Lightning invoice string. */
941
943
  invoice: string;
942
944
  /** The payment hash (hex-encoded). */
@@ -1174,6 +1176,8 @@ type ArkadeSwapsCreateConfig = Omit<ArkadeSwapsConfig, "swapProvider"> & {
1174
1176
  interface DecodedInvoice {
1175
1177
  /** Invoice expiry timestamp (Unix seconds). */
1176
1178
  expiry: number;
1179
+ /** Invoice creation timestamp (Unix seconds). Always present in a valid BOLT11 invoice; `0` should not occur in practice. */
1180
+ timestamp: number;
1177
1181
  /** Invoice amount in satoshis. */
1178
1182
  amountSats: number;
1179
1183
  /** Invoice description string (BOLT11 `d` field; "" if none). */
@@ -935,8 +935,10 @@ interface CreateLightningInvoiceRequest {
935
935
  interface CreateLightningInvoiceResponse {
936
936
  /** The on-chain amount in satoshis (after Boltz fees). */
937
937
  amount: number;
938
- /** Invoice expiry timestamp (Unix seconds). */
938
+ /** Invoice expiry: seconds from invoice creation (timestamp) until it expires. */
939
939
  expiry: number;
940
+ /** Invoice creation timestamp (Unix seconds). Always present in a valid BOLT11 invoice; `0` should not occur in practice. */
941
+ timestamp: number;
940
942
  /** The BOLT11-encoded Lightning invoice string. */
941
943
  invoice: string;
942
944
  /** The payment hash (hex-encoded). */
@@ -1174,6 +1176,8 @@ type ArkadeSwapsCreateConfig = Omit<ArkadeSwapsConfig, "swapProvider"> & {
1174
1176
  interface DecodedInvoice {
1175
1177
  /** Invoice expiry timestamp (Unix seconds). */
1176
1178
  expiry: number;
1179
+ /** Invoice creation timestamp (Unix seconds). Always present in a valid BOLT11 invoice; `0` should not occur in practice. */
1180
+ timestamp: number;
1177
1181
  /** Invoice amount in satoshis. */
1178
1182
  amountSats: number;
1179
1183
  /** Invoice description string (BOLT11 `d` field; "" if none). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/boltz-swap",
3
- "version": "0.3.45",
3
+ "version": "0.3.47",
4
4
  "type": "module",
5
5
  "description": "A production-ready TypeScript package that brings Boltz submarine-swaps to Arkade.",
6
6
  "main": "./dist/index.js",
@@ -76,7 +76,7 @@
76
76
  "@scure/btc-signer": "2.0.1",
77
77
  "bip68": "1.0.4",
78
78
  "light-bolt11-decoder": "3.2.0",
79
- "@arkade-os/sdk": "0.4.40"
79
+ "@arkade-os/sdk": "0.4.42"
80
80
  },
81
81
  "peerDependencies": {
82
82
  "expo-task-manager": ">=3.0.0",