@arkade-os/swap 0.0.11 → 0.0.12

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.js CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  ARKADE_ASSET,
3
3
  ARKADE_BTC,
4
4
  AddressMismatch,
5
+ L1_NETWORKS,
5
6
  LIGHTNING_BTC,
6
7
  LIGHTNING_RECEIVE_PAIR,
7
8
  LIGHTNING_SEND_PAIR,
@@ -38,6 +39,7 @@ import {
38
39
  deriveOnchainSend,
39
40
  extractPreimage,
40
41
  httpTransport,
42
+ l1ScriptForAddress,
41
43
  lightningReceiveRequest,
42
44
  lightningSendRequest,
43
45
  lightningSendVtxoScript,
@@ -63,7 +65,7 @@ import {
63
65
  unilateralRefundWithoutReceiverDelay,
64
66
  verifyLockupAddress,
65
67
  verifyReceiveInvoice
66
- } from "./chunk-RIC5ZTZK.js";
68
+ } from "./chunk-XXWOODUE.js";
67
69
  import {
68
70
  InMemoryAssetSwapRepository,
69
71
  marketsCacheKey
@@ -1061,7 +1063,8 @@ function onchainSendProfile(result) {
1061
1063
  htlcLocktime: result.htlcParams.refundLocktime,
1062
1064
  network: result.l1Network,
1063
1065
  htlcAddress: result.htlc.address,
1064
- minConfirmations: result.minConfirmations
1066
+ minConfirmations: result.minConfirmations,
1067
+ payoutPkScript: hex3.encode(result.payoutPkScript)
1065
1068
  };
1066
1069
  }
