@arkade-os/swap 0.0.9 → 0.0.11

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/README.md CHANGED
@@ -759,6 +759,23 @@ through their stored `preimageHex` or their HD descriptor, and `DB_VERSION` is u
759
759
 
760
760
  Notes from before 0.0.1, kept for consumers who tracked the branch.
761
761
 
762
+ - **`refundIfUnresolved` reports an exited lockup, and its input gained `paymentHash`.**
763
+ `RefundOutcome` has a new `{ outcome: "exited"; outpoints; status }` variant: a lockup whose
764
+ outputs were unilaterally exited lives onchain under the VHTLC script, where no offchain refund
765
+ reaches it. It used to come back as `nothing_to_refund`, which reads as "already resolved" over
766
+ money still sitting at the script — the swept case gets `needs_recovery` for the same reason, and
767
+ this is deliberately **not** that variant: recovery into a fresh batch is a spend no batch can
768
+ make for an onchain output. Complete the unroll and spend the outputs onchain instead.
769
+
770
+ Two required changes for direct callers. The input gains **`paymentHash`** (`sha256(P)` hex — the
771
+ quote's `payment_hash`, which callers already hold): the exit is read through `readLockupFate`,
772
+ and the VHTLC script cannot supply it, since its `preimageHash` is a `hash160` of the same secret.
773
+ And the `indexer` parameter widened from `RefundIndexer` to **`LockupSpendIndexer`**, so it must
774
+ now carry `getVirtualTxs` as well as `getVtxos` — a real `RestIndexerProvider` already does.
775
+
776
+ A lockup funded in two sends of which only one exited reports `exited` for the whole thing and
777
+ leaves the live half unrefunded. That matches `RfqSwapManager`, which reports the same lockup
778
+ `exited` on the same any-output rule; the two must not disagree.
762
779
  - **`RfqSwapManager` can own its own persistence.** New optional
763
780
  `RfqSwapManagerDeps.repository`, new `restoreFromRepository()` and `pruneRetiredSwaps()`, and
764
781
  `addSwap(swap, origin?)` gains an optional second argument. Nothing narrows and nothing is
@@ -422,8 +422,22 @@ var assertPairLength = (pair) => {
422
422
  };
423
423
  var verifyLockupAddress = (quote, derivedAddress) => {
424
424
  const quoted = quote.profile?.lockup_address;
425
- if (derivedAddress !== quoted) throw new AddressMismatch(derivedAddress, quoted);
426
- return derivedAddress;
425
+ const candidates = Array.isArray(derivedAddress) ? derivedAddress : [derivedAddress];
426
+ const matched = candidates.find((address) => address === quoted);
427
+ if (matched === void 0) throw new AddressMismatch(candidates, quoted);
428
+ return matched;
429
+ };
430
+ var LOCKUP_SHAPE_VARIANTS = [void 0, "preTimelockedRefund"];
431
+ var matchQuotedLockup = (quote, hrp, serverPubkey, build) => {
432
+ const candidates = LOCKUP_SHAPE_VARIANTS.map((legacy) => {
433
+ const script = build(legacy);
434
+ return { script, address: script.address(hrp, serverPubkey).encode(), legacy };
435
+ });
436
+ const matchedAddress = verifyLockupAddress(
437
+ quote,
438
+ candidates.map((candidate) => candidate.address)
439
+ );
440
+ return candidates.find((candidate) => candidate.address === matchedAddress);
427
441
  };
428
442
  var assertFundable = (input) => {
429
443
  const fail = (reason, message) => {
@@ -625,13 +639,11 @@ function lightningSendVtxoScript(params) {
625
639
  unilateralRefundWithoutReceiverDelay: seconds(
626
640
  unilateralRefundWithoutReceiverDelay(params.claimDelay)
627
641
  ),
628
- nonInteractiveClaim: {
642
+ nonInteractiveParameters: {
629
643
  receiverPkScript: params.receiverPkScript,
630
- emulatorPubkey: params.emulatorPubkey
631
- },
632
- nonInteractiveRefund: {
633
644
  senderPkScript: params.refundPkScript,
634
- emulatorPubkey: params.emulatorPubkey
645
+ emulatorPubkey: params.emulatorPubkey,
646
+ ...params.legacy !== void 0 && { legacy: params.legacy }
635
647
  }
636
648
  });
637
649
  }
@@ -639,10 +651,8 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
639
651
  const rfqId = params.rfqId ?? newRfqId();
640
652
  const secrets = await provisionRefundKey(wallet);
641
653
  const senderPubkey = secrets.pubkey;
642
- const [info, refundAddress] = await Promise.all([
643
- new RestArkProvider(arkServerUrl).getInfo(),
644
- wallet.getAddress()
645
- ]);
654
+ const refundAddress = secrets.address;
655
+ const info = await new RestArkProvider(arkServerUrl).getInfo();
646
656
  const quote = await transport.requestQuote(
647
657
  lightningSendRequest({ rfqId, invoice: params.invoice.raw, refundAddress, senderPubkey })
648
658
  );
@@ -677,11 +687,20 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
677
687
  ),
678
688
  senderPubkey,
679
689
  receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
680
- refundPkScript: ArkAddress2.decode(refundAddress).pkScript
690
+ refundPkScript: secrets.pkScript
691
+ };
692
+ const matched = matchQuotedLockup(
693
+ quote,
694
+ network.hrp,
695
+ serverPubkey,
696
+ (legacy) => lightningSendVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
697
+ );
698
+ const script = matched.script;
699
+ const address = matched.address;
700
+ const matchedTreeParams = {
701
+ ...treeParams,
702
+ ...matched.legacy !== void 0 && { legacy: matched.legacy }
681
703
  };
