@arkade-os/boltz-swap 0.3.44 → 0.3.46

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-8NrCdOpS.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-CS62-oEy.cjs';
3
3
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
4
4
 
5
5
  /**
@@ -415,6 +415,13 @@ declare class ArkadeSwaps {
415
415
  swap: BoltzChainSwap;
416
416
  arkInfo: ArkInfo;
417
417
  }): Promise<boolean>;
418
+ /**
419
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
420
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
421
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
422
+ * direction-specific keys, leaf version, and absolute refund CLTV.
423
+ */
424
+ private verifyBtcChainHtlc;
418
425
  /**
419
426
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
420
427
  * 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-8NrCdOpS.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-CS62-oEy.js';
3
3
  import { TransactionOutput } from '@scure/btc-signer/psbt.js';
4
4
 
5
5
  /**
@@ -415,6 +415,13 @@ declare class ArkadeSwaps {
415
415
  swap: BoltzChainSwap;
416
416
  arkInfo: ArkInfo;
417
417
  }): Promise<boolean>;
418
+ /**
419
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
420
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
421
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
422
+ * direction-specific keys, leaf version, and absolute refund CLTV.
423
+ */
424
+ private verifyBtcChainHtlc;
418
425
  /**
419
426
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
420
427
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -571,20 +571,26 @@ var BoltzSwapProvider = class {
571
571
  invoiceAmount,
572
572
  claimPublicKey,
573
573
  preimageHash,
574
- description
574
+ description,
575
+ descriptionHash
575
576
  }) {
576
577
  if (claimPublicKey.length != 66) {
577
578
  throw new SwapError({
578
579
  message: "claimPublicKey must be a compressed public key"
579
580
  });
580
581
  }
582
+ if (descriptionHash !== void 0 && !/^[0-9a-fA-F]{64}$/.test(descriptionHash)) {
583
+ throw new SwapError({
584
+ message: "descriptionHash must be a 32-byte SHA256 (64 hex chars)"
585
+ });
586
+ }
581
587
  const requestBody = {
582
588
  from: "BTC",
583
589
  to: "ARK",
584
590
  invoiceAmount,
585
591
  claimPublicKey,
586
592
  preimageHash,
587
- description: description?.trim() || "Send to Arkade address",
593
+ ...descriptionHash ? { descriptionHash } : { description: description?.trim() || "Send to Arkade address" },
588
594
  ...this.referralId ? { referralId: this.referralId } : {}
589
595
  };
590
596
  const response = await this.request(
@@ -997,6 +1003,12 @@ var decodeInvoice = (invoice) => {
997
1003
  expiry: decoded.expiry ?? 3600,
998
1004
  amountSats: Number(millisats / 1000n),
999
1005
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1006
+ // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
1007
+ // Section union even though the decoder emits it. Widen just this lookup
1008
+ // rather than patch the library's types (separate repo, out of scope).
1009
+ descriptionHash: decoded.sections.find(
1010
+ (s) => s.name === "description_hash"
1011
+ )?.value ?? "",
1000
1012
  paymentHash: decoded.sections.find((s) => s.name === "payment_hash")?.value ?? ""
1001
1013
  };
1002
1014
  };
@@ -2364,14 +2376,16 @@ var IndexedDbSwapRepository = class {
2364
2376
  // src/arkade-swaps.ts
2365
2377
  import {
2366
2378
  ArkAddress as ArkAddress2,
2367
- isRecoverable
2379
+ isRecoverable,
2380
+ getNetwork as getNetwork2
2368
2381
  } from "@arkade-os/sdk";
2369
2382
  import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
2383
+ import { ripemd160 as ripemd1602 } from "@noble/hashes/legacy.js";
2370
2384
  import { hex as hex8 } from "@scure/base";
2371
2385
  import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
2372
2386
  import { randomBytes } from "@noble/hashes/utils.js";
2373
2387
  import { Address as Address2, OutScript as OutScript2, SigHash as SigHash2, Transaction as Transaction6 } from "@scure/btc-signer";
2374
- import { NETWORK } from "@scure/btc-signer/utils.js";
2388
+ import { equalBytes as equalBytes3 } from "@scure/btc-signer/utils.js";
2375
2389
 
2376
2390
  // src/utils/musig.ts
2377
2391
  import { secp256k1 } from "@noble/curves/secp256k1.js";
@@ -2593,21 +2607,9 @@ var create = (privateKey, publicKeys) => {
2593
2607
  // src/utils/boltz-swap-tx.ts
2594
2608
  import { schnorr } from "@noble/curves/secp256k1.js";
2595
2609
  import { hex as hex4 } from "@scure/base";
2596
- import { Script, Transaction as Transaction2 } from "@scure/btc-signer";
2610
+ import { Script, ScriptNum, Transaction as Transaction2 } from "@scure/btc-signer";
2597
2611
  import { tapLeafHash } from "@scure/btc-signer/payment.js";
2598
2612
  import { compareBytes, equalBytes as equalBytes2 } from "@scure/btc-signer/utils.js";
2599
- var REGTEST_NETWORK = {
2600
- bech32: "bcrt",
2601
- pubKeyHash: 111,
2602
- scriptHash: 196,
2603
- wif: 239
2604
- };
2605
- var MUTINYNET_NETWORK = {
2606
- bech32: "tb",
2607
- pubKeyHash: 111,
2608
- scriptHash: 196,
2609
- wif: 239
2610
- };
2611
2613
  var deserializeLeaf = (leaf) => ({
2612
2614
  version: leaf.version,
2613
2615
  output: hex4.decode(leaf.output)
@@ -2641,6 +2643,19 @@ var deserializeSwapTree = (tree) => {
2641
2643
  ])
2642
2644
  };
2643
2645
  };
2646
+ var TAPLEAF_V1 = 192;
2647
+ var PUSH_32 = Uint8Array.of(32);
2648
+ var decodeScriptNum = (data) => data instanceof Uint8Array && data.length > 0 ? Number(ScriptNum(5, true).decode(data)) : void 0;
2649
+ var assertChainHtlcLeaves = (tree, expected) => {
2650
+ if (tree.claimLeaf.version !== TAPLEAF_V1 || tree.refundLeaf.version !== TAPLEAF_V1)
2651
+ throw new Error("unexpected leaf version");
2652
+ const claim = Script.decode(tree.claimLeaf.output);
2653
+ 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")
2654
+ throw new Error("unexpected claim leaf");
2655
+ const refund = Script.decode(tree.refundLeaf.output);
2656
+ if (refund.length !== 4 || !(refund[0] instanceof Uint8Array) || !equalBytes2(refund[0], expected.refundXOnly) || refund[1] !== "CHECKSIGVERIFY" || decodeScriptNum(refund[2]) !== expected.timeoutBlockHeight || refund[3] !== "CHECKLOCKTIMEVERIFY")
2657
+ throw new Error("unexpected refund leaf");
2658
+ };
2644
2659
  var taprootHashTree = (tree) => {
2645
2660
  if (!Array.isArray(tree)) {
2646
2661
  return {
@@ -2792,6 +2807,7 @@ import {
2792
2807
  buildOffchainTx,
2793
2808
  getSequence as getSequence2,
2794
2809
  Intent,
2810
+ getNetwork,
2795
2811
  networks,
2796
2812
  VHTLC,
2797
2813
  VtxoScript as VtxoScript2,
@@ -2978,7 +2994,7 @@ var createVHTLCScript = (args) => {
2978
2994
  )
2979
2995
  });
2980
2996
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2981
- const hrp = network === "bitcoin" ? "ark" : "tark";
2997
+ const hrp = getNetwork(network).hrp;
2982
2998
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2983
2999
  return { vhtlcScript, vhtlcAddress };
2984
3000
  };
@@ -3440,7 +3456,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3440
3456
  invoiceAmount: args.amount,
3441
3457
  claimPublicKey,
3442
3458
  preimageHash,
3443
- ...args.description?.trim() ? { description: args.description.trim() } : {}
3459
+ ...args.descriptionHash !== void 0 ? { descriptionHash: args.descriptionHash } : args.description?.trim() ? { description: args.description.trim() } : {}
3444
3460
  };
3445
3461
  const swapResponse = await this.swapProvider.createReverseSwap(swapRequest);
3446
3462
  const decodedInvoice = decodeInvoice(swapResponse.invoice);
@@ -4395,7 +4411,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4395
4411
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4396
4412
  const lockupTx = Transaction6.fromRaw(hex8.decode(swapStatus.transaction.hex));
4397
4413
  const arkInfo = await this.arkProvider.getInfo();
4398
- const network = arkInfo.network === "bitcoin" ? NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4414
+ const network = getNetwork2(arkInfo.network);
4399
4415
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4400
4416
  const musig = tweakMusig(
4401
4417
  create(hex8.decode(pendingSwap.ephemeralKey), [
@@ -4935,8 +4951,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
4935
4951
  message: "Boltz is trying to scam us (invalid address)"
4936
4952
  });
4937
4953
  }
4954
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
4938
4955
  return true;
4939
4956
  }
4957
+ /**
4958
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
4959
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
4960
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
4961
+ * direction-specific keys, leaf version, and absolute refund CLTV.
4962
+ */
4963
+ verifyBtcChainHtlc(args) {
4964
+ const { to, swap, arkNetwork } = args;
4965
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
4966
+ if (!btcDetails.swapTree)
4967
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
4968
+ if (!btcDetails.serverPublicKey)
4969
+ throw new SwapError({
4970
+ message: `Swap ${swap.id}: missing server public key in BTC details`
4971
+ });
4972
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
4973
+ throw new SwapError({
4974
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
4975
+ });
4976
+ const network = getNetwork2(arkNetwork);
4977
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
4978
+ const ephemeralPub = secp256k12.getPublicKey(hex8.decode(swap.ephemeralKey));
4979
+ const musig = tweakMusig(
4980
+ create(hex8.decode(swap.ephemeralKey), [
4981
+ hex8.decode(btcDetails.serverPublicKey),
4982
+ ephemeralPub
4983
+ ]),
4984
+ swapTree.tree
4985
+ );
4986
+ const expectedScript = OutScript2.encode(Address2(network).decode(btcDetails.lockupAddress));
4987
+ if (!equalBytes3(p2trScript(musig.aggPubkey), expectedScript))
4988
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
4989
+ const boltzXOnly = toXOnly(hex8.decode(btcDetails.serverPublicKey));
4990
+ const userXOnly = toXOnly(ephemeralPub);
4991
+ try {
4992
+ assertChainHtlcLeaves(swapTree, {
4993
+ preimageHash160: ripemd1602(hex8.decode(swap.request.preimageHash)),
4994
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
4995
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
4996
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
4997
+ });
4998
+ } catch (err) {
4999
+ throw new SwapError({
5000
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
5001
+ });
5002
+ }
5003
+ }
4940
5004
  /**
4941
5005
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
4942
5006
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -7,7 +7,7 @@ import {
7
7
  isSubmarineFinalStatus,
8
8
  isSubmarineSwapRefundable,
9
9
  logger
10
- } from "./chunk-QVXFEX5F.js";
10
+ } from "./chunk-GXO2EB6C.js";
11
11
 
12
12
  // src/expo/swapsPollProcessor.ts
13
13
  var SWAP_POLL_TASK_TYPE = "swap-poll";
@@ -544,20 +544,26 @@ var BoltzSwapProvider = class {
544
544
  invoiceAmount,
545
545
  claimPublicKey,
546
546
  preimageHash,
547
- description
547
+ description,
548
+ descriptionHash
548
549
  }) {
549
550
  if (claimPublicKey.length != 66) {
550
551
  throw new SwapError({
551
552
  message: "claimPublicKey must be a compressed public key"
552
553
  });
553
554
  }
555
+ if (descriptionHash !== void 0 && !/^[0-9a-fA-F]{64}$/.test(descriptionHash)) {
556
+ throw new SwapError({
557
+ message: "descriptionHash must be a 32-byte SHA256 (64 hex chars)"
558
+ });
559
+ }
554
560
  const requestBody = {
555
561
  from: "BTC",
556
562
  to: "ARK",
557
563
  invoiceAmount,
558
564
  claimPublicKey,
559
565
  preimageHash,
560
- description: description?.trim() || "Send to Arkade address",
566
+ ...descriptionHash ? { descriptionHash } : { description: description?.trim() || "Send to Arkade address" },
561
567
  ...this.referralId ? { referralId: this.referralId } : {}
562
568
  };
563
569
  const response = await this.request(
@@ -963,6 +969,7 @@ var BoltzSwapProvider = class {
963
969
  // src/arkade-swaps.ts
964
970
  var import_sdk8 = require("@arkade-os/sdk");
965
971
  var import_sha23 = require("@noble/hashes/sha2.js");
972
+ var import_legacy2 = require("@noble/hashes/legacy.js");
966
973
  var import_base9 = require("@scure/base");
967
974
  var import_secp256k13 = require("@noble/curves/secp256k1.js");
968
975
  var import_utils3 = require("@noble/hashes/utils.js");
@@ -1186,18 +1193,6 @@ var import_base3 = require("@scure/base");
1186
1193
  var import_btc_signer = require("@scure/btc-signer");
1187
1194
  var import_payment = require("@scure/btc-signer/payment.js");
1188
1195
  var import_utils2 = require("@scure/btc-signer/utils.js");
1189
- var REGTEST_NETWORK = {
1190
- bech32: "bcrt",
1191
- pubKeyHash: 111,
1192
- scriptHash: 196,
1193
- wif: 239
1194
- };
1195
- var MUTINYNET_NETWORK = {
1196
- bech32: "tb",
1197
- pubKeyHash: 111,
1198
- scriptHash: 196,
1199
- wif: 239
1200
- };
1201
1196
  var deserializeLeaf = (leaf) => ({
1202
1197
  version: leaf.version,
1203
1198
  output: import_base3.hex.decode(leaf.output)
@@ -1231,6 +1226,19 @@ var deserializeSwapTree = (tree) => {
1231
1226
  ])
1232
1227
  };
1233
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
+ };
1234
1242
  var taprootHashTree = (tree) => {
1235
1243
  if (!Array.isArray(tree)) {
1236
1244
  return {
@@ -1312,6 +1320,12 @@ var decodeInvoice = (invoice) => {
1312
1320
  expiry: decoded.expiry ?? 3600,
1313
1321
  amountSats: Number(millisats / 1000n),
1314
1322
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1323
+ // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
1324
+ // Section union even though the decoder emits it. Widen just this lookup
1325
+ // rather than patch the library's types (separate repo, out of scope).
1326
+ descriptionHash: decoded.sections.find(
1327
+ (s) => s.name === "description_hash"
1328
+ )?.value ?? "",
1315
1329
  paymentHash: decoded.sections.find((s) => s.name === "payment_hash")?.value ?? ""
1316
1330
  };
1317
1331
  };
@@ -2921,7 +2935,7 @@ var createVHTLCScript = (args) => {
2921
2935
  )
2922
2936
  });
2923
2937
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2924
- const hrp = network === "bitcoin" ? "ark" : "tark";
2938
+ const hrp = (0, import_sdk7.getNetwork)(network).hrp;
2925
2939
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2926
2940
  return { vhtlcScript, vhtlcAddress };
2927
2941
  };
@@ -3383,7 +3397,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3383
3397
  invoiceAmount: args.amount,
3384
3398
  claimPublicKey,
3385
3399
  preimageHash,
3386
- ...args.description?.trim() ? { description: args.description.trim() } : {}
3400
+ ...args.descriptionHash !== void 0 ? { descriptionHash: args.descriptionHash } : args.description?.trim() ? { description: args.description.trim() } : {}
3387
3401
  };
3388
3402
  const swapResponse = await this.swapProvider.createReverseSwap(swapRequest);
3389
3403
  const decodedInvoice = decodeInvoice(swapResponse.invoice);
@@ -4338,7 +4352,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4338
4352
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4339
4353
  const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4340
4354
  const arkInfo = await this.arkProvider.getInfo();
4341
- const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4355
+ const network = (0, import_sdk8.getNetwork)(arkInfo.network);
4342
4356
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4343
4357
  const musig = tweakMusig(
4344
4358
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
@@ -4878,8 +4892,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
4878
4892
  message: "Boltz is trying to scam us (invalid address)"
4879
4893
  });
4880
4894
  }
4895
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
4881
4896
  return true;
4882
4897
  }
4898
+ /**
4899
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
4900
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
4901
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
4902
+ * direction-specific keys, leaf version, and absolute refund CLTV.
4903
+ */
4904
+ verifyBtcChainHtlc(args) {
4905
+ const { to, swap, arkNetwork } = args;
4906
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
4907
+ if (!btcDetails.swapTree)
4908
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
4909
+ if (!btcDetails.serverPublicKey)
4910
+ throw new SwapError({
4911
+ message: `Swap ${swap.id}: missing server public key in BTC details`
4912
+ });
4913
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
4914
+ throw new SwapError({
4915
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
4916
+ });
4917
+ const network = (0, import_sdk8.getNetwork)(arkNetwork);
4918
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
4919
+ const ephemeralPub = import_secp256k13.secp256k1.getPublicKey(import_base9.hex.decode(swap.ephemeralKey));
4920
+ const musig = tweakMusig(
4921
+ create(import_base9.hex.decode(swap.ephemeralKey), [
4922
+ import_base9.hex.decode(btcDetails.serverPublicKey),
4923
+ ephemeralPub
4924
+ ]),
4925
+ swapTree.tree
4926
+ );
4927
+ const expectedScript = import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(btcDetails.lockupAddress));
4928
+ if (!(0, import_utils4.equalBytes)(p2trScript(musig.aggPubkey), expectedScript))
4929
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
4930
+ const boltzXOnly = toXOnly(import_base9.hex.decode(btcDetails.serverPublicKey));
4931
+ const userXOnly = toXOnly(ephemeralPub);
4932
+ try {
4933
+ assertChainHtlcLeaves(swapTree, {
4934
+ preimageHash160: (0, import_legacy2.ripemd160)(import_base9.hex.decode(swap.request.preimageHash)),
4935
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
4936
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
4937
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
4938
+ });
4939
+ } catch (err) {
4940
+ throw new SwapError({
4941
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
4942
+ });
4943
+ }
4944
+ }
4883
4945
  /**
4884
4946
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
4885
4947
  * over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
@@ -5537,14 +5599,19 @@ function createBackgroundWalletShim(args) {
5537
5599
  getVtxos: async () => notImplemented("getVtxos"),
5538
5600
  getBoardingUtxos: async () => notImplemented("getBoardingUtxos"),
5539
5601
  getTransactionHistory: async () => notImplemented("getTransactionHistory"),
5602
+ getActivityHistory: async () => notImplemented("getActivityHistory"),
5540
5603
  getContractManager: async () => notImplemented("getContractManager"),
5541
5604
  getDelegateManager: async () => notImplemented("getDelegateManager"),
5542
5605
  getDelegatorManager: async () => notImplemented("getDelegatorManager"),
5543
5606
  sendBitcoin: async () => notImplemented("sendBitcoin"),
5544
5607
  send: async () => notImplemented("send"),
5545
5608
  settle: async () => notImplemented("settle"),
5609
+ clear: async () => notImplemented("clear"),
5546
5610
  assetManager: new Proxy({}, {
5547
5611
  get: () => notImplemented("assetManager")
5612
+ }),
5613
+ activity: new Proxy({}, {
5614
+ get: () => notImplemented("activity")
5548
5615
  })
5549
5616
  };
5550
5617
  }
@@ -5566,13 +5633,13 @@ function defineExpoSwapBackgroundTask(taskName, options) {
5566
5633
  const wallet = createBackgroundWalletShim({
5567
5634
  identity,
5568
5635
  getAddress: async () => {
5569
- const { ArkAddress: ArkAddress3 } = await import("@arkade-os/sdk");
5636
+ const { ArkAddress: ArkAddress3, getNetwork: getNetwork3 } = await import("@arkade-os/sdk");
5570
5637
  const { hex: hex9 } = await import("@scure/base");
5571
5638
  const info = await arkProvider.getInfo();
5572
5639
  const pubkey = await identity.xOnlyPublicKey();
5573
5640
  const serverPubKey = hex9.decode(info.signerPubkey);
5574
5641
  const xOnlyServerPubKey = serverPubKey.length === 33 ? serverPubKey.slice(1) : serverPubKey;
5575
- const hrp = info.network === "bitcoin" ? "ark" : "tark";
5642
+ const hrp = getNetwork3(info.network).hrp;
5576
5643
  return new ArkAddress3(xOnlyServerPubKey, pubkey, hrp).encode();
5577
5644
  }
5578
5645
  });
@@ -1,8 +1,8 @@
1
- import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-CGMXUKPe.cjs';
2
- export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-CGMXUKPe.cjs';
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';
3
3
  import '@arkade-os/sdk/worker/expo';
4
4
  import '@arkade-os/sdk';
5
- import '../types-8NrCdOpS.cjs';
5
+ import '../types-CS62-oEy.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-CuDM6sxV.js';
2
- export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-CuDM6sxV.js';
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';
3
3
  import '@arkade-os/sdk/worker/expo';
4
4
  import '@arkade-os/sdk';
5
- import '../types-8NrCdOpS.js';
5
+ import '../types-CS62-oEy.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-PHF6C2NE.js";
4
+ } from "../chunk-UJXW5JBT.js";
5
5
  import {
6
6
  BoltzSwapProvider
7
- } from "../chunk-QVXFEX5F.js";
7
+ } from "../chunk-GXO2EB6C.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
  });
@@ -535,20 +535,26 @@ var BoltzSwapProvider = class {
535
535
  invoiceAmount,
536
536
  claimPublicKey,
537
537
  preimageHash,
538
- description
538
+ description,
539
+ descriptionHash
539
540
  }) {
540
541
  if (claimPublicKey.length != 66) {
541
542
  throw new SwapError({
542
543
  message: "claimPublicKey must be a compressed public key"
543
544
  });
544
545
  }
546
+ if (descriptionHash !== void 0 && !/^[0-9a-fA-F]{64}$/.test(descriptionHash)) {
547
+ throw new SwapError({
548
+ message: "descriptionHash must be a 32-byte SHA256 (64 hex chars)"
549
+ });
550
+ }
545
551
  const requestBody = {
546
552
  from: "BTC",
547
553
  to: "ARK",
548
554
  invoiceAmount,
549
555
  claimPublicKey,
550
556
  preimageHash,
551
- description: description?.trim() || "Send to Arkade address",
557
+ ...descriptionHash ? { descriptionHash } : { description: description?.trim() || "Send to Arkade address" },
552
558
  ...this.referralId ? { referralId: this.referralId } : {}
553
559
  };
554
560
  const response = await this.request(
@@ -953,6 +959,7 @@ var BoltzSwapProvider = class {
953
959
 
954
960
  // src/arkade-swaps.ts
955
961
  var import_sha23 = require("@noble/hashes/sha2.js");
962
+ var import_legacy2 = require("@noble/hashes/legacy.js");
956
963
  var import_base9 = require("@scure/base");
957
964
  var import_secp256k13 = require("@noble/curves/secp256k1.js");
958
965
  var import_utils3 = require("@noble/hashes/utils.js");
@@ -1176,18 +1183,6 @@ var import_base3 = require("@scure/base");
1176
1183
  var import_btc_signer = require("@scure/btc-signer");
1177
1184
  var import_payment = require("@scure/btc-signer/payment.js");
1178
1185
  var import_utils2 = require("@scure/btc-signer/utils.js");
1179
- var REGTEST_NETWORK = {
1180
- bech32: "bcrt",
1181
- pubKeyHash: 111,
1182
- scriptHash: 196,
1183
- wif: 239
1184
- };
1185
- var MUTINYNET_NETWORK = {
1186
- bech32: "tb",
1187
- pubKeyHash: 111,
1188
- scriptHash: 196,
1189
- wif: 239
1190
- };
1191
1186
  var deserializeLeaf = (leaf) => ({
1192
1187
  version: leaf.version,
1193
1188
  output: import_base3.hex.decode(leaf.output)
@@ -1221,6 +1216,19 @@ var deserializeSwapTree = (tree) => {
1221
1216
  ])
1222
1217
  };
1223
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
+ };
1224
1232
  var taprootHashTree = (tree) => {
1225
1233
  if (!Array.isArray(tree)) {
1226
1234
  return {
@@ -1302,6 +1310,12 @@ var decodeInvoice = (invoice) => {
1302
1310
  expiry: decoded.expiry ?? 3600,
1303
1311
  amountSats: Number(millisats / 1000n),
1304
1312
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1313
+ // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
1314
+ // Section union even though the decoder emits it. Widen just this lookup
1315
+ // rather than patch the library's types (separate repo, out of scope).
1316
+ descriptionHash: decoded.sections.find(
1317
+ (s) => s.name === "description_hash"
1318
+ )?.value ?? "",
1305
1319
  paymentHash: decoded.sections.find((s) => s.name === "payment_hash")?.value ?? ""
1306
1320
  };
1307
1321
  };
@@ -2911,7 +2925,7 @@ var createVHTLCScript = (args) => {
2911
2925
  )
2912
2926
  });
2913
2927
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
2914
- const hrp = network === "bitcoin" ? "ark" : "tark";
2928
+ const hrp = (0, import_sdk7.getNetwork)(network).hrp;
2915
2929
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
2916
2930
  return { vhtlcScript, vhtlcAddress };
2917
2931
  };
@@ -3373,7 +3387,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3373
3387
  invoiceAmount: args.amount,
3374
3388
  claimPublicKey,
3375
3389
  preimageHash,
3376
- ...args.description?.trim() ? { description: args.description.trim() } : {}
3390
+ ...args.descriptionHash !== void 0 ? { descriptionHash: args.descriptionHash } : args.description?.trim() ? { description: args.description.trim() } : {}
3377
3391
  };
3378
3392
  const swapResponse = await this.swapProvider.createReverseSwap(swapRequest);
3379
3393
  const decodedInvoice = decodeInvoice(swapResponse.invoice);
@@ -4328,7 +4342,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4328
4342
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4329
4343
  const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4330
4344
  const arkInfo = await this.arkProvider.getInfo();
4331
- const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4345
+ const network = (0, import_sdk8.getNetwork)(arkInfo.network);
4332
4346
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4333
4347
  const musig = tweakMusig(
4334
4348
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
@@ -4868,8 +4882,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
4868
4882
  message: "Boltz is trying to scam us (invalid address)"
4869
4883
  });
4870
4884
  }
4885
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
4871
4886
  return true;
4872
4887
  }
4888
+ /**
4889
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
4890
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
4891
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
4892
+ * direction-specific keys, leaf version, and absolute refund CLTV.
4893
+ */
4894
+ verifyBtcChainHtlc(args) {
4895
+ const { to, swap, arkNetwork } = args;
4896
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
4897
+ if (!btcDetails.swapTree)
4898
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
4899
+ if (!btcDetails.serverPublicKey)
4900
+ throw new SwapError({
4901
+ message: `Swap ${swap.id}: missing server public key in BTC details`
4902
+ });
4903
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
4904
+ throw new SwapError({
4905
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
4906
+ });
4907
+ const network = (0, import_sdk8.getNetwork)(arkNetwork);
4908
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
4909
+ const ephemeralPub = import_secp256k13.secp256k1.getPublicKey(import_base9.hex.decode(swap.ephemeralKey));
4910
+ const musig = tweakMusig(
4911
+ create(import_base9.hex.decode(swap.ephemeralKey), [
4912
+ import_base9.hex.decode(btcDetails.serverPublicKey),
4913
+ ephemeralPub
4914
+ ]),
4915
+ swapTree.tree
4916
+ );
4917
+ const expectedScript = import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(btcDetails.lockupAddress));
4918
+ if (!(0, import_utils4.equalBytes)(p2trScript(musig.aggPubkey), expectedScript))
4919
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
4920
+ const boltzXOnly = toXOnly(import_base9.hex.decode(btcDetails.serverPublicKey));
4921
+ const userXOnly = toXOnly(ephemeralPub);
4922
+ try {
4923
+ assertChainHtlcLeaves(swapTree, {
4924
+ preimageHash160: (0, import_legacy2.ripemd160)(import_base9.hex.decode(swap.request.preimageHash)),
4925
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
4926
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
4927
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
4928
+ });
4929
+ } catch (err) {
4930
+ throw new SwapError({
4931
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
4932
+ });
4933
+ }
4934
+ }
4873
4935
  /**
4874
4936
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
4875
4937
  * 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-DwgfLCMY.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-8NrCdOpS.cjs';
3
- import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-CGMXUKPe.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-CGMXUKPe.cjs';
1
+ import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-D-L0_0Ri.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';
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-2q3VSgb4.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-8NrCdOpS.js';
3
- import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-CuDM6sxV.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-CuDM6sxV.js';
1
+ import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-fmdCFjif.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';
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-PHF6C2NE.js";
3
+ } from "../chunk-UJXW5JBT.js";
4
4
  import {
5
5
  ArkadeSwaps
6
- } from "../chunk-QVXFEX5F.js";
6
+ } from "../chunk-GXO2EB6C.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.44";
98
+ var version = "0.3.46";
99
99
 
100
100
  // src/errors.ts
101
101
  var SwapError = class extends Error {
@@ -667,20 +667,26 @@ var BoltzSwapProvider = class {
667
667
  invoiceAmount,
668
668
  claimPublicKey,
669
669
  preimageHash,
670
- description
670
+ description,
671
+ descriptionHash
671
672
  }) {
672
673
  if (claimPublicKey.length != 66) {
673
674
  throw new SwapError({
674
675
  message: "claimPublicKey must be a compressed public key"
675
676
  });
676
677
  }
678
+ if (descriptionHash !== void 0 && !/^[0-9a-fA-F]{64}$/.test(descriptionHash)) {
679
+ throw new SwapError({
680
+ message: "descriptionHash must be a 32-byte SHA256 (64 hex chars)"
681
+ });
682
+ }
677
683
  const requestBody = {
678
684
  from: "BTC",
679
685
  to: "ARK",
680
686
  invoiceAmount,
681
687
  claimPublicKey,
682
688
  preimageHash,
683
- description: description?.trim() || "Send to Arkade address",
689
+ ...descriptionHash ? { descriptionHash } : { description: description?.trim() || "Send to Arkade address" },
684
690
  ...this.referralId ? { referralId: this.referralId } : {}
685
691
  };
686
692
  const response = await this.request(
@@ -1085,6 +1091,7 @@ var BoltzSwapProvider = class {
1085
1091
 
1086
1092
  // src/arkade-swaps.ts
1087
1093
  var import_sha23 = require("@noble/hashes/sha2.js");
1094
+ var import_legacy2 = require("@noble/hashes/legacy.js");
1088
1095
  var import_base9 = require("@scure/base");
1089
1096
  var import_secp256k13 = require("@noble/curves/secp256k1.js");
1090
1097
  var import_utils3 = require("@noble/hashes/utils.js");
@@ -1308,18 +1315,6 @@ var import_base3 = require("@scure/base");
1308
1315
  var import_btc_signer = require("@scure/btc-signer");
1309
1316
  var import_payment = require("@scure/btc-signer/payment.js");
1310
1317
  var import_utils2 = require("@scure/btc-signer/utils.js");
1311
- var REGTEST_NETWORK = {
1312
- bech32: "bcrt",
1313
- pubKeyHash: 111,
1314
- scriptHash: 196,
1315
- wif: 239
1316
- };
1317
- var MUTINYNET_NETWORK = {
1318
- bech32: "tb",
1319
- pubKeyHash: 111,
1320
- scriptHash: 196,
1321
- wif: 239
1322
- };
1323
1318
  var deserializeLeaf = (leaf) => ({
1324
1319
  version: leaf.version,
1325
1320
  output: import_base3.hex.decode(leaf.output)
@@ -1353,6 +1348,19 @@ var deserializeSwapTree = (tree) => {
1353
1348
  ])
1354
1349
  };
1355
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
+ };
1356
1364
  var taprootHashTree = (tree) => {
1357
1365
  if (!Array.isArray(tree)) {
1358
1366
  return {
@@ -1434,6 +1442,12 @@ var decodeInvoice = (invoice) => {
1434
1442
  expiry: decoded.expiry ?? 3600,
1435
1443
  amountSats: Number(millisats / 1000n),
1436
1444
  description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
1445
+ // description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
1446
+ // Section union even though the decoder emits it. Widen just this lookup
1447
+ // rather than patch the library's types (separate repo, out of scope).
1448
+ descriptionHash: decoded.sections.find(
1449
+ (s) => s.name === "description_hash"
1450
+ )?.value ?? "",
1437
1451
  paymentHash: decoded.sections.find((s) => s.name === "payment_hash")?.value ?? ""
1438
1452
  };
1439
1453
  };
@@ -3064,7 +3078,7 @@ var createVHTLCScript = (args) => {
3064
3078
  )
3065
3079
  });
3066
3080
  if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
3067
- const hrp = network === "bitcoin" ? "ark" : "tark";
3081
+ const hrp = (0, import_sdk7.getNetwork)(network).hrp;
3068
3082
  const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
3069
3083
  return { vhtlcScript, vhtlcAddress };
3070
3084
  };
@@ -3526,7 +3540,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
3526
3540
  invoiceAmount: args.amount,
3527
3541
  claimPublicKey,
3528
3542
  preimageHash,
3529
- ...args.description?.trim() ? { description: args.description.trim() } : {}
3543
+ ...args.descriptionHash !== void 0 ? { descriptionHash: args.descriptionHash } : args.description?.trim() ? { description: args.description.trim() } : {}
3530
3544
  };
3531
3545
  const swapResponse = await this.swapProvider.createReverseSwap(swapRequest);
3532
3546
  const decodedInvoice = decodeInvoice(swapResponse.invoice);
@@ -4481,7 +4495,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
4481
4495
  throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
4482
4496
  const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
4483
4497
  const arkInfo = await this.arkProvider.getInfo();
4484
- const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
4498
+ const network = (0, import_sdk8.getNetwork)(arkInfo.network);
4485
4499
  const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
4486
4500
  const musig = tweakMusig(
4487
4501
  create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
@@ -5021,8 +5035,56 @@ var ArkadeSwaps = class _ArkadeSwaps {
5021
5035
  message: "Boltz is trying to scam us (invalid address)"
5022
5036
  });
5023
5037
  }
5038
+ this.verifyBtcChainHtlc({ to, swap, arkNetwork: arkInfo.network });
5024
5039
  return true;
5025
5040
  }
5041
+ /**
5042
+ * Verifies the BTC-side Taproot HTLC of a chain swap before funds are
5043
+ * committed: the lockup address must bind to MuSig2(boltz, user) over
5044
+ * Boltz's leaves, and those leaves must enforce the agreed preimage hash,
5045
+ * direction-specific keys, leaf version, and absolute refund CLTV.
5046
+ */
5047
+ verifyBtcChainHtlc(args) {
5048
+ const { to, swap, arkNetwork } = args;
5049
+ const btcDetails = to === "ARK" ? swap.response.lockupDetails : swap.response.claimDetails;
5050
+ if (!btcDetails.swapTree)
5051
+ throw new SwapError({ message: `Swap ${swap.id}: missing swap tree in BTC details` });
5052
+ if (!btcDetails.serverPublicKey)
5053
+ throw new SwapError({
5054
+ message: `Swap ${swap.id}: missing server public key in BTC details`
5055
+ });
5056
+ if (typeof btcDetails.timeoutBlockHeight !== "number")
5057
+ throw new SwapError({
5058
+ message: `Swap ${swap.id}: missing timeout block height in BTC details`
5059
+ });
5060
+ const network = (0, import_sdk8.getNetwork)(arkNetwork);
5061
+ const swapTree = deserializeSwapTree(btcDetails.swapTree);
5062
+ const ephemeralPub = import_secp256k13.secp256k1.getPublicKey(import_base9.hex.decode(swap.ephemeralKey));
5063
+ const musig = tweakMusig(
5064
+ create(import_base9.hex.decode(swap.ephemeralKey), [
5065
+ import_base9.hex.decode(btcDetails.serverPublicKey),
5066
+ ephemeralPub
5067
+ ]),
5068
+ swapTree.tree
5069
+ );
5070
+ const expectedScript = import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(btcDetails.lockupAddress));
5071
+ if (!(0, import_utils4.equalBytes)(p2trScript(musig.aggPubkey), expectedScript))
5072
+ throw new SwapError({ message: "Boltz is trying to scam us (invalid BTC address)" });
5073
+ const boltzXOnly = toXOnly(import_base9.hex.decode(btcDetails.serverPublicKey));
5074
+ const userXOnly = toXOnly(ephemeralPub);
5075
+ try {
5076
+ assertChainHtlcLeaves(swapTree, {
5077
+ preimageHash160: (0, import_legacy2.ripemd160)(import_base9.hex.decode(swap.request.preimageHash)),
5078
+ claimXOnly: to === "ARK" ? boltzXOnly : userXOnly,
5079
+ refundXOnly: to === "ARK" ? userXOnly : boltzXOnly,
5080
+ timeoutBlockHeight: btcDetails.timeoutBlockHeight
5081
+ });
5082
+ } catch (err) {
5083
+ throw new SwapError({
5084
+ message: `Boltz is trying to scam us (invalid BTC HTLC: ${err.message})`
5085
+ });
5086
+ }
5087
+ }
5026
5088
  /**
5027
5089
  * Renegotiates the quote for an existing chain swap. Convenience wrapper
5028
5090
  * 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-DwgfLCMY.cjs';
2
- export { A as ArkadeSwaps } from './arkade-swaps-DwgfLCMY.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-8NrCdOpS.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-8NrCdOpS.cjs';
1
+ import { Q as QuoteSwapOptions, I as IArkadeSwaps, V as VhtlcTimeouts } from './arkade-swaps-D-L0_0Ri.cjs';
2
+ export { A as ArkadeSwaps } from './arkade-swaps-D-L0_0Ri.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';
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-2q3VSgb4.js';
2
- export { A as ArkadeSwaps } from './arkade-swaps-2q3VSgb4.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-8NrCdOpS.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-8NrCdOpS.js';
1
+ import { Q as QuoteSwapOptions, I as IArkadeSwaps, V as VhtlcTimeouts } from './arkade-swaps-fmdCFjif.js';
2
+ export { A as ArkadeSwaps } from './arkade-swaps-fmdCFjif.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';
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-QVXFEX5F.js";
56
+ } from "./chunk-GXO2EB6C.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.44";
63
+ var version = "0.3.46";
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-8NrCdOpS.cjs';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.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-8NrCdOpS.js';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.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-8NrCdOpS.cjs';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.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-8NrCdOpS.js';
2
+ import { m as SwapRepository, B as BoltzSwap, p as GetSwapsFilter } from '../../types-CS62-oEy.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-8NrCdOpS.cjs';
3
+ import { m as SwapRepository, N as Network, r as BoltzSwapProvider, A as ArkadeSwapsConfig } from './types-CS62-oEy.cjs';
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-8NrCdOpS.js';
3
+ import { m as SwapRepository, N as Network, r as BoltzSwapProvider, A as ArkadeSwapsConfig } from './types-CS62-oEy.js';
4
4
 
5
5
  /**
6
6
  * Dependencies injected into every swap processor at runtime.
@@ -144,6 +144,12 @@ type CreateReverseSwapRequest = {
144
144
  preimageHash: string;
145
145
  /** Optional description for the BOLT11 invoice. */