1067
1070
  var OnchainSendCorridor = {
@@ -1110,6 +1113,9 @@ var OnchainSendCorridor = {
1110
1113
  paymentHash,
1111
1114
  htlc,
1112
1115
  minConfirmations: profile.minConfirmations,
1116
+ // Optional here, required at the write: throwing on an older
1117
+ // record would strand the refund it is still owed.
1118
+ ...profile.payoutPkScript ? { payoutPkScript: hex3.decode(profile.payoutPkScript) } : {},
1113
1119
  ...profile.funding ? { funding: profile.funding } : {},
1114
1120
  ...profile.claimTxid ? { claimTxid: profile.claimTxid } : {}
1115
1121
  };
@@ -1490,8 +1496,294 @@ async function watchOfferSwaps({
1490
1496
  };
1491
1497
  }
1492
1498
 
1493
- // src/claim.ts
1499
+ // src/chainSource.ts
1500
+ import * as btc from "@scure/btc-signer";
1501
+ import { hex as hex7 } from "@scure/base";
1502
+ var addressOf = (pkScript, network) => btc.Address(L1_NETWORKS[network]).encode(btc.OutScript.decode(pkScript));
1503
+ var chainSourceFrom = (provider, network) => {
1504
+ const spenderFromHistory = async (txid, vout, pkScript) => {
1505
+ const txs = await provider.getTransactions(addressOf(pkScript, network));
1506
+ return txs.find((tx) => tx.vin?.some((i) => i.txid === txid && i.vout === vout))?.txid;
1507
+ };
1508
+ return {
1509
+ async getScriptUtxos(pkScript) {
1510
+ const address = addressOf(pkScript, network);
1511
+ const [coins, tip] = await Promise.all([
1512
+ provider.getCoins(address),
1513
+ provider.getChainTip()
1514
+ ]);
1515
+ return coins.map((coin) => ({
1516
+ txid: coin.txid,
1517
+ vout: coin.vout,
1518
+ amount: BigInt(coin.value),
1519
+ // Zero, not one: calling a mempool output "1 deep" would let a
1520
+ // 1-confirmation policy claim against a replaceable transaction.
1521
+ confirmations: coin.status.confirmed && typeof coin.status.block_height === "number" ? Math.max(0, tip.height - coin.status.block_height + 1) : 0
1522
+ }));
1523
+ },
1524
+ async getSpendingTx(txid, vout, pkScript) {
1525
+ const outspends = await provider.getTxOutspends(txid);
1526
+ const outspend = outspends[vout];
1527
+ if (!outspend?.spent) return null;
1528
+ const spender = outspend.txid || await spenderFromHistory(txid, vout, pkScript);
1529
+ if (!spender) return null;
1530
+ const raw = await provider.getRawTransaction(spender);
1531
+ return { txHex: hex7.encode(raw) };
1532
+ },
1533
+ broadcast(txHex) {
1534
+ return provider.broadcastTransaction(txHex);
1535
+ },
1536
+ async getMtp() {
1537
+ return (await provider.getChainTip()).time;
1538
+ }
1539
+ };
1540
+ };
1541
+
1542
+ // src/payment/rendezvous.ts
1543
+ import { selectMarkets, sideLimits as sideLimits2 } from "@arkade-os/solver-discovery";
1494
1544
  import { hex as hex8 } from "@scure/base";
1545
+ var XONLY_HEX = /^[0-9a-f]{64}$/;
1546
+ var rendezvousOf = (market, pinned) => {
1547
+ const transports = { nostr: { relays: market.transports?.nostr?.relays ?? [] } };
1548
+ if (!market.discovery_pubkey || !transports.nostr.relays.length) return void 0;
1549
+ const advertised = market.emulator_pubkey;
1550
+ const emulatorPubkey = advertised === void 0 || advertised === null || advertised === "" ? pinned : typeof advertised === "string" && XONLY_HEX.test(advertised) ? advertised : void 0;
1551
+ if (!emulatorPubkey) return void 0;
1552
+ if (pinned && emulatorPubkey !== pinned) return void 0;
1553
+ const bounds = sideLimits2(market, "quote");
1554
+ if (!bounds) return void 0;
1555
+ return {
1556
+ solverPubkey: market.discovery_pubkey,
1557
+ transports,
1558
+ minSats: Number(bounds.min),
1559
+ maxSats: Number(bounds.max)
1560
+ };
1561
+ };
1562
+ var solverRendezvous = (markets, payoutCorridor, amountSats, fallbackEmulatorPubkey) => {
1563
+ const encoded = fallbackEmulatorPubkey ? hex8.encode(fallbackEmulatorPubkey) : void 0;
1564
+ if (encoded !== void 0 && !XONLY_HEX.test(encoded)) return void 0;
1565
+ const pinned = encoded;
1566
+ const candidates = selectMarkets(markets, {
1567
+ baseId: BTC_ASSET_ID,
1568
+ quoteId: BTC_ASSET_ID,
1569
+ baseCorridor: "arkade",
1570
+ quoteCorridor: payoutCorridor
1571
+ });
1572
+ for (const market of candidates) {
1573
+ const rendezvous = rendezvousOf(market, pinned);
1574
+ if (!rendezvous) continue;
1575
+ if (amountSats >= rendezvous.minSats && amountSats <= rendezvous.maxSats) {
1576
+ return rendezvous;
1577
+ }
1578
+ }
1579
+ return void 0;
1580
+ };
1581
+
1582
+ // src/payment/solverOnchain.ts
1583
+ import { btcTarget, makeHandle, resolveSendAmount, tryResolveSendAmount } from "@arkade-os/sdk";
1584
+ var SOLVER_ONCHAIN_RAIL = "solver-onchain";
1585
+ var solverOnchainRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "onchain", amountSats, fallbackEmulatorPubkey);
1586
+ function solverOnchainRail(deps) {
1587
+ const rendezvousFor = async (amount) => {
1588
+ if (amount === void 0) return void 0;
1589
+ const markets = await deps.discover();
1590
+ return solverOnchainRendezvous(markets, amount, deps.fallbackEmulatorPubkey);
1591
+ };
1592
+ return {
1593
+ id: SOLVER_ONCHAIN_RAIL,
1594
+ match: (req) => btcTarget(req.raw) !== void 0,
1595
+ available: async (req) => {
1596
+ const address = btcTarget(req.raw);
1597
+ if (!address) return false;
1598
+ try {
1599
+ l1ScriptForAddress(address, deps.l1Network);
1600
+ } catch {
1601
+ return false;
1602
+ }
1603
+ const amount = tryResolveSendAmount(req.raw, req.amount);
1604
+ if (amount === void 0) return false;
1605
+ return await rendezvousFor(amount) !== void 0;
1606
+ },
1607
+ quote: async (req, ctx) => {
1608
+ const address = btcTarget(req.raw);
1609
+ const amount = resolveSendAmount(SOLVER_ONCHAIN_RAIL, req.raw, req.amount);
1610
+ const payoutPkScript = l1ScriptForAddress(address, deps.l1Network);
1611
+ const rendezvous = await rendezvousFor(amount);
1612
+ if (!rendezvous) {
1613
+ throw new Error(
1614
+ `${SOLVER_ONCHAIN_RAIL}: no solver serves arkade:BTC -> onchain:BTC at ${amount} sats`
1615
+ );
1616
+ }
1617
+ const negotiated = await deps.connect(
1618
+ rendezvous,
1619
+ (transport) => requestOnchainSend(ctx.wallet, deps.arkServerUrl, transport, {
1620
+ amount,
1621
+ amountSide: "to",
1622
+ payoutPubkey: deps.payoutPubkey,
1623
+ ...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
1624
+ })
1625
+ );
1626
+ if (negotiated.l1Network !== deps.l1Network) {
1627
+ throw new Error(
1628
+ `${SOLVER_ONCHAIN_RAIL}: rail built for ${deps.l1Network} but the swap was negotiated on ${negotiated.l1Network}`
1629
+ );
1630
+ }
1631
+ const swap = { ...negotiated, rendezvous, payoutPkScript };
1632
+ return {
1633
+ railId: SOLVER_ONCHAIN_RAIL,
1634
+ amount,
1635
+ fee: swap.fundAmount - amount,
1636
+ total: swap.fundAmount,
1637
+ meta: {
1638
+ rfqId: swap.rfqId,
1639
+ validUntil: swap.quote.valid_until,
1640
+ htlcAddress: swap.htlc.address,
1641
+ minConfirmations: swap.minConfirmations,
1642
+ solverPubkey: rendezvous.solverPubkey,
1643
+ // The claim tx's fee comes out of the HTLC output at a rate
1644
+ // not knowable now, so `amount` is the payout, not the net.
1645
+ claimFeeDeductedFromPayout: true
1646
+ },
1647
+ send: async () => makeHandle(SOLVER_ONCHAIN_RAIL, async (emit) => {
1648
+ assertFundable({
1649
+ quote: swap.quote,
1650
+ now: Math.floor(Date.now() / 1e3),
1651
+ onchain: {
1652
+ htlcLocktime: swap.htlcParams.refundLocktime,
1653
+ minConfirmations: swap.minConfirmations,
1654
+ direction: "send"
1655
+ }
1656
+ });
1657
+ await deps.persist(swap);
1658
+ await ctx.wallet.send({
1659
+ address: swap.address,
1660
+ amount: swap.fundAmount
1661
+ });
1662
+ emit({ status: "sent" });
1663
+ const result = { railId: SOLVER_ONCHAIN_RAIL, swapId: swap.rfqId };
1664
+ if (!deps.awaitSettlement) return result;
1665
+ let txid;
1666
+ try {
1667
+ ({ txid } = await deps.awaitSettlement(swap));
1668
+ } catch (e) {
1669
+ console.warn(
1670
+ `${SOLVER_ONCHAIN_RAIL}: settlement watch failed; the payment is sent`,
1671
+ e
1672
+ );
1673
+ return result;
1674
+ }
1675
+ const settled = { ...result, txid };
1676
+ emit({ status: "settled", result: settled });
1677
+ return settled;
1678
+ })
1679
+ };
1680
+ }
1681
+ };
1682
+ }
1683
+
1684
+ // src/payment/solverLightning.ts
1685
+ import { invoiceTarget, makeHandle as makeHandle2 } from "@arkade-os/sdk";
1686
+ var SOLVER_LIGHTNING_RAIL = "solver-lightning";
1687
+ var factsOf = (raw, decode, now) => {
1688
+ const invoice = invoiceTarget(raw);
1689
+ if (!invoice) return void 0;
1690
+ let facts;
1691
+ try {
1692
+ facts = decode(invoice);
1693
+ } catch {
1694
+ return void 0;
1695
+ }
1696
+ if (!Number.isInteger(facts.amountSats) || facts.amountSats <= 0) return void 0;
1697
+ if (facts.expiresAt <= now) return void 0;
1698
+ return facts;
1699
+ };
1700
+ var solverLightningRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "lightning", amountSats, fallbackEmulatorPubkey);
1701
+ function solverLightningRail(deps) {
1702
+ const rendezvousFor = async (amountSats) => solverLightningRendezvous(await deps.discover(), amountSats, deps.fallbackEmulatorPubkey);
1703
+ return {
1704
+ id: SOLVER_LIGHTNING_RAIL,
1705
+ match: (req) => invoiceTarget(req.raw) !== void 0,
1706
+ available: async (req) => {
1707
+ const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
1708
+ if (!facts) return false;
1709
+ if (req.amount !== void 0 && req.amount !== facts.amountSats) return false;
1710
+ return await rendezvousFor(facts.amountSats) !== void 0;
1711
+ },
1712
+ quote: async (req, ctx) => {
1713
+ const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
1714
+ if (!facts) {
1715
+ throw new Error(
1716
+ `${SOLVER_LIGHTNING_RAIL}: the request carries no payable BOLT11 invoice (amountless, expired, or undecodable)`
1717
+ );
1718
+ }
1719
+ if (req.amount !== void 0 && req.amount !== facts.amountSats) {
1720
+ throw new Error(
1721
+ `${SOLVER_LIGHTNING_RAIL}: the request names ${req.amount} sats but the invoice is for ${facts.amountSats} \u2014 the payee is paid the invoice`
1722
+ );
1723
+ }
1724
+ const rendezvous = await rendezvousFor(facts.amountSats);
1725
+ if (!rendezvous) {
1726
+ throw new Error(
1727
+ `${SOLVER_LIGHTNING_RAIL}: no solver serves arkade:BTC -> lightning:BTC at ${facts.amountSats} sats`
1728
+ );
1729
+ }
1730
+ const negotiated = await deps.connect(
1731
+ rendezvous,
1732
+ (transport) => requestLightningSend(ctx.wallet, deps.arkServerUrl, transport, {
1733
+ invoice: facts,
1734
+ ...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
1735
+ })
1736
+ );
1737
+ const swap = { ...negotiated, invoice: facts, rendezvous };
1738
+ return {
1739
+ railId: SOLVER_LIGHTNING_RAIL,
1740
+ // `requestLightningSend` refuses a quote that reprices the
1741
+ // invoice, so the spread is a fee on top.
1742
+ amount: facts.amountSats,
1743
+ fee: swap.fundAmount - facts.amountSats,
1744
+ total: swap.fundAmount,
1745
+ meta: {
1746
+ rfqId: swap.rfqId,
1747
+ validUntil: swap.quote.valid_until,
1748
+ paymentHash: facts.paymentHash,
1749
+ invoiceExpiresAt: facts.expiresAt,
1750
+ solverPubkey: rendezvous.solverPubkey
1751
+ },
1752
+ send: async () => makeHandle2(SOLVER_LIGHTNING_RAIL, async (emit) => {
1753
+ assertFundable({
1754
+ quote: swap.quote,
1755
+ invoiceExpiresAt: facts.expiresAt,
1756
+ now: Math.floor(Date.now() / 1e3)
1757
+ });
1758
+ await deps.persist(swap);
1759
+ await ctx.wallet.send({
1760
+ address: swap.address,
1761
+ amount: swap.fundAmount
1762
+ });
1763
+ emit({ status: "sent" });
1764
+ const result = { railId: SOLVER_LIGHTNING_RAIL, swapId: swap.rfqId };
1765
+ if (!deps.awaitSettlement) return result;
1766
+ let preimage;
1767
+ try {
1768
+ ({ preimage } = await deps.awaitSettlement(swap));
1769
+ } catch (e) {
1770
+ console.warn(
1771
+ `${SOLVER_LIGHTNING_RAIL}: settlement watch failed; the payment is sent`,
1772
+ e
1773
+ );
1774
+ return result;
1775
+ }
1776
+ const settled = { ...result, ...preimage !== void 0 && { preimage } };
1777
+ emit({ status: "settled", result: settled });
1778
+ return settled;
1779
+ })
1780
+ };
1781
+ }
1782
+ };
1783
+ }
1784
+
1785
+ // src/claim.ts
1786
+ import { hex as hex10 } from "@scure/base";
1495
1787
  import { ripemd160 } from "@noble/hashes/legacy.js";