682
- const script = lightningSendVtxoScript(treeParams);
683
- const address = script.address(network.hrp, serverPubkey).encode();
684
- verifyLockupAddress(quote, address);
685
704
  assertFundable({
686
705
  quote,
687
706
  invoiceExpiresAt: params.invoice.expiresAt,
@@ -700,7 +719,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
700
719
  refundAddress,
701
720
  senderPubkey,
702
721
  secrets,
703
- treeParams
722
+ treeParams: matchedTreeParams
704
723
  };
705
724
  }
706
725
  var offerTermsFromQuote = (quote, assets) => {
@@ -765,7 +784,7 @@ function deriveOnchainSend(input) {
765
784
  if (refundLocktime === void 0 || htlcPubkey === void 0 || htlcLocktime === void 0 || minConfirmations === void 0 || receiverPkScriptHex === void 0) {
766
785
  throw new Error("onchain-send quote is missing a binding field");
767
786
  }
768
- const script = lightningSendVtxoScript({
787
+ const treeParams = {
769
788
  solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
770
789
  refundLocktime,
771
790
  serverPubkey: input.serverPubkey,
@@ -775,9 +794,13 @@ function deriveOnchainSend(input) {
775
794
  senderPubkey: input.senderPubkey,
776
795
  receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
777
796
  refundPkScript: ArkAddress2.decode(input.refundAddress).pkScript
778
- });
779
- const address = script.address(input.hrp, input.serverPubkey).encode();
780
- verifyLockupAddress(quote, address);
797
+ };
798
+ const { script, address } = matchQuotedLockup(
799
+ quote,
800
+ input.hrp,
801
+ input.serverPubkey,
802
+ (legacy) => lightningSendVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
803
+ );
781
804
  const htlcParams = {
782
805
  paymentHash: input.paymentHash,
783
806
  claimKey: input.payoutPubkey,
@@ -952,13 +975,11 @@ function receiveVtxoScript(params) {
952
975
  unilateralRefundWithoutReceiverDelay: seconds(
953
976
  unilateralRefundWithoutReceiverDelay(params.claimDelay)
954
977
  ),
955
- nonInteractiveClaim: {
978
+ nonInteractiveParameters: {
956
979
  receiverPkScript: params.payoutPkScript,
957
- emulatorPubkey: params.emulatorPubkey
958
- },
959
- nonInteractiveRefund: {
960
980
  senderPkScript: params.solverRefundPkScript,
961
- emulatorPubkey: params.emulatorPubkey
981
+ emulatorPubkey: params.emulatorPubkey,
982
+ ...params.legacy !== void 0 && { legacy: params.legacy }
962
983
  }
963
984
  });
964
985
  }
@@ -982,10 +1003,23 @@ function deriveLightningReceive(input) {
982
1003
  payoutPubkey: input.payoutPubkey,
983
1004
  payoutPkScript: ArkAddress2.decode(input.payoutAddress).pkScript
984
1005
  };
985
- const script = receiveVtxoScript(treeParams);
986
- const address = script.address(input.hrp, input.serverPubkey).encode();
987
- verifyLockupAddress(quote, address);
988
- return { address, swapPkScript: script.pkScript, script, invoice, refundLocktime, treeParams };
1006
+ const matched = matchQuotedLockup(
1007
+ quote,
1008
+ input.hrp,
1009
+ input.serverPubkey,
1010
+ (legacy) => receiveVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
1011
+ );
1012
+ return {
1013
+ address: matched.address,
1014
+ swapPkScript: matched.script.pkScript,
1015
+ script: matched.script,
1016
+ invoice,
1017
+ refundLocktime,
1018
+ treeParams: {
1019
+ ...treeParams,
1020
+ ...matched.legacy !== void 0 && { legacy: matched.legacy }
1021
+ }
1022
+ };
989
1023
  }
990
1024
  async function requestLightningReceive(wallet, arkServerUrl, transport, params) {
991
1025
  const rfqId = params.rfqId ?? newRfqId();
@@ -1073,7 +1107,7 @@ function deriveOnchainReceive(input) {
1073
1107
  if (refundLocktime === void 0 || claimPubkey === void 0 || htlcLocktime === void 0 || minConfirmations === void 0 || solverRefundPkScriptHex === void 0) {
1074
1108
  throw new Error("onchain-receive quote is missing a binding field");
1075
1109
  }
1076
- const script = receiveVtxoScript({
1110
+ const treeParams = {
1077
1111
  solverPubkey: toXOnly(hex3.decode(quote.solver_pubkey), "solver key"),
1078
1112
  refundLocktime,
1079
1113
  serverPubkey: input.serverPubkey,
@@ -1083,9 +1117,13 @@ function deriveOnchainReceive(input) {
1083
1117
  solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
1084
1118
  payoutPubkey: input.payoutPubkey,
1085
1119
  payoutPkScript: ArkAddress2.decode(input.payoutAddress).pkScript
1086
- });
1087
- const address = script.address(input.hrp, input.serverPubkey).encode();
1088
- verifyLockupAddress(quote, address);
1120
+ };
1121
+ const { script, address } = matchQuotedLockup(
1122
+ quote,
1123
+ input.hrp,
1124
+ input.serverPubkey,
1125
+ (legacy) => receiveVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
1126
+ );
1089
1127
  const htlc = onchainHtlcScript(
1090
1128
  {
1091
1129
  paymentHash: input.paymentHash,