@arkade-os/swap 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -53,20 +53,20 @@ __export(index_exports, {
53
53
  ONCHAIN_RECEIVE_PAIR: () => ONCHAIN_RECEIVE_PAIR,
54
54
  ONCHAIN_SECONDS_PER_BLOCK: () => ONCHAIN_SECONDS_PER_BLOCK,
55
55
  ONCHAIN_SEND_PAIR: () => ONCHAIN_SEND_PAIR,
56
+ PreimageNotRecoverableError: () => PreimageNotRecoverableError,
56
57
  QUOTE_OPTIONS: () => QUOTE_OPTIONS,
57
58
  REFUND_MTP_LAG_SECONDS: () => REFUND_MTP_LAG_SECONDS,
58
- RFQ_PREIMAGE_TAG: () => RFQ_PREIMAGE_TAG,
59
59
  RFQ_RESOLVED_STATES: () => RFQ_RESOLVED_STATES,
60
60
  RFQ_SWAP_TERMINAL_STATES: () => RFQ_SWAP_TERMINAL_STATES,
61
61
  RFQ_TERMINAL_STATES: () => RFQ_TERMINAL_STATES,
62
62
  RefundNotLocallyPossibleError: () => RefundNotLocallyPossibleError,
63
63
  RfqSwapManager: () => RfqSwapManager,
64
+ SOLO_REFUND_HEADROOM_SECONDS: () => SOLO_REFUND_HEADROOM_SECONDS,
64
65
  SWAP_LOCKUP_CONTRACT_KIND: () => SWAP_LOCKUP_CONTRACT_KIND,
65
66
  SWAP_LOCKUP_CONTRACT_LABEL: () => SWAP_LOCKUP_CONTRACT_LABEL,
66
67
  SWAP_LOCKUP_CONTRACT_TYPE: () => SWAP_LOCKUP_CONTRACT_TYPE,
67
68
  SwapRefusal: () => SwapRefusal,
68
69
  addAssetSwap: () => addAssetSwap,
69
- adoptSwapDescriptor: () => adoptSwapDescriptor,
70
70
  arkadeSwapRequest: () => arkadeSwapRequest,
71
71
  assertFundable: () => assertFundable,
72
72
  assertReceivable: () => assertReceivable,
@@ -75,7 +75,6 @@ __export(index_exports, {
75
75
  awaitRfqResolution: () => awaitRfqResolution,
76
76
  buildHtlcClaim: () => buildHtlcClaim,
77
77
  buildHtlcRefund: () => buildHtlcRefund,
78
- buildPreimageMessage: () => buildPreimageMessage,
79
78
  cancelOffer: () => cancelOffer,
80
79
  claimOnchainFill: () => claimOnchainFill,
81
80
  claimReceiveLockup: () => claimReceiveLockup,
@@ -87,8 +86,6 @@ __export(index_exports, {
87
86
  deriveLightningReceive: () => deriveLightningReceive,
88
87
  deriveOnchainReceive: () => deriveOnchainReceive,
89
88
  deriveOnchainSend: () => deriveOnchainSend,
90
- derivePreimage: () => derivePreimage,
91
- deriveSwapSecrets: () => deriveSwapSecrets,
92
89
  discoverMarkets: () => discoverMarkets,
93
90
  encodeOffer: () => encodeOffer,
94
91
  extractPreimage: () => extractPreimage,
@@ -97,7 +94,6 @@ __export(index_exports, {
97
94
  getAssetSwaps: () => getAssetSwaps,
98
95
  getAssetSwapsOrThrow: () => getAssetSwapsOrThrow,
99
96
  httpTransport: () => httpTransport,
100
- isDeterministicSigner: () => isDeterministicSigner,
101
97
  isRfqSwapTerminal: () => isRfqSwapTerminal,
102
98
  isRfqTerminal: () => isRfqTerminal,
103
99
  lightningReceiveRequest: () => lightningReceiveRequest,
@@ -113,10 +109,9 @@ __export(index_exports, {
113
109
  onchainReceiveRequest: () => onchainReceiveRequest,
114
110
  onchainSendRequest: () => onchainSendRequest,
115
111
  paymentHashOf: () => paymentHashOf,
116
- preimageForRfqSecrets: () => preimageForRfqSecrets,
112
+ preimageForSwapRecord: () => preimageForSwapRecord,
117
113
  pushClaim: () => pushClaim,
118
114
  pushRefundWithoutReceiver: () => pushRefundWithoutReceiver,
119
- randomSwapSecrets: () => randomSwapSecrets,
120
115
  readLockupFate: () => readLockupFate,
121
116
  receiveVtxoScript: () => receiveVtxoScript,
122
117
  refundIfUnresolved: () => refundIfUnresolved,
@@ -129,15 +124,12 @@ __export(index_exports, {
129
124
  restoreAssetSwaps: () => restoreAssetSwaps,
130
125
  retireSettledOfferContracts: () => retireSettledOfferContracts,
131
126
  rfqPair: () => rfqPair,
132
- rfqSecretsOfRecord: () => rfqSecretsOfRecord,
133
- rfqSecretsToRecord: () => rfqSecretsToRecord,
134
127
  sealClaimPacket: () => sealClaimPacket,
135
- senderIdentityForRfqSecrets: () => senderIdentityForRfqSecrets,
136
128
  senderIdentityForSwapRecord: () => senderIdentityForSwapRecord,
137
- senderPubkeyForRfqSecrets: () => senderPubkeyForRfqSecrets,
138
129
  spendTxidsOf: () => spendTxidsOf,
139
130
  spendUpdate: () => spendUpdate,
140
131
  swapPrograms: () => swapPrograms,
132
+ swapSecretsToRecord: () => swapSecretsToRecord,
141
133
  unilateralClaimDelay: () => unilateralClaimDelay,
142
134
  unilateralRefundDelay: () => unilateralRefundDelay,
143
135
  unilateralRefundWithoutReceiverDelay: () => unilateralRefundWithoutReceiverDelay,
@@ -151,9 +143,9 @@ __export(index_exports, {
151
143
  module.exports = __toCommonJS(index_exports);
152
144
 
153
145
  // src/offer.ts
154
- var import_base = require("@scure/base");
146
+ var import_base2 = require("@scure/base");
155
147
  var import_utils = require("@scure/btc-signer/utils.js");
156
- var import_sdk = require("@arkade-os/sdk");
148
+ var import_sdk2 = require("@arkade-os/sdk");
157
149
 
158
150
  // src/swap-want-asset.program.json
159
151
  var swap_want_asset_program_default = {
@@ -283,6 +275,9 @@ async function retireSettledOfferContracts(manager, swaps) {
283
275
  }
284
276
 
285
277
  // src/store.ts
278
+ var import_base = require("@scure/base");
279
+ var import_sha2 = require("@noble/hashes/sha2.js");
280
+ var import_sdk = require("@arkade-os/sdk");
286
281
  var BTC_ASSET_ID = "btc";
287
282
  var byNewest = (a, b) => b.createdAt - a.createdAt;
288
283
  var getAssetSwapsOrThrow = async (repository) => {
@@ -335,11 +330,68 @@ var updateAssetSwapBestEffort = async (repository, id, changes) => {
335
330
  return { swaps, persisted: false };
336
331
  }
337
332
  };
333
+ var swapSecretsToRecord = (secrets) => ({
334
+ signingDescriptor: secrets.descriptor,
335
+ ..."mustPersistPreimage" in secrets && secrets.mustPersistPreimage ? { preimageHex: import_base.hex.encode(secrets.preimage) } : {},
336
+ ..."preimageSalt" in secrets && secrets.preimageSalt ? { preimageSaltHex: import_base.hex.encode(secrets.preimageSalt) } : {}
337
+ });
338
+ var decodeHex32 = (value, field) => {
339
+ const bytes = import_base.hex.decode(value);
340
+ if (bytes.length !== 32) {
341
+ throw new Error(`${field} must be 32 bytes, got ${bytes.length}`);
342
+ }
343
+ return bytes;
344
+ };
345
+ var PreimageNotRecoverableError = class extends Error {
346
+ constructor(reason, message, options) {
347
+ super(message, options);
348
+ this.reason = reason;
349
+ }
350
+ reason;
351
+ name = "PreimageNotRecoverableError";
352
+ };
353
+ var preimageForSwapRecord = async (wallet, record) => {
354
+ if (!record.signingDescriptor) {
355
+ throw new PreimageNotRecoverableError(
356
+ "no-secrets",
357
+ "this swap record carries no signing descriptor"
358
+ );
359
+ }
360
+ let stored;
361
+ let salt;
362
+ try {
363
+ stored = record.preimageHex ? decodeHex32(record.preimageHex, "preimageHex") : void 0;
364
+ salt = record.preimageSaltHex ? decodeHex32(record.preimageSaltHex, "preimageSaltHex") : void 0;
365
+ } catch (cause) {
366
+ throw new PreimageNotRecoverableError(
367
+ "malformed-record",
368
+ `this swap record's secrets projection is unreadable: ${String(cause)}`,
369
+ { cause }
370
+ );
371
+ }
372
+ let preimage;
373
+ try {
374
+ preimage = await (0, import_sdk.contractPreimage)(wallet, record.signingDescriptor, { stored, salt });
375
+ } catch (cause) {
376
+ throw new PreimageNotRecoverableError(
377
+ "not-derivable",
378
+ `this wallet cannot produce the preimage for ${record.signingDescriptor}`,
379
+ { cause }
380
+ );
381
+ }
382
+ if (record.paymentHash && import_base.hex.encode((0, import_sha2.sha256)(preimage)) !== record.paymentHash.toLowerCase()) {
383
+ throw new PreimageNotRecoverableError(
384
+ "hash-mismatch",
385
+ "the derived preimage does not match this swap's payment hash: wrong wallet, or a tampered salt"
386
+ );
387
+ }
388
+ return preimage;
389
+ };
338
390
 
339
391
  // src/offer.ts
340
392
  var swapPrograms = {
341
- wantAsset: import_sdk.arkade.parseArtifact(swap_want_asset_program_default),
342
- wantBtc: import_sdk.arkade.parseArtifact(swap_want_btc_program_default)
393
+ wantAsset: import_sdk2.arkade.parseArtifact(swap_want_asset_program_default),
394
+ wantBtc: import_sdk2.arkade.parseArtifact(swap_want_btc_program_default)
343
395
  };
344
396
  function swapProgramBinding(offer, serverPubkey) {
345
397
  if (offer.makerPkScript.length !== FIELDS.makerPkScript.width) {
@@ -367,7 +419,7 @@ function swapProgramBinding(offer, serverPubkey) {
367
419
  }
368
420
  function offerVtxoScript(offer, serverPubkey) {
369
421
  const { program, args, keys } = swapProgramBinding(offer, serverPubkey);
370
- return new import_sdk.arkade.ArkadeProgramScript(program, args, keys);
422
+ return new import_sdk2.arkade.ArkadeProgramScript(program, args, keys);
371
423
  }
372
424
  var OFFER_PACKET_TYPE = 3;
373
425
  var FIELDS = {
@@ -449,8 +501,8 @@ function decodeOffer(data) {
449
501
  return {
450
502
  swapPkScript: need("swapPkScript"),
451
503
  wantAmount: new DataView(amount.buffer, amount.byteOffset).getBigUint64(0, false),
452
- ...fields.wantAsset && { wantAsset: import_sdk.asset.AssetId.fromBytes(fields.wantAsset) },
453
- ...fields.offerAsset && { offerAsset: import_sdk.asset.AssetId.fromBytes(fields.offerAsset) },
504
+ ...fields.wantAsset && { wantAsset: import_sdk2.asset.AssetId.fromBytes(fields.wantAsset) },
505
+ ...fields.offerAsset && { offerAsset: import_sdk2.asset.AssetId.fromBytes(fields.offerAsset) },
454
506
  makerPkScript: need("makerPkScript"),
455
507
  makerPublicKey: need("makerPublicKey"),
456
508
  emulatorPubkey: need("emulatorPubkey")
@@ -461,45 +513,45 @@ var OFFER_CONTRACT_KIND = "asset-swap-offer";
461
513
  async function registerOfferContract(wallet, arkServerUrl, network, binding, serverPubkey, expectedPkScript) {
462
514
  const { program, args, keys } = swapProgramBinding(binding, serverPubkey);
463
515
  const contractManager = await wallet.getContractManager();
464
- const client = await import_sdk.arkade.Arkade.connect({
465
- arkade: new import_sdk.RestArkProvider(arkServerUrl),
466
- indexer: new import_sdk.RestIndexerProvider(arkServerUrl),
516
+ const client = await import_sdk2.arkade.Arkade.connect({
517
+ arkade: new import_sdk2.RestArkProvider(arkServerUrl),
518
+ indexer: new import_sdk2.RestIndexerProvider(arkServerUrl),
467
519
  identity: wallet.identity,
468
520
  // without this the row's `address` would be derived against the SDK's
469
521
  // default network while its script is right — a row that disagrees with
470
522
  // the address the user is about to fund
471
- network: (0, import_sdk.getNetwork)(network),
523
+ network: (0, import_sdk2.getNetwork)(network),
472
524
  contractManager
473
525
  });
474
- const contract = new import_sdk.arkade.ArkadeContract(client, program, args, keys);
475
- if (import_base.hex.encode(contract.pkScript) !== import_base.hex.encode(expectedPkScript)) {
526
+ const contract = new import_sdk2.arkade.ArkadeContract(client, program, args, keys);
527
+ if (import_base2.hex.encode(contract.pkScript) !== import_base2.hex.encode(expectedPkScript)) {
476
528
  throw new Error("derived covenant does not match the offer's swapPkScript");
477
529
  }
478
530
  await contract.register({
479
531
  label: OFFER_CONTRACT_LABEL,
480
532
  metadata: { genericallySpendable: false, kind: OFFER_CONTRACT_KIND }
481
533
  });
482
- await promoteOfferContract(contractManager, import_base.hex.encode(expectedPkScript));
534
+ await promoteOfferContract(contractManager, import_base2.hex.encode(expectedPkScript));
483
535
  }
484
536
  async function createOffer(wallet, arkServerUrl, params) {
485
537
  if (Boolean(params.wantAsset) === Boolean(params.offerAsset)) {
486
538
  throw new Error("set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC)");
487
539
  }
488
540
  const [info, makerAddress, makerPublicKey] = await Promise.all([
489
- new import_sdk.RestArkProvider(arkServerUrl).getInfo(),
541
+ new import_sdk2.RestArkProvider(arkServerUrl).getInfo(),
490
542
  wallet.getAddress(),
491
543
  wallet.identity.xOnlyPublicKey()
492
544
  ]);
493
- const serverPubKey = import_base.hex.decode((0, import_sdk.toXOnlySignerHex)(info.signerPubkey));
494
- const network = (0, import_sdk.getNetwork)(info.network);
495
- const emuKey = import_base.hex.decode(
496
- (0, import_sdk.toXOnlySignerHex)((0, import_sdk.resolveEmulatorPubkey)(network, params.emulatorPubkey))
545
+ const serverPubKey = import_base2.hex.decode((0, import_sdk2.toXOnlySignerHex)(info.signerPubkey));
546
+ const network = (0, import_sdk2.getNetwork)(info.network);
547
+ const emuKey = import_base2.hex.decode(
548
+ (0, import_sdk2.toXOnlySignerHex)((0, import_sdk2.resolveEmulatorPubkey)(network, params.emulatorPubkey))
497
549
  );
498
550
  const binding = {
499
551
  wantAmount: params.wantAmount,
500
552
  wantAsset: params.wantAsset,
501
553
  offerAsset: params.offerAsset,
502
- makerPkScript: import_sdk.ArkAddress.decode(makerAddress).pkScript,
554
+ makerPkScript: import_sdk2.ArkAddress.decode(makerAddress).pkScript,
503
555
  makerPublicKey,
504
556
  emulatorPubkey: emuKey
505
557
  };
@@ -515,7 +567,7 @@ async function createOffer(wallet, arkServerUrl, params) {
515
567
  );
516
568
  const payload = encodeOffer(offer);
517
569
  return {
518
- offerHex: import_base.hex.encode(payload),
570
+ offerHex: import_base2.hex.encode(payload),
519
571
  extension: { type: OFFER_PACKET_TYPE, payload },
520
572
  // VtxoScript.address owns address construction; assembling an ArkAddress
521
573
  // from tweakedPublicKey here would silently miss any future step it gains
@@ -525,11 +577,11 @@ async function createOffer(wallet, arkServerUrl, params) {
525
577
  }
526
578
  async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
527
579
  const { repository, fundingTxid, swapAddress } = opts;
528
- const offer = decodeOffer(import_base.hex.decode(offerHex));
580
+ const offer = decodeOffer(import_base2.hex.decode(offerHex));
529
581
  const contractManager = await wallet.getContractManager();
530
- const client = await import_sdk.arkade.Arkade.connect({
531
- arkade: new import_sdk.RestArkProvider(arkServerUrl),
532
- indexer: new import_sdk.RestIndexerProvider(arkServerUrl),
582
+ const client = await import_sdk2.arkade.Arkade.connect({
583
+ arkade: new import_sdk2.RestArkProvider(arkServerUrl),
584
+ indexer: new import_sdk2.RestIndexerProvider(arkServerUrl),
533
585
  identity: wallet.identity,
534
586
  // registered offers resolve their VTXOs from the contract repository
535
587
  // instead of a direct indexer query; the indexer above stays as the
@@ -539,15 +591,15 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
539
591
  // script and the payout script comes from wallet.getAddress(), so the
540
592
  // client's network (which only shapes address derivation) is unused here
541
593
  });
542
- const serverKey = swapAddress ? import_sdk.ArkAddress.decode(swapAddress).serverPubKey : client.serverKey;
594
+ const serverKey = swapAddress ? import_sdk2.ArkAddress.decode(swapAddress).serverPubKey : client.serverKey;
543
595
  const { program, args, keys } = swapProgramBinding(offer, serverKey);
544
- const rebuilt = new import_sdk.arkade.ArkadeProgramScript(program, args, keys);
545
- if (import_base.hex.encode(rebuilt.pkScript) !== import_base.hex.encode(offer.swapPkScript)) {
596
+ const rebuilt = new import_sdk2.arkade.ArkadeProgramScript(program, args, keys);
597
+ if (import_base2.hex.encode(rebuilt.pkScript) !== import_base2.hex.encode(offer.swapPkScript)) {
546
598
  throw new Error(
547
599
  "rebuilt covenant does not match the offer's swapPkScript \u2014 the server signing key has likely rotated since funding; pass swapAddress (the funded address) to pin the original key"
548
600
  );
549
601
  }
550
- const contract = new import_sdk.arkade.ArkadeContract(client, program, args, keys);
602
+ const contract = new import_sdk2.arkade.ArkadeContract(client, program, args, keys);
551
603
  const [vtxos, makerAddress] = await Promise.all([contract.getUtxos(), wallet.getAddress()]);
552
604
  if (!fundingTxid && vtxos.length > 1) {
553
605
  throw new Error(
@@ -556,7 +608,7 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
556
608
  }
557
609
  const vtxo = fundingTxid ? vtxos.find((v) => v.txid === fundingTxid) : vtxos[0];
558
610
  if (!vtxo) throw new Error("no spendable VTXO at the swap address");
559
- const makerPkScript = import_sdk.ArkAddress.decode(makerAddress).pkScript;
611
+ const makerPkScript = import_sdk2.ArkAddress.decode(makerAddress).pkScript;
560
612
  const cancel = contract.functions.cancel().from({ txid: vtxo.txid, vout: vtxo.vout, value: vtxo.value }).to(makerPkScript, BigInt(vtxo.value));
561
613
  for (const a of vtxo.assets ?? []) {
562
614
  cancel.withAsset({
@@ -575,7 +627,7 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
575
627
  spentTxid: txid
576
628
  });
577
629
  if (persisted) {
578
- await retireOfferContract(contractManager, swaps, import_base.hex.encode(offer.swapPkScript));
630
+ await retireOfferContract(contractManager, swaps, import_base2.hex.encode(offer.swapPkScript));
579
631
  }
580
632
  }
581
633
  return txid;
@@ -583,7 +635,7 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
583
635
 
584
636
  // src/markets.ts
585
637
  var import_solver_discovery = require("@arkade-os/solver-discovery");
586
- var import_sdk2 = require("@arkade-os/sdk");
638
+ var import_sdk3 = require("@arkade-os/sdk");
587
639
  var QUOTE_OPTIONS = { safetyBps: 0 };
588
640
  var makeCachedFeedFetch = (ttlMs = 3e4, fetchImpl = fetch) => {
589
641
  const cache = /* @__PURE__ */ new Map();
@@ -700,7 +752,7 @@ var validatePlan = (plan, giveBalance, dust) => {
700
752
  const receiveIsBtc = plan.receive.asset.id === BTC_ASSET_ID;
701
753
  if (depositIsBtc || receiveIsBtc) {
702
754
  const btcSide = depositIsBtc ? plan.deposit.atomic : plan.receive.atomic;
703
- if ((0, import_sdk2.isSubdust)(btcSide, dust)) return "below-dust";
755
+ if ((0, import_sdk3.isSubdust)(btcSide, dust)) return "below-dust";
704
756
  }
705
757
  return void 0;
706
758
  };
@@ -708,7 +760,7 @@ var validatePlan = (plan, giveBalance, dust) => {
708
760
  // src/repository.ts
709
761
  var marketsCacheKey = (network, registry) => `arkade-intents-markets-${network}-${registry}`;
710
762
  var InMemoryAssetSwapRepository = class {
711
- version = 1;
763
+ version = 2;
712
764
  swaps = /* @__PURE__ */ new Map();
713
765
  scanned = /* @__PURE__ */ new Set();
714
766
  markets = /* @__PURE__ */ new Map();
@@ -740,7 +792,7 @@ var InMemoryAssetSwapRepository = class {
740
792
  };
741
793
 
742
794
  // src/indexedDbRepository.ts
743
- var import_sdk3 = require("@arkade-os/sdk");
795
+ var import_sdk4 = require("@arkade-os/sdk");
744
796
  var DEFAULT_DB_NAME = "arkade-intents";
745
797
  var DB_VERSION = 1;
746
798
  var STORE_SWAPS = "swaps";
@@ -770,7 +822,7 @@ var IndexedDbAssetSwapRepository = class {
770
822
  this.dbName = dbName;
771
823
  }
772
824
  dbName;
773
- version = 1;
825
+ version = 2;
774
826
  // the promise, not the resolved database: openDatabase bumps a refcount on
775
827
  // every call including cache hits, while dispose closes once, so two
776
828
  // concurrent first calls would strand the refcount above zero and leak the
@@ -778,7 +830,7 @@ var IndexedDbAssetSwapRepository = class {
778
830
  // can be retried rather than cached forever.
779
831
  dbPromise = null;
780
832
  ensureDb() {
781
- return this.dbPromise ??= (0, import_sdk3.openDatabase)(this.dbName, DB_VERSION, initDatabase).catch(
833
+ return this.dbPromise ??= (0, import_sdk4.openDatabase)(this.dbName, DB_VERSION, initDatabase).catch(
782
834
  (err) => {
783
835
  this.dbPromise = null;
784
836
  throw err;
@@ -835,14 +887,14 @@ var IndexedDbAssetSwapRepository = class {
835
887
  }
836
888
  async [Symbol.asyncDispose]() {
837
889
  if (!this.dbPromise) return;
838
- await (0, import_sdk3.closeDatabase)(this.dbName);
890
+ await (0, import_sdk4.closeDatabase)(this.dbName);
839
891
  this.dbPromise = null;
840
892
  }
841
893
  };
842
894
 
843
895
  // src/restore.ts
844
- var import_base2 = require("@scure/base");
845
- var import_sdk4 = require("@arkade-os/sdk");
896
+ var import_base3 = require("@scure/base");
897
+ var import_sdk5 = require("@arkade-os/sdk");
846
898
  var TXS_PER_REQUEST = 50;
847
899
  async function fetchParsedTxs(indexer, txids) {
848
900
  const parsedByTxid = /* @__PURE__ */ new Map();
@@ -858,7 +910,7 @@ async function fetchParsedTxs(indexer, txids) {
858
910
  if (result.status !== "fulfilled") continue;
859
911
  for (const psbt of result.value) {
860
912
  try {
861
- const parsed = import_sdk4.Transaction.fromPSBT(import_base2.base64.decode(psbt));
913
+ const parsed = import_sdk5.Transaction.fromPSBT(import_base3.base64.decode(psbt));
862
914
  parsedByTxid.set(parsed.id, parsed);
863
915
  } catch {
864
916
  }
@@ -873,7 +925,7 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
873
925
  let leaves;
874
926
  try {
875
927
  const script = offerVtxoScript(offer, serverPubkey);
876
- if (import_base2.hex.encode(script.pkScript) !== import_base2.hex.encode(offer.swapPkScript)) return "indeterminate";
928
+ if (import_base3.hex.encode(script.pkScript) !== import_base3.hex.encode(offer.swapPkScript)) return "indeterminate";
877
929
  leaves = {
878
930
  cancel: script.functionByName("cancel")?.leafScript,
879
931
  fulfill: script.functionByName("fulfill")?.leafScript
@@ -884,11 +936,11 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
884
936
  for (let i = 0; i < spendTx.inputsLength; i++) {
885
937
  const input = spendTx.getInput(i);
886
938
  if (!input.txid || input.index !== deposit.vout) continue;
887
- if (import_base2.hex.encode(input.txid) !== deposit.txid) continue;
939
+ if (import_base3.hex.encode(input.txid) !== deposit.txid) continue;
888
940
  for (const leaf of input.tapLeafScript ?? []) {
889
- const spent = import_base2.hex.encode((0, import_sdk4.scriptFromTapLeafScript)(leaf));
890
- if (leaves.cancel && spent === import_base2.hex.encode(leaves.cancel)) return "cancelled";
891
- if (leaves.fulfill && spent === import_base2.hex.encode(leaves.fulfill)) return "fulfilled";
941
+ const spent = import_base3.hex.encode((0, import_sdk5.scriptFromTapLeafScript)(leaf));
942
+ if (leaves.cancel && spent === import_base3.hex.encode(leaves.cancel)) return "cancelled";
943
+ if (leaves.fulfill && spent === import_base3.hex.encode(leaves.fulfill)) return "fulfilled";
892
944
  }
893
945
  }
894
946
  return "indeterminate";
@@ -917,19 +969,19 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
917
969
  if (!fundingTx) continue;
918
970
  fetchedTxids.push(txid);
919
971
  try {
920
- const packet = import_sdk4.Extension.fromTx(parsed).getPacketByType(OFFER_PACKET_TYPE);
972
+ const packet = import_sdk5.Extension.fromTx(parsed).getPacketByType(OFFER_PACKET_TYPE);
921
973
  if (!packet) continue;
922
974
  const payload = packet.serialize();
923
975
  found.push({
924
976
  fundingTx,
925
977
  offer: decodeOffer(payload),
926
- offerHex: import_base2.hex.encode(payload)
978
+ offerHex: import_base3.hex.encode(payload)
927
979
  });
928
980
  } catch {
929
981
  }
930
982
  }
931
983
  if (found.length === 0) return { restored: [], scannedTxids: fetchedTxids };
932
- const scripts = [...new Set(found.map((f) => import_base2.hex.encode(f.offer.swapPkScript)))];
984
+ const scripts = [...new Set(found.map((f) => import_base3.hex.encode(f.offer.swapPkScript)))];
933
985
  const { vtxos } = await indexer.getVtxos({ scripts });
934
986
  const vtxoByScriptAndTxid = new Map(vtxos.map((v) => [`${v.script}:${v.txid}`, v]));
935
987
  const txByAnyId = /* @__PURE__ */ new Map();
@@ -941,7 +993,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
941
993
  const spendTxids = /* @__PURE__ */ new Set();
942
994
  for (const { fundingTx, offer } of found) {
943
995
  const vtxo = vtxoByScriptAndTxid.get(
944
- `${import_base2.hex.encode(offer.swapPkScript)}:${fundingTx.redeemTxid}`
996
+ `${import_base3.hex.encode(offer.swapPkScript)}:${fundingTx.redeemTxid}`
945
997
  );
946
998
  if (vtxo?.virtualStatus.state !== "spent") continue;
947
999
  for (const txid of spendTxidsOf(vtxo)) spendTxids.add(txid);
@@ -950,7 +1002,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
950
1002
  const restored = [];
951
1003
  const unresolved = /* @__PURE__ */ new Set();
952
1004
  for (const { fundingTx, offer, offerHex } of found) {
953
- const swapPkScript = import_base2.hex.encode(offer.swapPkScript);
1005
+ const swapPkScript = import_base3.hex.encode(offer.swapPkScript);
954
1006
  const vtxo = vtxoByScriptAndTxid.get(`${swapPkScript}:${fundingTx.redeemTxid}`);
955
1007
  if (!vtxo) {
956
1008
  unresolved.add(fundingTx.redeemTxid);
@@ -1007,8 +1059,8 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
1007
1059
  }
1008
1060
 
1009
1061
  // src/watch.ts
1010
- var import_base3 = require("@scure/base");
1011
- var import_sdk5 = require("@arkade-os/sdk");
1062
+ var import_base4 = require("@scure/base");
1063
+ var import_sdk6 = require("@arkade-os/sdk");
1012
1064
  var TERMINAL = ["fulfilled", "cancelled", "recoverable"];
1013
1065
  function spendUpdate(swap, spend) {
1014
1066
  if (TERMINAL.includes(swap.status)) return void 0;
@@ -1028,8 +1080,8 @@ async function watchOfferSwaps({
1028
1080
  onUpdate
1029
1081
  }) {
1030
1082
  const manager = await wallet.getContractManager();
1031
- const serverPubkey = import_sdk5.ArkAddress.decode(await wallet.getAddress()).serverPubKey;
1032
- const indexer = new import_sdk5.RestIndexerProvider(arkServerUrl);
1083
+ const serverPubkey = import_sdk6.ArkAddress.decode(await wallet.getAddress()).serverPubKey;
1084
+ const indexer = new import_sdk6.RestIndexerProvider(arkServerUrl);
1033
1085
  let queue = Promise.resolve();
1034
1086
  const enqueue = (task) => {
1035
1087
  queue = queue.then(task).catch(() => {
@@ -1042,9 +1094,9 @@ async function watchOfferSwaps({
1042
1094
  if (candidates.length === 0) return "indeterminate";
1043
1095
  const { txs } = await indexer.getVirtualTxs(candidates);
1044
1096
  return classifyDepositSpend(
1045
- decodeOffer(import_base3.hex.decode(swap.offerHex)),
1097
+ decodeOffer(import_base4.hex.decode(swap.offerHex)),
1046
1098
  serverPubkey,
1047
- txs.map((psbt) => import_sdk5.Transaction.fromPSBT(import_base3.base64.decode(psbt))),
1099
+ txs.map((psbt) => import_sdk6.Transaction.fromPSBT(import_base4.base64.decode(psbt))),
1048
1100
  { txid: vtxo.txid, vout: vtxo.vout }
1049
1101
  );
1050
1102
  } catch {
@@ -1091,8 +1143,8 @@ var import_legacy2 = require("@noble/hashes/legacy.js");
1091
1143
  var import_sdk8 = require("@arkade-os/sdk");
1092
1144
 
1093
1145
  // src/onchainHtlc.ts
1094
- var import_base4 = require("@scure/base");
1095
- var import_sha2 = require("@noble/hashes/sha2.js");
1146
+ var import_base5 = require("@scure/base");
1147
+ var import_sha22 = require("@noble/hashes/sha2.js");
1096
1148
  var import_legacy = require("@noble/hashes/legacy.js");
1097
1149
  var btc = __toESM(require("@scure/btc-signer"), 1);
1098
1150
  var ONCHAIN_ORDER_MARGIN_SECONDS = 2 * 60 * 60;
@@ -1101,8 +1153,8 @@ var MAX_MIN_CONFIRMATIONS = 6;
1101
1153
  var ONCHAIN_SECONDS_PER_BLOCK = 600;
1102
1154
  var ONCHAIN_DUST_SATS = BigInt(330);
1103
1155
  var newPreimage = () => crypto.getRandomValues(new Uint8Array(32));
1104
- var paymentHashOf = (preimage) => import_base4.hex.encode((0, import_sha2.sha256)(preimage));
1105
- var h160FromPaymentHash = (paymentHash) => (0, import_legacy.ripemd160)(import_base4.hex.decode(paymentHash));
1156
+ var paymentHashOf = (preimage) => import_base5.hex.encode((0, import_sha22.sha256)(preimage));
1157
+ var h160FromPaymentHash = (paymentHash) => (0, import_legacy.ripemd160)(import_base5.hex.decode(paymentHash));
1106
1158
  var L1_NETWORKS = {
1107
1159
  bitcoin: btc.NETWORK,
1108
1160
  testnet: btc.TEST_NETWORK,
@@ -1143,7 +1195,7 @@ function onchainHtlcScript(params, network) {
1143
1195
  );
1144
1196
  const controlBlockFor = (leaf) => {
1145
1197
  for (const [block, script] of payment.tapLeafScript ?? []) {
1146
- if (script.length - 1 === leaf.length && import_base4.hex.encode(script.subarray(0, leaf.length)) === import_base4.hex.encode(leaf)) {
1198
+ if (script.length - 1 === leaf.length && import_base5.hex.encode(script.subarray(0, leaf.length)) === import_base5.hex.encode(leaf)) {
1147
1199
  return btc.TaprootControlBlock.encode(block);
1148
1200
  }
1149
1201
  }
@@ -1236,13 +1288,13 @@ var buildHtlcRefund = (input) => buildLeafSpend({
1236
1288
  function extractPreimage(txHex, paymentHash) {
1237
1289
  let raw;
1238
1290
  try {
1239
- raw = btc.RawTx.decode(import_base4.hex.decode(txHex));
1291
+ raw = btc.RawTx.decode(import_base5.hex.decode(txHex));
1240
1292
  } catch {
1241
1293
  return null;
1242
1294
  }
1243
1295
  for (const stack of raw.witnesses ?? []) {
1244
1296
  for (const item of stack) {
1245
- if (item.length === 32 && import_base4.hex.encode((0, import_sha2.sha256)(item)) === paymentHash) return item;
1297
+ if (item.length === 32 && import_base5.hex.encode((0, import_sha22.sha256)(item)) === paymentHash) return item;
1246
1298
  }
1247
1299
  }
1248
1300
  return null;
@@ -1283,7 +1335,7 @@ async function classifyOnchainHtlc(chain, input) {
1283
1335
  const spend = await chain.getSpendingTx(input.funding.txid, input.funding.vout);
1284
1336
  if (!spend) return { phase: "unfunded" };
1285
1337
  const preimage = extractPreimage(spend.txHex, input.htlc.paymentHash);
1286
- const txid = btc.Transaction.fromRaw(import_base4.hex.decode(spend.txHex), {
1338
+ const txid = btc.Transaction.fromRaw(import_base5.hex.decode(spend.txHex), {
1287
1339
  allowUnknownInputs: true,
1288
1340
  allowUnknownOutputs: true
1289
1341
  }).id;
@@ -1296,177 +1348,19 @@ async function classifyOnchainHtlc(chain, input) {
1296
1348
  return { phase: "claimable", utxo: best };
1297
1349
  }
1298
1350
 
1299
- // src/secrets.ts
1300
- var import_base5 = require("@scure/base");
1301
- var import_sha22 = require("@noble/hashes/sha2.js");
1302
- var import_utils2 = require("@noble/hashes/utils.js");
1303
- var import_secp256k1 = require("@noble/curves/secp256k1.js");
1304
- var import_sdk6 = require("@arkade-os/sdk");
1305
- var RFQ_PREIMAGE_TAG = "Arkade-RFQ-Preimage-v1";
1306
- function buildPreimageMessage(xonly, index) {
1307
- if (xonly.length !== 32) {
1308
- throw new Error(`x-only pubkey must be 32 bytes, got ${xonly.length}`);
1309
- }
1310
- if (!Number.isInteger(index) || index < 0 || index > 4294967295) {
1311
- throw new Error(`index must be a u32, got ${index}`);
1312
- }
1313
- const tag = new TextEncoder().encode(RFQ_PREIMAGE_TAG);
1314
- const message = new Uint8Array(tag.length + 32 + 4);
1315
- message.set(tag, 0);
1316
- message.set(xonly, tag.length);
1317
- new DataView(message.buffer).setUint32(tag.length + 32, index, true);
1318
- return message;
1319
- }
1320
- var PREIMAGE_INDEX = 0;
1321
- async function deriveSwapSecrets(wallet) {
1322
- if (!(0, import_sdk6.isHDAllocationCapable)(wallet)) return void 0;
1323
- const signingDescriptor = await wallet.getNextSigningDescriptor();
1324
- if (!signingDescriptor) return void 0;
1325
- return { derivable: true, signingDescriptor };
1326
- }
1327
- function randomSwapSecrets(opts = {}) {
1328
- if (opts.preimage instanceof Uint8Array && opts.preimage.length !== 32) {
1329
- throw new Error(`preimage must be 32 bytes, got ${opts.preimage.length}`);
1330
- }
1331
- const preimage = opts.preimage instanceof Uint8Array ? opts.preimage : opts.preimage ? (0, import_utils2.randomBytes)(32) : void 0;
1332
- return {
1333
- derivable: false,
1334
- senderPrivateKey: import_secp256k1.schnorr.utils.randomSecretKey(),
1335
- ...preimage ? { preimage } : {}
1336
- };
1337
- }
1338
- function rfqSecretsToRecord(secrets) {
1339
- if (secrets.derivable) {
1340
- return {
1341
- signingDescriptor: secrets.signingDescriptor,
1342
- ...secrets.preimage ? { preimageHex: import_base5.hex.encode(secrets.preimage) } : {}
1343
- };
1344
- }
1345
- return {
1346
- fallbackSecrets: {
1347
- version: 1,
1348
- type: "stored",
1349
- senderPrivateKeyHex: import_base5.hex.encode(secrets.senderPrivateKey),
1350
- ...secrets.preimage ? { preimageHex: import_base5.hex.encode(secrets.preimage) } : {}
1351
- }
1352
- };
1353
- }
1354
- function rfqSecretsOfRecord(record) {
1355
- if (record.signingDescriptor) {
1356
- return {
1357
- derivable: true,
1358
- signingDescriptor: record.signingDescriptor,
1359
- ...record.preimageHex ? { preimage: decodeHex32(record.preimageHex, "preimageHex") } : {}
1360
- };
1361
- }
1362
- const fallback = record.fallbackSecrets;
1363
- if (!fallback) return void 0;
1364
- if (fallback.version !== 1 || fallback.type !== "stored") {
1365
- throw new Error("unsupported RFQ fallback secrets record");
1366
- }
1367
- return {
1368
- derivable: false,
1369
- senderPrivateKey: decodeHex32(fallback.senderPrivateKeyHex, "senderPrivateKeyHex"),
1370
- ...fallback.preimageHex ? { preimage: decodeHex32(fallback.preimageHex, "preimageHex") } : {}
1371
- };
1372
- }
1373
- function decodeHex32(value, label) {
1374
- const bytes = import_base5.hex.decode(value);
1375
- if (bytes.length !== 32) {
1376
- throw new Error(label + " must be 32 bytes, got " + bytes.length);
1377
- }
1378
- return bytes;
1379
- }
1380
- async function adoptSwapDescriptor(wallet, signingDescriptor) {
1381
- if (!(0, import_sdk6.isHDAllocationCapable)(wallet)) return;
1382
- await wallet.advanceSigningDescriptorWatermark(signingDescriptor);
1383
- }
1384
- var RefundNotLocallyPossibleError = class extends Error {
1385
- constructor(reason, message, options) {
1386
- super(message, options);
1387
- this.reason = reason;
1388
- }
1389
- reason;
1390
- name = "RefundNotLocallyPossibleError";
1391
- };
1392
- async function senderIdentityForRfqSecrets(wallet, secrets) {
1393
- if (!secrets.derivable) return import_sdk6.SingleKey.fromPrivateKey(secrets.senderPrivateKey);
1394
- const signer = (0, import_sdk6.isHDWalletCapable)(wallet) ? await wallet.signerForDescriptor(secrets.signingDescriptor) : void 0;
1395
- if (!signer || !isDeterministicSigner(signer)) {
1396
- throw new RefundNotLocallyPossibleError(
1397
- "foreign-descriptor",
1398
- `this wallet cannot derive ${secrets.signingDescriptor}; the swap was created on another wallet`
1399
- );
1400
- }
1401
- return signer;
1402
- }
1403
- async function senderIdentityForSwapRecord(wallet, record) {
1404
- let secrets;
1405
- try {
1406
- secrets = rfqSecretsOfRecord(record);
1407
- } catch (error) {
1408
- throw new RefundNotLocallyPossibleError(
1409
- "unreadable-secrets",
1410
- error instanceof Error ? error.message : String(error),
1411
- // the record's own diagnosis, kept for a caller that wants more
1412
- // than the message
1413
- { cause: error }
1414
- );
1415
- }
1416
- if (!secrets) {
1417
- throw new RefundNotLocallyPossibleError(
1418
- "no-secrets",
1419
- "this swap record carries no signing descriptor and no fallback secrets"
1420
- );
1421
- }
1422
- return senderIdentityForRfqSecrets(wallet, secrets);
1423
- }
1424
- async function senderPubkeyForRfqSecrets(wallet, secrets) {
1425
- if (!secrets.derivable) return import_secp256k1.schnorr.getPublicKey(secrets.senderPrivateKey);
1426
- return (await senderIdentityForRfqSecrets(wallet, secrets)).xOnlyPublicKey();
1427
- }
1428
- function isDeterministicSigner(value) {
1429
- if (typeof value !== "object" || value === null) return false;
1430
- const v = value;
1431
- return typeof v.signSchnorrDeterministic === "function" && typeof v.xOnlyPublicKey === "function";
1432
- }
1433
- async function derivePreimage(signer) {
1434
- const xonly = await signer.xOnlyPublicKey();
1435
- const message = buildPreimageMessage(xonly, PREIMAGE_INDEX);
1436
- return (0, import_sha22.sha256)(await signer.signSchnorrDeterministic((0, import_sha22.sha256)(message)));
1437
- }
1438
- async function preimageForRfqSecrets(wallet, secrets) {
1439
- if (!secrets.derivable) {
1440
- if (!secrets.preimage) throw new Error("this swap carries no stored preimage");
1441
- return secrets.preimage;
1442
- }
1443
- if (secrets.preimage) return secrets.preimage;
1444
- const signer = await senderIdentityForRfqSecrets(wallet, secrets);
1445
- if (!isDeterministicSigner(signer)) {
1446
- throw new Error(
1447
- `wallet cannot sign deterministically for ${secrets.signingDescriptor}; its preimage is not derivable`
1448
- );
1449
- }
1450
- try {
1451
- return await derivePreimage(signer);
1452
- } catch (cause) {
1453
- throw new Error(
1454
- `wallet cannot sign deterministically for ${secrets.signingDescriptor}; its preimage is not derivable`,
1455
- { cause }
1456
- );
1457
- }
1458
- }
1351
+ // src/rfq.ts
1352
+ var import_sdk9 = require("@arkade-os/sdk");
1459
1353
 
1460
1354
  // src/claimPacket.ts
1461
1355
  var import_base6 = require("@scure/base");
1462
- var import_secp256k12 = require("@noble/curves/secp256k1.js");
1356
+ var import_secp256k1 = require("@noble/curves/secp256k1.js");
1463
1357
  var import_hkdf = require("@noble/hashes/hkdf.js");
1464
1358
  var import_sha23 = require("@noble/hashes/sha2.js");
1465
1359
  var HKDF_INFO = new TextEncoder().encode("covclaimd/preimage/v1");
1466
1360
  async function sealClaimPacket(input) {
1467
1361
  return sealWithEntropy(
1468
1362
  input,
1469
- import_secp256k12.secp256k1.utils.randomSecretKey(),
1363
+ import_secp256k1.secp256k1.utils.randomSecretKey(),
1470
1364
  crypto.getRandomValues(new Uint8Array(12))
1471
1365
  );
1472
1366
  }
@@ -1475,8 +1369,8 @@ async function sealWithEntropy(input, ephemeralKey, nonce) {
1475
1369
  if (input.covclaimdPubkey.length !== 33) {
1476
1370
  throw new Error("covclaimd pubkey must be 33-byte compressed");
1477
1371
  }
1478
- const ephemeralPub = import_secp256k12.secp256k1.getPublicKey(ephemeralKey, true);
1479
- const sharedX = import_secp256k12.secp256k1.getSharedSecret(ephemeralKey, input.covclaimdPubkey, true).subarray(1);
1372
+ const ephemeralPub = import_secp256k1.secp256k1.getPublicKey(ephemeralKey, true);
1373
+ const sharedX = import_secp256k1.secp256k1.getSharedSecret(ephemeralKey, input.covclaimdPubkey, true).subarray(1);
1480
1374
  const key = (0, import_hkdf.hkdf)(import_sha23.sha256, sharedX, ephemeralPub, HKDF_INFO, 32);
1481
1375
  if (nonce.length !== 12) throw new Error("nonce must be 12 bytes");
1482
1376
  const aesKey = await crypto.subtle.importKey("raw", key, "AES-GCM", false, [
@@ -1786,21 +1680,22 @@ var relayTransport = (relayUrl, options) => {
1786
1680
  };
1787
1681
  };
1788
1682
  var SEQUENCE_GRANULARITY_SECONDS = 512;
1683
+ var SOLO_REFUND_HEADROOM_SECONDS = 8 * SEQUENCE_GRANULARITY_SECONDS;
1789
1684
  var unilateralClaimDelay = (serverExitDelaySeconds) => {
1790
1685
  if (!Number.isFinite(serverExitDelaySeconds) || serverExitDelaySeconds < SEQUENCE_GRANULARITY_SECONDS) {
1791
1686
  throw new Error(
1792
1687
  `server exit delay must be at least ${SEQUENCE_GRANULARITY_SECONDS}s of seconds, got ${serverExitDelaySeconds}`
1793
1688
  );
1794
1689
  }
1795
- if (serverExitDelaySeconds > (65535 - 2) * SEQUENCE_GRANULARITY_SECONDS) {
1690
+ if (serverExitDelaySeconds > 65535 * SEQUENCE_GRANULARITY_SECONDS - SOLO_REFUND_HEADROOM_SECONDS) {
1796
1691
  throw new Error(
1797
- `server exit delay ${serverExitDelaySeconds}s exceeds what BIP68 can encode once the two refund tiers are stacked above it`
1692
+ `server exit delay ${serverExitDelaySeconds}s exceeds what BIP68 can encode once the solo refund's headroom is stacked above it`
1798
1693
  );
1799
1694
  }
1800
1695
  return Math.ceil(serverExitDelaySeconds / SEQUENCE_GRANULARITY_SECONDS) * SEQUENCE_GRANULARITY_SECONDS;
1801
1696
  };
1802
- var unilateralRefundDelay = (claimDelay) => claimDelay + SEQUENCE_GRANULARITY_SECONDS;
1803
- var unilateralRefundWithoutReceiverDelay = (claimDelay) => claimDelay + 2 * SEQUENCE_GRANULARITY_SECONDS;
1697
+ var unilateralRefundDelay = (claimDelay) => claimDelay;
1698
+ var unilateralRefundWithoutReceiverDelay = (claimDelay) => claimDelay + SOLO_REFUND_HEADROOM_SECONDS;
1804
1699
  function lightningSendVtxoScript(params) {
1805
1700
  const seconds = (value) => ({
1806
1701
  type: "seconds",
@@ -1829,13 +1724,8 @@ function lightningSendVtxoScript(params) {
1829
1724
  }
1830
1725
  async function requestLightningSend(wallet, arkServerUrl, transport, params) {
1831
1726
  const rfqId = params.rfqId ?? newRfqId();
1832
- const secrets = await deriveSwapSecrets(wallet) ?? randomSwapSecrets();
1833
- if (!secrets.derivable) {
1834
- console.warn(
1835
- "[swap] wallet cannot allocate an HD descriptor: the sender key is random and MUST be persisted before funding"
1836
- );
1837
- }
1838
- const senderPubkey = await senderPubkeyForRfqSecrets(wallet, secrets);
1727
+ const secrets = await (0, import_sdk9.provisionRefundKey)(wallet);
1728
+ const senderPubkey = secrets.pubkey;
1839
1729
  const [info, refundAddress] = await Promise.all([
1840
1730
  new import_sdk8.RestArkProvider(arkServerUrl).getInfo(),
1841
1731
  wallet.getAddress()
@@ -1995,23 +1885,14 @@ function deriveOnchainSend(input) {
1995
1885
  }
1996
1886
  async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
1997
1887
  const rfqId = params.rfqId ?? newRfqId();
1998
- if (params.preimage && params.preimage.length !== 32) {
1999
- throw new Error(`preimage must be 32 bytes, got ${params.preimage.length}`);
2000
- }
2001
- const derivedSecrets = await deriveSwapSecrets(wallet);
2002
- const secrets = derivedSecrets ? params.preimage ? { ...derivedSecrets, preimage: params.preimage } : derivedSecrets : randomSwapSecrets({ preimage: params.preimage ?? true });
2003
- if (!secrets.derivable) {
1888
+ const secrets = await (0, import_sdk9.provisionClaimSecret)(wallet, { preimage: params.preimage });
1889
+ if (secrets.mustPersistPreimage) {
2004
1890
  console.warn(
2005
- params.preimage ? "[swap] this swap's sender key is random; the supplied preimage and sender key MUST be persisted before funding" : "[swap] this swap's preimage and sender key are random and MUST be persisted before funding"
2006
- );
2007
- } else if (params.preimage) {
2008
- console.warn(
2009
- "[swap] this swap's preimage was supplied by the caller and MUST be persisted with the signing descriptor before funding"
1891
+ "[swap] this swap's preimage cannot be re-derived from the seed and MUST be persisted with the record before funding"
2010
1892
  );
2011
1893
  }
2012
- const preimage = await preimageForRfqSecrets(wallet, secrets);
2013
- const paymentHash = paymentHashOf(preimage);
2014
- const senderPubkey = await senderPubkeyForRfqSecrets(wallet, secrets);
1894
+ const paymentHash = import_base8.hex.encode(secrets.paymentHash);
1895
+ const senderPubkey = secrets.pubkey;
2015
1896
  const [info, refundAddress] = await Promise.all([
2016
1897
  new import_sdk8.RestArkProvider(arkServerUrl).getInfo(),
2017
1898
  wallet.getAddress()
@@ -2189,15 +2070,15 @@ function deriveLightningReceive(input) {
2189
2070
  }
2190
2071
  async function requestLightningReceive(wallet, arkServerUrl, transport, params) {
2191
2072
  const rfqId = params.rfqId ?? newRfqId();
2192
- const secrets = await deriveSwapSecrets(wallet) ?? randomSwapSecrets({ preimage: true });
2193
- if (!secrets.derivable) {
2073
+ const secrets = await (0, import_sdk9.provisionClaimSecret)(wallet);
2074
+ if (secrets.mustPersistPreimage) {
2194
2075
  console.warn(
2195
- "[swap] this swap's preimage and payout key are random and MUST be persisted before paying"
2076
+ "[swap] this swap's preimage cannot be re-derived from the seed and MUST be persisted with the record before paying"
2196
2077
  );
2197
2078
  }
2198
- const preimage = await preimageForRfqSecrets(wallet, secrets);
2199
- const paymentHash = paymentHashOf(preimage);
2200
- const payoutPubkey = await senderPubkeyForRfqSecrets(wallet, secrets);
2079
+ const preimage = secrets.preimage;
2080
+ const paymentHash = import_base8.hex.encode(secrets.paymentHash);
2081
+ const payoutPubkey = secrets.pubkey;
2201
2082
  const [info, payoutAddress] = await Promise.all([
2202
2083
  new import_sdk8.RestArkProvider(arkServerUrl).getInfo(),
2203
2084
  wallet.getAddress()
@@ -2307,15 +2188,15 @@ function deriveOnchainReceive(input) {
2307
2188
  }
2308
2189
  async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
2309
2190
  const rfqId = params.rfqId ?? newRfqId();
2310
- const secrets = await deriveSwapSecrets(wallet) ?? randomSwapSecrets({ preimage: true });
2311
- if (!secrets.derivable) {
2191
+ const secrets = await (0, import_sdk9.provisionClaimSecret)(wallet);
2192
+ if (secrets.mustPersistPreimage) {
2312
2193
  console.warn(
2313
- "[swap] this swap's preimage and payout key are random and MUST be persisted before funding"
2194
+ "[swap] this swap's preimage cannot be re-derived from the seed and MUST be persisted with the record before funding"
2314
2195
  );
2315
2196
  }
2316
- const preimage = await preimageForRfqSecrets(wallet, secrets);
2317
- const paymentHash = paymentHashOf(preimage);
2318
- const payoutPubkey = await senderPubkeyForRfqSecrets(wallet, secrets);
2197
+ const preimage = secrets.preimage;
2198
+ const paymentHash = import_base8.hex.encode(secrets.paymentHash);
2199
+ const payoutPubkey = secrets.pubkey;
2319
2200
  const [info, payoutAddress] = await Promise.all([
2320
2201
  new import_sdk8.RestArkProvider(arkServerUrl).getInfo(),
2321
2202
  wallet.getAddress()
@@ -2386,12 +2267,12 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
2386
2267
  var import_base10 = require("@scure/base");
2387
2268
  var import_legacy3 = require("@noble/hashes/legacy.js");
2388
2269
  var import_sha25 = require("@noble/hashes/sha2.js");
2389
- var import_sdk10 = require("@arkade-os/sdk");
2270
+ var import_sdk11 = require("@arkade-os/sdk");
2390
2271
 
2391
2272
  // src/refund.ts
2392
2273
  var import_base9 = require("@scure/base");
2393
2274
  var import_sha24 = require("@noble/hashes/sha2.js");
2394
- var import_sdk9 = require("@arkade-os/sdk");
2275
+ var import_sdk10 = require("@arkade-os/sdk");
2395
2276
  var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2396
2277
  var isRfqTerminal = (state) => RFQ_TERMINAL_STATES.includes(state);
2397
2278
  var RFQ_RESOLVED_STATES = ["settled", "refunded"];
@@ -2465,7 +2346,7 @@ async function findLockupVtxos(indexer, swapPkScript) {
2465
2346
  }
2466
2347
  var hashesTo = (candidate, paymentHash) => import_base9.hex.encode((0, import_sha24.sha256)(candidate)) === paymentHash;
2467
2348
  var candidateWitnessItems = (tx, inputIndex) => [
2468
- ...(0, import_sdk9.getArkPsbtFields)(tx, inputIndex, import_sdk9.ConditionWitness).flat(),
2349
+ ...(0, import_sdk10.getArkPsbtFields)(tx, inputIndex, import_sdk10.ConditionWitness).flat(),
2469
2350
  ...tx.getInput(inputIndex).finalScriptWitness ?? []
2470
2351
  ];
2471
2352
  async function readLockupFate(indexer, input) {
@@ -2484,7 +2365,7 @@ async function readLockupFate(indexer, input) {
2484
2365
  for (const raw of txs) {
2485
2366
  let tx;
2486
2367
  try {
2487
- tx = import_sdk9.Transaction.fromPSBT(import_base9.base64.decode(raw));
2368
+ tx = import_sdk10.Transaction.fromPSBT(import_base9.base64.decode(raw));
2488
2369
  } catch {
2489
2370
  continue;
2490
2371
  }
@@ -2521,14 +2402,14 @@ async function pushRefundWithoutReceiver(ark, input) {
2521
2402
  const info = await ark.getInfo();
2522
2403
  let serverUnrollScript;
2523
2404
  try {
2524
- serverUnrollScript = import_sdk9.CSVMultisigTapscript.decode(import_base9.hex.decode(info.checkpointTapscript));
2405
+ serverUnrollScript = import_sdk10.CSVMultisigTapscript.decode(import_base9.hex.decode(info.checkpointTapscript));
2525
2406
  } catch {
2526
2407
  throw new Error("invalid checkpointTapscript from the Arkade server");
2527
2408
  }
2528
2409
  const leaf = input.script.refundWithoutReceiver();
2529
2410
  const tapTree = input.script.encode();
2530
2411
  const amount = input.vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
2531
- const { arkTx, checkpoints } = (0, import_sdk9.buildOffchainTx)(
2412
+ const { arkTx, checkpoints } = (0, import_sdk10.buildOffchainTx)(
2532
2413
  input.vtxos.map((vtxo) => ({
2533
2414
  txid: vtxo.txid,
2534
2415
  vout: vtxo.vout,
@@ -2544,8 +2425,8 @@ async function pushRefundWithoutReceiver(ark, input) {
2544
2425
  import_base9.base64.encode(signedArkTx.toPSBT()),
2545
2426
  checkpoints.map((c) => import_base9.base64.encode(c.toPSBT()))
2546
2427
  );
2547
- (0, import_sdk9.assertSubmittedArkTxid)(submitted, signedArkTx, "refundWithoutReceiver");
2548
- const matched = (0, import_sdk9.matchServerCheckpoints)(
2428
+ (0, import_sdk10.assertSubmittedArkTxid)(submitted, signedArkTx, "refundWithoutReceiver");
2429
+ const matched = (0, import_sdk10.matchServerCheckpoints)(
2549
2430
  submitted.signedCheckpointTxs,
2550
2431
  checkpoints,
2551
2432
  "refundWithoutReceiver"
@@ -2638,14 +2519,14 @@ async function pushClaim(ark, input) {
2638
2519
  const info = await ark.getInfo();
2639
2520
  let serverUnrollScript;
2640
2521
  try {
2641
- serverUnrollScript = import_sdk10.CSVMultisigTapscript.decode(import_base10.hex.decode(info.checkpointTapscript));
2522
+ serverUnrollScript = import_sdk11.CSVMultisigTapscript.decode(import_base10.hex.decode(info.checkpointTapscript));
2642
2523
  } catch {
2643
2524
  throw new Error("invalid checkpointTapscript from the Arkade server");
2644
2525
  }
2645
2526
  const leaf = input.script.claim();
2646
2527
  const tapTree = input.script.encode();
2647
- const arkTxid = await (0, import_sdk10.signAndSubmitOffchainTx)({
2648
- identity: (0, import_sdk10.claimWithPreimageIdentity)(input.receiver, input.preimage),
2528
+ const arkTxid = await (0, import_sdk11.signAndSubmitOffchainTx)({
2529
+ identity: (0, import_sdk11.claimWithPreimageIdentity)(input.receiver, input.preimage),
2649
2530
  provider: ark,
2650
2531
  inputs: input.vtxos.map((vtxo) => ({
2651
2532
  txid: vtxo.txid,
@@ -2691,6 +2572,44 @@ async function claimReceiveLockup(indexer, ark, input) {
2691
2572
  });
2692
2573
  }
2693
2574
 
2575
+ // src/refundBlocked.ts
2576
+ var import_sdk12 = require("@arkade-os/sdk");
2577
+ var RefundNotLocallyPossibleError = class extends Error {
2578
+ constructor(reason, message, options) {
2579
+ super(message, options);
2580
+ this.reason = reason;
2581
+ }
2582
+ reason;
2583
+ name = "RefundNotLocallyPossibleError";
2584
+ };
2585
+ async function senderIdentityForSwapRecord(wallet, record) {
2586
+ if (!record.signingDescriptor) {
2587
+ throw new RefundNotLocallyPossibleError(
2588
+ "no-secrets",
2589
+ "this swap record carries no signing descriptor"
2590
+ );
2591
+ }
2592
+ try {
2593
+ return await (0, import_sdk12.contractSigner)(wallet, record.signingDescriptor);
2594
+ } catch (cause) {
2595
+ if (cause instanceof import_sdk12.WalletCannotSignError) {
2596
+ throw new RefundNotLocallyPossibleError(
2597
+ "unsignable-wallet",
2598
+ `this wallet holds ${record.signingDescriptor} but cannot sign with it; attach its signer`,
2599
+ { cause }
2600
+ );
2601
+ }
2602
+ if (cause instanceof import_sdk12.ForeignDescriptorError) {
2603
+ throw new RefundNotLocallyPossibleError(
2604
+ "foreign-descriptor",
2605
+ `this wallet cannot derive ${record.signingDescriptor}; the swap was created on another wallet`,
2606
+ { cause }
2607
+ );
2608
+ }
2609
+ throw cause;
2610
+ }
2611
+ }
2612
+
2694
2613
  // src/swapManager.ts
2695
2614
  var import_base11 = require("@scure/base");
2696
2615
  var RFQ_SWAP_TERMINAL_STATES = ["settled", "refunded", "failed"];
@@ -3482,20 +3401,20 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
3482
3401
  ONCHAIN_RECEIVE_PAIR,
3483
3402
  ONCHAIN_SECONDS_PER_BLOCK,
3484
3403
  ONCHAIN_SEND_PAIR,
3404
+ PreimageNotRecoverableError,
3485
3405
  QUOTE_OPTIONS,
3486
3406
  REFUND_MTP_LAG_SECONDS,
3487
- RFQ_PREIMAGE_TAG,
3488
3407
  RFQ_RESOLVED_STATES,
3489
3408
  RFQ_SWAP_TERMINAL_STATES,
3490
3409
  RFQ_TERMINAL_STATES,
3491
3410
  RefundNotLocallyPossibleError,
3492
3411
  RfqSwapManager,
3412
+ SOLO_REFUND_HEADROOM_SECONDS,
3493
3413
  SWAP_LOCKUP_CONTRACT_KIND,
3494
3414
  SWAP_LOCKUP_CONTRACT_LABEL,
3495
3415
  SWAP_LOCKUP_CONTRACT_TYPE,
3496
3416
  SwapRefusal,
3497
3417
  addAssetSwap,
3498
- adoptSwapDescriptor,
3499
3418
  arkadeSwapRequest,
3500
3419
  assertFundable,
3501
3420
  assertReceivable,
@@ -3504,7 +3423,6 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
3504
3423
  awaitRfqResolution,
3505
3424
  buildHtlcClaim,
3506
3425
  buildHtlcRefund,
3507
- buildPreimageMessage,
3508
3426
  cancelOffer,
3509
3427
  claimOnchainFill,
3510
3428
  claimReceiveLockup,
@@ -3516,8 +3434,6 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
3516
3434
  deriveLightningReceive,
3517
3435
  deriveOnchainReceive,
3518
3436
  deriveOnchainSend,
3519
- derivePreimage,
3520
- deriveSwapSecrets,
3521
3437
  discoverMarkets,
3522
3438
  encodeOffer,
3523
3439
  extractPreimage,
@@ -3526,7 +3442,6 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
3526
3442
  getAssetSwaps,
3527
3443
  getAssetSwapsOrThrow,
3528
3444
  httpTransport,
3529
- isDeterministicSigner,
3530
3445
  isRfqSwapTerminal,
3531
3446
  isRfqTerminal,
3532
3447
  lightningReceiveRequest,
@@ -3542,10 +3457,9 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
3542
3457
  onchainReceiveRequest,
3543
3458
  onchainSendRequest,
3544
3459
  paymentHashOf,
3545
- preimageForRfqSecrets,
3460
+ preimageForSwapRecord,
3546
3461
  pushClaim,
3547
3462
  pushRefundWithoutReceiver,
3548
- randomSwapSecrets,
3549
3463
  readLockupFate,
3550
3464
  receiveVtxoScript,
3551
3465
  refundIfUnresolved,
@@ -3558,15 +3472,12 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
3558
3472
  restoreAssetSwaps,
3559
3473
  retireSettledOfferContracts,
3560
3474
  rfqPair,
3561
- rfqSecretsOfRecord,
3562
- rfqSecretsToRecord,
3563
3475
  sealClaimPacket,
3564
- senderIdentityForRfqSecrets,
3565
3476
  senderIdentityForSwapRecord,
3566
- senderPubkeyForRfqSecrets,
3567
3477
  spendTxidsOf,
3568
3478
  spendUpdate,
3569
3479
  swapPrograms,
3480
+ swapSecretsToRecord,
3570
3481
  unilateralClaimDelay,
3571
3482
  unilateralRefundDelay,
3572
3483
  unilateralRefundWithoutReceiverDelay,