1496
1788
  import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
1497
1789
  import {
@@ -1501,7 +1793,7 @@ import {
1501
1793
  } from "@arkade-os/sdk";
1502
1794
 
1503
1795
  // src/refund.ts
1504
- import { base64 as base643, hex as hex7 } from "@scure/base";
1796
+ import { base64 as base643, hex as hex9 } from "@scure/base";
1505
1797
  import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
1506
1798
  import {
1507
1799
  CSVMultisigTapscript,
@@ -1559,7 +1851,7 @@ var LockupNeedsRecoveryError = class extends Error {
1559
1851
  }
1560
1852
  };
1561
1853
  async function findLockupVtxos(indexer, swapPkScript) {
1562
- const scripts = [hex7.encode(swapPkScript)];
1854
+ const scripts = [hex9.encode(swapPkScript)];
1563
1855
  const [spendable, recoverable] = await Promise.all([
1564
1856
  indexer.getVtxos({ scripts, spendableOnly: true }),
1565
1857
  indexer.getVtxos({ scripts, recoverableOnly: true })
@@ -1585,13 +1877,13 @@ async function findLockupVtxos(indexer, swapPkScript) {
1585
1877
  }
1586
1878
  return out;
1587
1879
  }
1588
- var hashesTo = (candidate, paymentHash) => hex7.encode(sha2562(candidate)) === paymentHash;
1880
+ var hashesTo = (candidate, paymentHash) => hex9.encode(sha2562(candidate)) === paymentHash;
1589
1881
  var candidateWitnessItems = (tx, inputIndex) => [
1590
1882
  ...getArkPsbtFields(tx, inputIndex, ConditionWitness).flat(),
1591
1883
  ...tx.getInput(inputIndex).finalScriptWitness ?? []
1592
1884
  ];
1593
1885
  async function readLockupFate(indexer, input) {
1594
- const { vtxos } = await indexer.getVtxos({ scripts: [hex7.encode(input.swapPkScript)] });
1886
+ const { vtxos } = await indexer.getVtxos({ scripts: [hex9.encode(input.swapPkScript)] });
1595
1887
  const all = vtxos ?? [];
1596
1888
  if (all.length === 0) return { fate: "unknown" };
1597
1889
  const exited = all.filter((vtxo) => vtxo.isUnrolled && !hasTerminalSpend(vtxo));
@@ -1626,7 +1918,7 @@ async function readLockupFate(indexer, input) {
1626
1918
  for (let i = 0; i < tx.inputsLength; i++) {
1627
1919
  const spent = tx.getInput(i);
1628
1920
  if (!spent.txid) continue;
1629
- const txid = hex7.encode(spent.txid);
1921
+ const txid = hex9.encode(spent.txid);
1630
1922
  if (!all.some((vtxo) => vtxo.txid === txid && vtxo.vout === spent.index)) continue;
1631
1923
  for (const candidate of candidateWitnessItems(tx, i)) {
1632
1924
  if (hashesTo(candidate, input.paymentHash)) {
@@ -1655,7 +1947,7 @@ async function pushRefundWithoutReceiver(ark, input) {
1655
1947
  const info = await ark.getInfo();
1656
1948
  let serverUnrollScript;
1657
1949
  try {
1658
- serverUnrollScript = CSVMultisigTapscript.decode(hex7.decode(info.checkpointTapscript));
1950
+ serverUnrollScript = CSVMultisigTapscript.decode(hex9.decode(info.checkpointTapscript));
1659
1951
  } catch {
1660
1952
  throw new Error("invalid checkpointTapscript from the Arkade server");
1661
1953
  }
@@ -1781,13 +2073,13 @@ async function pushClaim(ark, input) {
1781
2073
  }
1782
2074
  }
1783
2075
  const committed = input.script.options.preimageHash;
1784
- if (hex8.encode(ripemd160(sha2563(input.preimage))) !== hex8.encode(committed)) {
2076
+ if (hex10.encode(ripemd160(sha2563(input.preimage))) !== hex10.encode(committed)) {
1785
2077
  throw new Error("preimage does not match the covenant's payment hash");
1786
2078
  }
1787
2079
  const info = await ark.getInfo();
1788
2080
  let serverUnrollScript;
1789
2081
  try {
1790
- serverUnrollScript = CSVMultisigTapscript2.decode(hex8.decode(info.checkpointTapscript));
2082
+ serverUnrollScript = CSVMultisigTapscript2.decode(hex10.decode(info.checkpointTapscript));
1791
2083
  } catch {
1792
2084
  throw new Error("invalid checkpointTapscript from the Arkade server");
1793
2085
  }
@@ -1906,7 +2198,7 @@ function arkadeRefunder(deps) {
1906
2198
  }
1907
2199
 
1908
2200
  // src/swapManager.ts
1909
- import { hex as hex9 } from "@scure/base";
2201
+ import { hex as hex11 } from "@scure/base";
1910
2202
  function nextOnchainAction(input) {
1911
2203
  switch (input.phase.phase) {
1912
2204
  case "unfunded":
@@ -2354,7 +2646,7 @@ var RfqSwapManager = class {
2354
2646
  // ── internals ────────────────────────────────────────────────────────────
2355
2647
  track(swap) {
2356
2648
  this.monitored.set(swap.rfqId, swap);
2357
- this.byLockupScript.set(hex9.encode(swap.lockupPkScript), swap);
2649
+ this.byLockupScript.set(hex11.encode(swap.lockupPkScript), swap);
2358
2650
  }
2359
2651
  /** Drops the swap from BOTH indexes. The event index is the one that stops
2360
2652
  * a late event finding a swap that is gone; `pollSwap`'s own
@@ -2363,7 +2655,7 @@ var RfqSwapManager = class {
2363
2655
  * them from silently re-driving a cancelled swap. */
2364
2656
  untrack(rfqId) {
2365
2657
  const swap = this.monitored.get(rfqId);
2366
- if (swap) this.byLockupScript.delete(hex9.encode(swap.lockupPkScript));
2658
+ if (swap) this.byLockupScript.delete(hex11.encode(swap.lockupPkScript));
2367
2659
  this.monitored.delete(rfqId);
2368
2660
  this.refundRefused.delete(rfqId);
2369
2661
  this.lastClaimError.delete(rfqId);
@@ -2425,7 +2717,7 @@ var RfqSwapManager = class {
2425
2717
  if (!lockup) {
2426
2718
  try {
2427
2719
  const [existing] = await contracts.getContracts({
2428
- script: hex9.encode(swap.lockupPkScript)
2720
+ script: hex11.encode(swap.lockupPkScript)
2429
2721
  });
2430
2722
  if (existing) {
2431
2723
  this.registered.set(swap.rfqId, true);
@@ -2444,13 +2736,13 @@ var RfqSwapManager = class {
2444
2736
  );
2445
2737
  return;
2446
2738
  }
2447
- const script = hex9.encode(lockup.script.pkScript);
2448
- if (script !== hex9.encode(swap.lockupPkScript)) {
2739
+ const script = hex11.encode(lockup.script.pkScript);
2740
+ if (script !== hex11.encode(swap.lockupPkScript)) {
2449
2741
  this.registered.set(swap.rfqId, false);
2450
2742
  this.emitFailed(
2451
2743
  swap,
2452
2744
  new Error(
2453
- `swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${hex9.encode(swap.lockupPkScript)}`
2745
+ `swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${hex11.encode(swap.lockupPkScript)}`
2454
2746
  )
2455
2747
  );
2456
2748
  return;
@@ -2469,7 +2761,7 @@ var RfqSwapManager = class {
2469
2761
  * script for its whole life. Best-effort — the swap is over either way. */
2470
2762
  retireContract(swap) {
2471
2763
  if (!this.deps.contracts || !this.registered.get(swap.rfqId)) return;
2472
- void this.deps.contracts.setContractWatchState(hex9.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
2764
+ void this.deps.contracts.setContractWatchState(hex11.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
2473
2765
  }
2474
2766
  arm() {
2475
2767
  if (!this.running) return;
@@ -2945,7 +3237,7 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
2945
3237
  import {
2946
3238
  ArkAddress as ArkAddress4
2947
3239
  } from "@arkade-os/sdk";
2948
- import { hex as hex10 } from "@scure/base";
3240
+ import { hex as hex12 } from "@scure/base";
2949
3241
  var LABELS = {
2950
3242
  lightning_send: "Lightning send",
2951
3243
  lightning_receive: "Lightning receive",
@@ -3020,7 +3312,7 @@ async function activityInputOf(record, indexer) {
3020
3312
  async function lockupTxids(indexer, record, wantFunding) {
3021
3313
  let script;
3022
3314
  try {
3023
- script = hex10.encode(ArkAddress4.decode(record.lockupAddress).pkScript);
3315
+ script = hex12.encode(ArkAddress4.decode(record.lockupAddress).pkScript);
3024
3316
  } catch {
3025
3317
  return [];
3026
3318
  }
@@ -3073,6 +3365,8 @@ export {
3073
3365
  RfqSwapManager,
3074
3366
  RfqSwapOriginRequired,
3075
3367
  SOLO_REFUND_HEADROOM_SECONDS,
3368
+ SOLVER_LIGHTNING_RAIL,
3369
+ SOLVER_ONCHAIN_RAIL,
3076
3370
  SWAP_LOCKUP_CONTRACT_KIND,
3077
3371
  SWAP_LOCKUP_CONTRACT_LABEL,
3078
3372
  SWAP_LOCKUP_CONTRACT_TYPE,
@@ -3089,6 +3383,7 @@ export {
3089
3383
  buildHtlcClaim,
3090
3384
  buildHtlcRefund,
3091
3385
  cancelOffer,
3386
+ chainSourceFrom,
3092
3387
  claimOnchainFill,
3093
3388
  claimReceiveLockup,
3094
3389
  classifyDepositSpend,
@@ -3110,6 +3405,7 @@ export {
3110
3405
  httpTransport,
3111
3406
  isRfqSwapTerminal,
3112
3407
  isRfqTerminal,
3408
+ l1ScriptForAddress,
3113
3409
  lightningReceiveRequest,
3114
3410
  lightningSendRequest,
3115
3411
  lightningSendVtxoScript,
@@ -3149,6 +3445,11 @@ export {
3149
3445
  sealClaimPacket,
3150
3446
  senderIdentityForSwapRecord,
3151
3447
  shouldRetainRfqSwap,
3448
+ solverLightningRail,
3449
+ solverLightningRendezvous,
3450
+ solverOnchainRail,
3451
+ solverOnchainRendezvous,
3452
+ solverRendezvous,
3152
3453
  spendTxidsOf,
3153
3454
  spendUpdate,
3154
3455
  swapActivityResolver,
package/dist/nostr.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { y as RfqTransport } from './rfq-DkckzRKK.cjs';
1
+ import { R as RfqTransport } from './rfq-DzsmhXX3.cjs';
2
2
  import { SimplePool } from 'nostr-tools';
3
3
  import '@arkade-os/sdk';
4
4
 
package/dist/nostr.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { y as RfqTransport } from './rfq-DkckzRKK.js';
1
+ import { R as RfqTransport } from './rfq-DzsmhXX3.js';
2
2
  import { SimplePool } from 'nostr-tools';
3
3
  import '@arkade-os/sdk';
4
4
 
package/dist/nostr.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  expectQuote,
3
3
  pairOf
4
- } from "./chunk-RIC5ZTZK.js";
4
+ } from "./chunk-XXWOODUE.js";
5
5
 
6
6
  // src/nostr.ts
7
7
  import {
@@ -1,8 +1,8 @@
1
1
  import { RealmLike } from '@arkade-os/sdk/repositories/realm';
2
- import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-B02vsgcV.cjs';
2
+ import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-DaK1RzRq.cjs';
3
3
  import '@arkade-os/solver-discovery';
4
4
  import '@arkade-os/sdk';
5
- import '../../rfq-DkckzRKK.cjs';
5
+ import '../../rfq-DzsmhXX3.cjs';
6
6
 
7
7
  /**
8
8
  * Realm backend for React Native.
@@ -1,8 +1,8 @@
1
1
  import { RealmLike } from '@arkade-os/sdk/repositories/realm';
2
- import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-CfE18Fif.js';
2
+ import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-DlLvj_y6.js';
3
3
  import '@arkade-os/solver-discovery';
4
4
  import '@arkade-os/sdk';
5
- import '../../rfq-DkckzRKK.js';
5
+ import '../../rfq-DzsmhXX3.js';
6
6
 
7
7
  /**
8
8
  * Realm backend for React Native.
@@ -1,8 +1,8 @@
1
1
  import { SQLExecutor } from '@arkade-os/sdk/repositories/sqlite';
2
- import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-B02vsgcV.cjs';
2
+ import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-DaK1RzRq.cjs';
3
3
  import '@arkade-os/solver-discovery';
4
4
  import '@arkade-os/sdk';
5
- import '../../rfq-DkckzRKK.cjs';
5
+ import '../../rfq-DzsmhXX3.cjs';
6
6
 
7
7
  /**
8
8
  * SQLite backend over the SDK's `SQLExecutor`, so any driver plugs in
@@ -1,8 +1,8 @@
1
1
  import { SQLExecutor } from '@arkade-os/sdk/repositories/sqlite';
2
- import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-CfE18Fif.js';
2
+ import { A as AssetSwapRepository, a as AssetSwap, R as RfqSwapRecord, M as MarketsCacheEntry } from '../../repository-DlLvj_y6.js';
3
3
  import '@arkade-os/solver-discovery';
4
4
  import '@arkade-os/sdk';
5
- import '../../rfq-DkckzRKK.js';
5
+ import '../../rfq-DzsmhXX3.js';
6
6
 
7
7
  /**
8
8
  * SQLite backend over the SDK's `SQLExecutor`, so any driver plugs in
@@ -1,6 +1,6 @@
1
1
  import { DiscoveredMarket } from '@arkade-os/solver-discovery';
2
2
  import { IWallet, ProvisionedKey, ProvisionedClaimSecret, RestArkProvider, RestIndexerProvider, VHTLC, Identity, IContractManager } from '@arkade-os/sdk';
3
- import { x as RfqStatus, y as RfqTransport, a as OnchainHtlc, e as ChainUtxo, C as ChainSource, t as OnchainHtlcPhase } from './rfq-DkckzRKK.js';
3
+ import { B as RfqStatus, R as RfqTransport, a as OnchainHtlc, f as ChainUtxo, C as ChainSource, v as OnchainHtlcPhase } from './rfq-DzsmhXX3.cjs';
4
4
 
5
5
  type AssetSwapStatus = "pending" | "cancelling" | "fulfilled" | "cancelled" | "recoverable" | "awaiting_fill" | "claimable" | "claimed" | "refunded_l1";
6
6
  /** The sentinel asset id for BTC itself, as opposed to a 68-hex asset id.
@@ -829,6 +829,9 @@ interface OnchainSendSwap extends RfqSwapCommon {
829
829
  htlc: OnchainHtlc;
830
830
  /** `profile.min_confirmations` from the quote. */
831
831
  minConfirmations: number;
832
+ /** Where {@link RfqSwapManagerCallbacks.claimOnchain} pays; optional only
833
+ * so records predating its profile slot still restore. */
834
+ payoutPkScript?: Uint8Array;
832
835
  /** The fill's outpoint, learned on first sighting. Without it a SPENT
833
836
  * HTLC reads as never funded — see {@link classifyOnchainHtlc}. */
834
837
  funding?: {
@@ -1,6 +1,6 @@
1
1
  import { DiscoveredMarket } from '@arkade-os/solver-discovery';
2
2
  import { IWallet, ProvisionedKey, ProvisionedClaimSecret, RestArkProvider, RestIndexerProvider, VHTLC, Identity, IContractManager } from '@arkade-os/sdk';
3
- import { x as RfqStatus, y as RfqTransport, a as OnchainHtlc, e as ChainUtxo, C as ChainSource, t as OnchainHtlcPhase } from './rfq-DkckzRKK.cjs';
3
+ import { B as RfqStatus, R as RfqTransport, a as OnchainHtlc, f as ChainUtxo, C as ChainSource, v as OnchainHtlcPhase } from './rfq-DzsmhXX3.js';
4
4
 
5
5
  type AssetSwapStatus = "pending" | "cancelling" | "fulfilled" | "cancelled" | "recoverable" | "awaiting_fill" | "claimable" | "claimed" | "refunded_l1";
6
6
  /** The sentinel asset id for BTC itself, as opposed to a 68-hex asset id.
@@ -829,6 +829,9 @@ interface OnchainSendSwap extends RfqSwapCommon {
829
829
  htlc: OnchainHtlc;
830
830
  /** `profile.min_confirmations` from the quote. */
831
831
  minConfirmations: number;
832
+ /** Where {@link RfqSwapManagerCallbacks.claimOnchain} pays; optional only
833
+ * so records predating its profile slot still restore. */
834
+ payoutPkScript?: Uint8Array;
832
835
  /** The fill's outpoint, learned on first sighting. Without it a SPENT
833
836
  * HTLC reads as never funded — see {@link classifyOnchainHtlc}. */
834
837
  funding?: {