146
146
  description?: string;
147
+ /**
148
+ * Optional SHA256 description hash (hex, 32 bytes / 64 chars) to commit
149
+ * into the invoice instead of a plaintext description. BOLT11 carries one
150
+ * or the other, never both; when set, `description` is omitted.
151
+ */
152
+ descriptionHash?: string;
147
153
  };
148
154
  /** Response from creating a reverse swap. */
149
155
  type CreateReverseSwapResponse = {
@@ -345,7 +351,7 @@ declare class BoltzSwapProvider {
345
351
  /** Creates a submarine swap (Arkade → Lightning) on Boltz. */
346
352
  createSubmarineSwap({ invoice, refundPublicKey, }: CreateSubmarineSwapRequest): Promise<CreateSubmarineSwapResponse>;
347
353
  /** Creates a reverse swap (Lightning → Arkade) on Boltz. */
348
- createReverseSwap({ invoiceAmount, claimPublicKey, preimageHash, description, }: CreateReverseSwapRequest): Promise<CreateReverseSwapResponse>;
354
+ createReverseSwap({ invoiceAmount, claimPublicKey, preimageHash, description, descriptionHash, }: CreateReverseSwapRequest): Promise<CreateReverseSwapResponse>;
349
355
  /** Creates a chain swap (ARK ↔ BTC) on Boltz. */
350
356
  createChainSwap({ to, from, preimageHash, feeSatsPerByte, claimPublicKey, refundPublicKey, serverLockAmount, userLockAmount, }: CreateChainSwapRequest): Promise<CreateChainSwapResponse>;
351
357
  /** Requests Boltz co-signature for a submarine swap refund. Returns signed transaction + checkpoint. */
@@ -915,6 +921,15 @@ interface CreateLightningInvoiceRequest {
915
921
  amount: number;
916
922
  /** Optional description embedded in the BOLT11 invoice. */
917
923
  description?: string;
924
+ /**
925
+ * Optional SHA256 description hash (hex, 32 bytes) to commit into the
926
+ * BOLT11 invoice instead of a plaintext description. A BOLT11 invoice
927
+ * carries either a description or a description hash, never both, so when
928
+ * this is set `description` is ignored. Use this for flows that must bind
929
+ * the invoice to an external document — e.g. NIP-57 zaps, where the hash
930
+ * is SHA256 of the zap request and the receipt later proves the match.
931
+ */
932
+ descriptionHash?: string;
918
933
  }
919
934
  /** Response containing the created Lightning invoice and swap details. */
920
935
  interface CreateLightningInvoiceResponse {
@@ -1161,8 +1176,10 @@ interface DecodedInvoice {
1161
1176
  expiry: number;
1162
1177
  /** Invoice amount in satoshis. */
1163
1178
  amountSats: number;
1164
- /** Invoice description string. */
1179
+ /** Invoice description string (BOLT11 `d` field; "" if none). */
1165
1180
  description: string;
1181
+ /** Invoice description hash (BOLT11 `h` field, hex; "" if none). */
1182
+ descriptionHash: string;
1166
1183
  /** Payment hash (hex-encoded). */
1167
1184
  paymentHash: string;
1168
1185
  }
@@ -144,6 +144,12 @@ type CreateReverseSwapRequest = {
144
144
  preimageHash: string;
145
145
  /** Optional description for the BOLT11 invoice. */
146
146
  description?: string;
147
+ /**
148
+ * Optional SHA256 description hash (hex, 32 bytes / 64 chars) to commit
149
+ * into the invoice instead of a plaintext description. BOLT11 carries one
150
+ * or the other, never both; when set, `description` is omitted.
151
+ */
152
+ descriptionHash?: string;
147
153
  };
148
154
  /** Response from creating a reverse swap. */
149
155
  type CreateReverseSwapResponse = {
@@ -345,7 +351,7 @@ declare class BoltzSwapProvider {
345
351
  /** Creates a submarine swap (Arkade → Lightning) on Boltz. */
346
352
  createSubmarineSwap({ invoice, refundPublicKey, }: CreateSubmarineSwapRequest): Promise<CreateSubmarineSwapResponse>;
347
353
  /** Creates a reverse swap (Lightning → Arkade) on Boltz. */
348
- createReverseSwap({ invoiceAmount, claimPublicKey, preimageHash, description, }: CreateReverseSwapRequest): Promise<CreateReverseSwapResponse>;
354
+ createReverseSwap({ invoiceAmount, claimPublicKey, preimageHash, description, descriptionHash, }: CreateReverseSwapRequest): Promise<CreateReverseSwapResponse>;
349
355
  /** Creates a chain swap (ARK ↔ BTC) on Boltz. */
350
356
  createChainSwap({ to, from, preimageHash, feeSatsPerByte, claimPublicKey, refundPublicKey, serverLockAmount, userLockAmount, }: CreateChainSwapRequest): Promise<CreateChainSwapResponse>;
351
357
  /** Requests Boltz co-signature for a submarine swap refund. Returns signed transaction + checkpoint. */
@@ -915,6 +921,15 @@ interface CreateLightningInvoiceRequest {
915
921
  amount: number;
916
922
  /** Optional description embedded in the BOLT11 invoice. */
917
923
  description?: string;
924
+ /**
925
+ * Optional SHA256 description hash (hex, 32 bytes) to commit into the
926
+ * BOLT11 invoice instead of a plaintext description. A BOLT11 invoice
927
+ * carries either a description or a description hash, never both, so when
928
+ * this is set `description` is ignored. Use this for flows that must bind
929
+ * the invoice to an external document — e.g. NIP-57 zaps, where the hash
930
+ * is SHA256 of the zap request and the receipt later proves the match.
931
+ */
932
+ descriptionHash?: string;
918
933
  }
919
934
  /** Response containing the created Lightning invoice and swap details. */
920
935
  interface CreateLightningInvoiceResponse {
@@ -1161,8 +1176,10 @@ interface DecodedInvoice {
1161
1176
  expiry: number;
1162
1177
  /** Invoice amount in satoshis. */
1163
1178
  amountSats: number;
1164
- /** Invoice description string. */
1179
+ /** Invoice description string (BOLT11 `d` field; "" if none). */
1165
1180
  description: string;
1181
+ /** Invoice description hash (BOLT11 `h` field, hex; "" if none). */
1182
+ descriptionHash: string;
1166
1183
  /** Payment hash (hex-encoded). */
1167
1184
  paymentHash: string;
1168
1185
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/boltz-swap",
3
- "version": "0.3.44",
3
+ "version": "0.3.46",
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.39"
79
+ "@arkade-os/sdk": "0.4.41"
80
80
  },
81
81
  "peerDependencies": {
82
82
  "expo-task-manager": ">=3.0.0",