@arkade-os/swap 0.0.11 → 0.0.13

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
@@ -34,6 +34,8 @@ __export(index_exports, {
34
34
  ARKADE_BTC: () => ARKADE_BTC,
35
35
  AddressMismatch: () => AddressMismatch,
36
36
  BTC_ASSET_ID: () => BTC_ASSET_ID,
37
+ CROSS_ASSET_RAIL: () => CROSS_ASSET_RAIL,
38
+ CovclaimdRevealError: () => CovclaimdRevealError,
37
39
  InMemoryAssetSwapRepository: () => InMemoryAssetSwapRepository,
38
40
  IndexedDbAssetSwapRepository: () => IndexedDbAssetSwapRepository,
39
41
  LIGHTNING_BTC: () => LIGHTNING_BTC,
@@ -66,6 +68,8 @@ __export(index_exports, {
66
68
  RfqSwapManager: () => RfqSwapManager,
67
69
  RfqSwapOriginRequired: () => RfqSwapOriginRequired,
68
70
  SOLO_REFUND_HEADROOM_SECONDS: () => SOLO_REFUND_HEADROOM_SECONDS,
71
+ SOLVER_LIGHTNING_RAIL: () => SOLVER_LIGHTNING_RAIL,
72
+ SOLVER_ONCHAIN_RAIL: () => SOLVER_ONCHAIN_RAIL,
69
73
  SWAP_LOCKUP_CONTRACT_KIND: () => SWAP_LOCKUP_CONTRACT_KIND,
70
74
  SWAP_LOCKUP_CONTRACT_LABEL: () => SWAP_LOCKUP_CONTRACT_LABEL,
71
75
  SWAP_LOCKUP_CONTRACT_TYPE: () => SWAP_LOCKUP_CONTRACT_TYPE,
@@ -82,13 +86,16 @@ __export(index_exports, {
82
86
  buildHtlcClaim: () => buildHtlcClaim,
83
87
  buildHtlcRefund: () => buildHtlcRefund,
84
88
  cancelOffer: () => cancelOffer,
89
+ chainSourceFrom: () => chainSourceFrom,
85
90
  claimOnchainFill: () => claimOnchainFill,
86
91
  claimReceiveLockup: () => claimReceiveLockup,
87
92
  classifyDepositSpend: () => classifyDepositSpend,
88
93
  classifyOnchainHtlc: () => classifyOnchainHtlc,
89
94
  classifySpend: () => classifySpend,
95
+ covclaimdClient: () => covclaimdClient,
90
96
  createOffer: () => createOffer,
91
97
  createRfqSwapRecord: () => createRfqSwapRecord,
98
+ crossAssetRail: () => crossAssetRail,
92
99
  decodeOffer: () => decodeOffer,
93
100
  deriveLightningReceive: () => deriveLightningReceive,
94
101
  deriveOnchainReceive: () => deriveOnchainReceive,
@@ -103,6 +110,7 @@ __export(index_exports, {
103
110
  httpTransport: () => httpTransport,
104
111
  isRfqSwapTerminal: () => isRfqSwapTerminal,
105
112
  isRfqTerminal: () => isRfqTerminal,
113
+ l1ScriptForAddress: () => l1ScriptForAddress,
106
114
  lightningReceiveRequest: () => lightningReceiveRequest,
107
115
  lightningSendRequest: () => lightningSendRequest,
108
116
  lightningSendVtxoScript: () => lightningSendVtxoScript,
@@ -133,6 +141,8 @@ __export(index_exports, {
133
141
  requestOnchainSend: () => requestOnchainSend,
134
142
  restoreAssetSwaps: () => restoreAssetSwaps,
135
143
  retireSettledOfferContracts: () => retireSettledOfferContracts,
144
+ revealClaimPacket: () => revealClaimPacket,
145
+ revealFieldsFromScript: () => revealFieldsFromScript,
136
146
  rfqClaimSecretOf: () => rfqClaimSecretOf,
137
147
  rfqPair: () => rfqPair,
138
148
  rfqSecretsProfile: () => rfqSecretsProfile,
@@ -142,6 +152,11 @@ __export(index_exports, {
142
152
  sealClaimPacket: () => sealClaimPacket,
143
153
  senderIdentityForSwapRecord: () => senderIdentityForSwapRecord,
144
154
  shouldRetainRfqSwap: () => shouldRetainRfqSwap,
155
+ solverLightningRail: () => solverLightningRail,
156
+ solverLightningRendezvous: () => solverLightningRendezvous,
157
+ solverOnchainRail: () => solverOnchainRail,
158
+ solverOnchainRendezvous: () => solverOnchainRendezvous,
159
+ solverRendezvous: () => solverRendezvous,
145
160
  spendTxidsOf: () => spendTxidsOf,
146
161
  spendUpdate: () => spendUpdate,
147
162
  swapActivityResolver: () => swapActivityResolver,
@@ -1158,6 +1173,7 @@ var L1_NETWORKS = {
1158
1173
  testnet: btc.TEST_NETWORK,
1159
1174
  regtest: { ...btc.TEST_NETWORK, bech32: "bcrt" }
1160
1175
  };
1176
+ var l1ScriptForAddress = (address, network) => btc.OutScript.encode(btc.Address(L1_NETWORKS[network]).decode(address));
1161
1177
  function onchainHtlcScript(params, network) {
1162
1178
  if (params.claimKey.length !== 32 || params.refundKey.length !== 32) {
1163
1179
  throw new Error("claimKey and refundKey must be 32-byte x-only keys");
@@ -1340,7 +1356,11 @@ async function classifyOnchainHtlc(chain, input) {
1340
1356
  const best = utxos.sort((a, b) => b.amount > a.amount ? 1 : -1)[0];
1341
1357
  if (!best) {
1342
1358
  if (!input.funding) return { phase: "unfunded" };
1343
- const spend = await chain.getSpendingTx(input.funding.txid, input.funding.vout);
1359
+ const spend = await chain.getSpendingTx(
1360
+ input.funding.txid,
1361
+ input.funding.vout,
1362
+ input.htlc.pkScript
1363
+ );
1344
1364
  if (!spend) return { phase: "unfunded" };
1345
1365
  const preimage = extractPreimage(spend.txHex, input.htlc.paymentHash);
1346
1366
  const txid = btc.Transaction.fromRaw(import_base3.hex.decode(spend.txHex), {
@@ -1399,7 +1419,8 @@ function onchainSendProfile(result) {
1399
1419
  htlcLocktime: result.htlcParams.refundLocktime,
1400
1420
  network: result.l1Network,
1401
1421
  htlcAddress: result.htlc.address,
1402
- minConfirmations: result.minConfirmations
1422
+ minConfirmations: result.minConfirmations,
1423
+ payoutPkScript: import_base4.hex.encode(result.payoutPkScript)
1403
1424
  };
1404
1425
  }
1405
1426
  var OnchainSendCorridor = {
@@ -1448,6 +1469,9 @@ var OnchainSendCorridor = {
1448
1469
  paymentHash,
1449
1470
  htlc,
1450
1471
  minConfirmations: profile.minConfirmations,
1472
+ // Optional here, required at the write: throwing on an older
1473
+ // record would strand the refund it is still owed.
1474
+ ...profile.payoutPkScript ? { payoutPkScript: import_base4.hex.decode(profile.payoutPkScript) } : {},
1451
1475
  ...profile.funding ? { funding: profile.funding } : {},
1452
1476
  ...profile.claimTxid ? { claimTxid: profile.claimTxid } : {}
1453
1477
  };
@@ -2045,6 +2069,43 @@ var assertFundable = (input) => {
2045
2069
  if (input.quote.refund_locktime !== void 0 && input.quote.refund_locktime - input.now < MIN_HEADROOM_SECONDS) {
2046
2070
  fail("insufficient_headroom", "refund deadline headroom below 90 minutes");
2047
2071
  }
2072
+ if (input.maxFee) {
2073
+ const { bps, sats, referenceRate } = input.maxFee;
2074
+ if (bps === void 0 && sats === void 0) {
2075
+ fail("max_fee_unbounded", "maxFee names neither bps nor sats");
2076
+ }
2077
+ if (bps !== void 0 && (!Number.isInteger(bps) || bps < 0 || bps > 1e4)) {
2078
+ fail("max_fee_out_of_range", `maxFee.bps must be an integer in 0..10000, got ${bps}`);
2079
+ }
2080
+ if (sats !== void 0 && (!Number.isInteger(sats) || sats < 0)) {
2081
+ fail("max_fee_out_of_range", `maxFee.sats must be a non-negative integer, got ${sats}`);
2082
+ }
2083
+ const legs = input.quote.pair.split("->");
2084
+ const assetOf = (leg) => leg.slice(leg.indexOf(":") + 1);
2085
+ const sameAsset = legs.length === 2 && assetOf(legs[0]) === assetOf(legs[1]);
2086
+ if (!sameAsset && referenceRate === void 0) {
2087
+ fail(
2088
+ "fee_gate_unavailable",
2089
+ `maxFee cannot gate ${input.quote.pair}: its legs name different assets, so from_amount - to_amount is not a fee. Supply maxFee.referenceRate (to-units per from-unit) from a source of your OWN \u2014 reading it off the solver's published feed would check the solver against its own number`
2090
+ );
2091
+ }
2092
+ if (!sameAsset && (!Number.isFinite(referenceRate) || referenceRate <= 0)) {
2093
+ fail(
2094
+ "max_fee_out_of_range",
2095
+ `maxFee.referenceRate must be a positive finite number, got ${referenceRate}`
2096
+ );
2097
+ }
2098
+ const fee = sameAsset ? input.quote.from_amount - input.quote.to_amount : Math.ceil(
2099
+ (input.quote.from_amount * referenceRate - input.quote.to_amount) / referenceRate
2100
+ );
2101
+ const allowed = Math.max(
2102
+ sats ?? 0,
2103
+ Math.floor(input.quote.from_amount * (bps ?? 0) / 1e4)
2104
+ );
2105
+ if (fee > allowed) {
2106
+ fail("fee_too_high", `fee ${fee} exceeds the ${allowed} this client allows`);
2107
+ }
2108
+ }
2048
2109
  if (input.onchain) {
2049
2110
  const { htlcLocktime, minConfirmations, direction } = input.onchain;
2050
2111
  if (!Number.isInteger(minConfirmations) || minConfirmations < 1 || minConfirmations > MAX_MIN_CONFIRMATIONS) {
@@ -2348,7 +2409,7 @@ var lightningReceiveRequest = (input) => ({
2348
2409
  payment_hash: input.paymentHash,
2349
2410
  payout_address: input.payoutAddress,
2350
2411
  payout_pubkey: import_base10.hex.encode(input.payoutPubkey),
2351
- claim_packet: input.claimPacket
2412
+ ...input.claimPacket === void 0 ? {} : { claim_packet: input.claimPacket }
2352
2413
  }
2353
2414
  });
2354
2415
  var onchainReceiveRequest = (input) => ({
@@ -2360,7 +2421,7 @@ var onchainReceiveRequest = (input) => ({
2360
2421
  amount: input.amount,
2361
2422
  profile: {
2362
2423
  payment_hash: input.paymentHash,
2363
- claim_packet: input.claimPacket,
2424
+ ...input.claimPacket === void 0 ? {} : { claim_packet: input.claimPacket },
2364
2425
  refund_pubkey: import_base10.hex.encode(input.refundPubkey),
2365
2426
  payout_address: input.payoutAddress,
2366
2427
  payout_pubkey: import_base10.hex.encode(input.payoutPubkey)
@@ -2440,6 +2501,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
2440
2501
  amountSide: params.amountSide
2441
2502
  })
2442
2503
  );
2504
+ assertQuotedAmount(quote, params.amountSide, params.amount);
2443
2505
  const network = (0, import_sdk9.getNetwork)(info.network);
2444
2506
  const derived = deriveOnchainSend({
2445
2507
  quote,
@@ -2630,17 +2692,17 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
2630
2692
  new import_sdk9.RestArkProvider(arkServerUrl).getInfo(),
2631
2693
  wallet.getAddress()
2632
2694
  ]);
2633
- const claimPacket = await sealClaimPacket({
2695
+ const claimPacket = params.covclaimdPubkey ? await sealClaimPacket({
2634
2696
  preimage,
2635
2697
  covclaimdPubkey: params.covclaimdPubkey
2636
- });
2698
+ }) : void 0;
2637
2699
  const quote = await transport.requestQuote(
2638
2700
  lightningReceiveRequest({
2639
2701
  rfqId,
2640
2702
  paymentHash,
2641
2703
  payoutAddress,
2642
2704
  payoutPubkey,
2643
- claimPacket: claimPacket.ciphertext,
2705
+ claimPacket: claimPacket?.ciphertext,
2644
2706
  amount: params.amount,
2645
2707
  amountSide: params.amountSide
2646
2708
  })
@@ -2753,10 +2815,10 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
2753
2815
  new import_sdk9.RestArkProvider(arkServerUrl).getInfo(),
2754
2816
  wallet.getAddress()
2755
2817
  ]);
2756
- const claimPacket = await sealClaimPacket({
2818
+ const claimPacket = params.covclaimdPubkey ? await sealClaimPacket({
2757
2819
  preimage,
2758
2820
  covclaimdPubkey: params.covclaimdPubkey
2759
- });
2821
+ }) : void 0;
2760
2822
  const quote = await transport.requestQuote(
2761
2823
  onchainReceiveRequest({
2762
2824
  rfqId,
@@ -2764,7 +2826,7 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
2764
2826
  payoutAddress,
2765
2827
  payoutPubkey,
2766
2828
  refundPubkey: params.refundPubkey,
2767
- claimPacket: claimPacket.ciphertext,
2829
+ claimPacket: claimPacket?.ciphertext,
2768
2830
  amount: params.amount,
2769
2831
  amountSide: params.amountSide
2770
2832
  })
@@ -2815,16 +2877,550 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
2815
2877
  };
2816
2878
  }
2817
2879
 
2818
- // src/claim.ts
2880
+ // src/chainSource.ts
2881
+ var btc2 = __toESM(require("@scure/btc-signer"), 1);
2882
+ var import_base11 = require("@scure/base");
2883
+ var addressOf = (pkScript, network) => btc2.Address(L1_NETWORKS[network]).encode(btc2.OutScript.decode(pkScript));
2884
+ var chainSourceFrom = (provider, network) => {
2885
+ const spenderFromHistory = async (txid, vout, pkScript) => {
2886
+ const txs = await provider.getTransactions(addressOf(pkScript, network));
2887
+ return txs.find((tx) => tx.vin?.some((i) => i.txid === txid && i.vout === vout))?.txid;
2888
+ };
2889
+ return {
2890
+ async getScriptUtxos(pkScript) {
2891
+ const address = addressOf(pkScript, network);
2892
+ const [coins, tip] = await Promise.all([
2893
+ provider.getCoins(address),
2894
+ provider.getChainTip()
2895
+ ]);
2896
+ return coins.map((coin) => ({
2897
+ txid: coin.txid,
2898
+ vout: coin.vout,
2899
+ amount: BigInt(coin.value),
2900
+ // Zero, not one: calling a mempool output "1 deep" would let a
2901
+ // 1-confirmation policy claim against a replaceable transaction.
2902
+ confirmations: coin.status.confirmed && typeof coin.status.block_height === "number" ? Math.max(0, tip.height - coin.status.block_height + 1) : 0
2903
+ }));
2904
+ },
2905
+ async getSpendingTx(txid, vout, pkScript) {
2906
+ const outspends = await provider.getTxOutspends(txid);
2907
+ const outspend = outspends[vout];
2908
+ if (!outspend?.spent) return null;
2909
+ const spender = outspend.txid || await spenderFromHistory(txid, vout, pkScript);
2910
+ if (!spender) return null;
2911
+ const raw = await provider.getRawTransaction(spender);
2912
+ return { txHex: import_base11.hex.encode(raw) };
2913
+ },
2914
+ broadcast(txHex) {
2915
+ return provider.broadcastTransaction(txHex);
2916
+ },
2917
+ async getMtp() {
2918
+ return (await provider.getChainTip()).time;
2919
+ }
2920
+ };
2921
+ };
2922
+
2923
+ // src/payment/rendezvous.ts
2924
+ var import_solver_discovery2 = require("@arkade-os/solver-discovery");
2819
2925
  var import_base12 = require("@scure/base");
2820
- var import_legacy3 = require("@noble/hashes/legacy.js");
2821
- var import_sha25 = require("@noble/hashes/sha2.js");
2926
+ var XONLY_HEX = /^[0-9a-f]{64}$/;
2927
+ var rendezvousOf = (market, pinned) => {
2928
+ const transports = { nostr: { relays: market.transports?.nostr?.relays ?? [] } };
2929
+ if (!market.discovery_pubkey || !transports.nostr.relays.length) return void 0;
2930
+ const advertised = market.emulator_pubkey;
2931
+ const emulatorPubkey = advertised === void 0 || advertised === null || advertised === "" ? pinned : typeof advertised === "string" && XONLY_HEX.test(advertised) ? advertised : void 0;
2932
+ if (!emulatorPubkey) return void 0;
2933
+ if (pinned && emulatorPubkey !== pinned) return void 0;
2934
+ const bounds = (0, import_solver_discovery2.sideLimits)(market, "quote");
2935
+ if (!bounds) return void 0;
2936
+ return {
2937
+ solverPubkey: market.discovery_pubkey,
2938
+ transports,
2939
+ minSats: Number(bounds.min),
2940
+ maxSats: Number(bounds.max)
2941
+ };
2942
+ };
2943
+ var solverRendezvous = (markets, payoutCorridor, amountSats, fallbackEmulatorPubkey) => {
2944
+ const encoded = fallbackEmulatorPubkey ? import_base12.hex.encode(fallbackEmulatorPubkey) : void 0;
2945
+ if (encoded !== void 0 && !XONLY_HEX.test(encoded)) return void 0;
2946
+ const pinned = encoded;
2947
+ const candidates = (0, import_solver_discovery2.selectMarkets)(markets, {
2948
+ baseId: BTC_ASSET_ID,
2949
+ quoteId: BTC_ASSET_ID,
2950
+ baseCorridor: "arkade",
2951
+ quoteCorridor: payoutCorridor
2952
+ });
2953
+ for (const market of candidates) {
2954
+ const rendezvous = rendezvousOf(market, pinned);
2955
+ if (!rendezvous) continue;
2956
+ if (amountSats >= rendezvous.minSats && amountSats <= rendezvous.maxSats) {
2957
+ return rendezvous;
2958
+ }
2959
+ }
2960
+ return void 0;
2961
+ };
2962
+
2963
+ // src/payment/solverOnchain.ts
2964
+ var import_sdk11 = require("@arkade-os/sdk");
2965
+ var SOLVER_ONCHAIN_RAIL = "solver-onchain";
2966
+ var solverOnchainRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "onchain", amountSats, fallbackEmulatorPubkey);
2967
+ function solverOnchainRail(deps) {
2968
+ const rendezvousFor = async (amount) => {
2969
+ if (amount === void 0) return void 0;
2970
+ const markets = await deps.discover();
2971
+ return solverOnchainRendezvous(markets, amount, deps.fallbackEmulatorPubkey);
2972
+ };
2973
+ return {
2974
+ id: SOLVER_ONCHAIN_RAIL,
2975
+ match: (req) => (0, import_sdk11.btcTarget)(req.raw) !== void 0,
2976
+ available: async (req) => {
2977
+ if ((0, import_sdk11.assetsOf)(req).length > 0) return false;
2978
+ const address = (0, import_sdk11.btcTarget)(req.raw);
2979
+ if (!address) return false;
2980
+ try {
2981
+ l1ScriptForAddress(address, deps.l1Network);
2982
+ } catch {
2983
+ return false;
2984
+ }
2985
+ const amount = (0, import_sdk11.tryResolveSendAmount)(req.raw, req.amount);
2986
+ if (amount === void 0) return false;
2987
+ return await rendezvousFor(amount) !== void 0;
2988
+ },
2989
+ quote: async (req, ctx) => {
2990
+ const address = (0, import_sdk11.btcTarget)(req.raw);
2991
+ (0, import_sdk11.assertNoAssets)(SOLVER_ONCHAIN_RAIL, req);
2992
+ const amount = (0, import_sdk11.resolveSendAmount)(SOLVER_ONCHAIN_RAIL, req.raw, req.amount);
2993
+ const payoutPkScript = l1ScriptForAddress(address, deps.l1Network);
2994
+ const rendezvous = await rendezvousFor(amount);
2995
+ if (!rendezvous) {
2996
+ throw new Error(
2997
+ `${SOLVER_ONCHAIN_RAIL}: no solver serves arkade:BTC -> onchain:BTC at ${amount} sats`
2998
+ );
2999
+ }
3000
+ const negotiated = await deps.connect(
3001
+ rendezvous,
3002
+ (transport) => requestOnchainSend(ctx.wallet, deps.arkServerUrl, transport, {
3003
+ amount,
3004
+ amountSide: "to",
3005
+ payoutPubkey: deps.payoutPubkey,
3006
+ ...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
3007
+ })
3008
+ );
3009
+ if (negotiated.l1Network !== deps.l1Network) {
3010
+ throw new Error(
3011
+ `${SOLVER_ONCHAIN_RAIL}: rail built for ${deps.l1Network} but the swap was negotiated on ${negotiated.l1Network}`
3012
+ );
3013
+ }
3014
+ const swap = { ...negotiated, rendezvous, payoutPkScript };
3015
+ return {
3016
+ railId: SOLVER_ONCHAIN_RAIL,
3017
+ amount,
3018
+ fee: swap.fundAmount - amount,
3019
+ total: swap.fundAmount,
3020
+ meta: {
3021
+ rfqId: swap.rfqId,
3022
+ validUntil: swap.quote.valid_until,
3023
+ htlcAddress: swap.htlc.address,
3024
+ minConfirmations: swap.minConfirmations,
3025
+ solverPubkey: rendezvous.solverPubkey,
3026
+ // The claim tx's fee comes out of the HTLC output at a rate
3027
+ // not knowable now, so `amount` is the payout, not the net.
3028
+ claimFeeDeductedFromPayout: true
3029
+ },
3030
+ send: async () => (0, import_sdk11.makeHandle)(SOLVER_ONCHAIN_RAIL, async (emit) => {
3031
+ assertFundable({
3032
+ quote: swap.quote,
3033
+ now: Math.floor(Date.now() / 1e3),
3034
+ onchain: {
3035
+ htlcLocktime: swap.htlcParams.refundLocktime,
3036
+ minConfirmations: swap.minConfirmations,
3037
+ direction: "send"
3038
+ }
3039
+ });
3040
+ await deps.persist(swap);
3041
+ await ctx.wallet.send({
3042
+ address: swap.address,
3043
+ amount: swap.fundAmount
3044
+ });
3045
+ emit({ status: "sent" });
3046
+ const result = { railId: SOLVER_ONCHAIN_RAIL, swapId: swap.rfqId };
3047
+ if (!deps.awaitSettlement) return result;
3048
+ let txid;
3049
+ try {
3050
+ ({ txid } = await deps.awaitSettlement(swap));
3051
+ } catch (e) {
3052
+ console.warn(
3053
+ `${SOLVER_ONCHAIN_RAIL}: settlement watch failed; the payment is sent`,
3054
+ e
3055
+ );
3056
+ return result;
3057
+ }
3058
+ const settled = { ...result, txid };
3059
+ emit({ status: "settled", result: settled });
3060
+ return settled;
3061
+ })
3062
+ };
3063
+ }
3064
+ };
3065
+ }
3066
+
3067
+ // src/payment/solverLightning.ts
2822
3068
  var import_sdk12 = require("@arkade-os/sdk");
3069
+ var SOLVER_LIGHTNING_RAIL = "solver-lightning";
3070
+ var factsOf = (raw, decode, now) => {
3071
+ const invoice = (0, import_sdk12.invoiceTarget)(raw);
3072
+ if (!invoice) return void 0;
3073
+ let facts;
3074
+ try {
3075
+ facts = decode(invoice);
3076
+ } catch {
3077
+ return void 0;
3078
+ }
3079
+ if (!Number.isInteger(facts.amountSats) || facts.amountSats <= 0) return void 0;
3080
+ if (facts.expiresAt <= now) return void 0;
3081
+ return facts;
3082
+ };
3083
+ var solverLightningRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "lightning", amountSats, fallbackEmulatorPubkey);
3084
+ function solverLightningRail(deps) {
3085
+ const rendezvousFor = async (amountSats) => solverLightningRendezvous(await deps.discover(), amountSats, deps.fallbackEmulatorPubkey);
3086
+ return {
3087
+ id: SOLVER_LIGHTNING_RAIL,
3088
+ match: (req) => (0, import_sdk12.invoiceTarget)(req.raw) !== void 0,
3089
+ available: async (req) => {
3090
+ if ((0, import_sdk12.assetsOf)(req).length > 0) return false;
3091
+ const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
3092
+ if (!facts) return false;
3093
+ if (req.amount !== void 0 && req.amount !== facts.amountSats) return false;
3094
+ return await rendezvousFor(facts.amountSats) !== void 0;
3095
+ },
3096
+ quote: async (req, ctx) => {
3097
+ (0, import_sdk12.assertNoAssets)(SOLVER_LIGHTNING_RAIL, req);
3098
+ const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
3099
+ if (!facts) {
3100
+ throw new Error(
3101
+ `${SOLVER_LIGHTNING_RAIL}: the request carries no payable BOLT11 invoice (amountless, expired, or undecodable)`
3102
+ );
3103
+ }
3104
+ if (req.amount !== void 0 && req.amount !== facts.amountSats) {
3105
+ throw new Error(
3106
+ `${SOLVER_LIGHTNING_RAIL}: the request names ${req.amount} sats but the invoice is for ${facts.amountSats} \u2014 the payee is paid the invoice`
3107
+ );
3108
+ }
3109
+ const rendezvous = await rendezvousFor(facts.amountSats);
3110
+ if (!rendezvous) {
3111
+ throw new Error(
3112
+ `${SOLVER_LIGHTNING_RAIL}: no solver serves arkade:BTC -> lightning:BTC at ${facts.amountSats} sats`
3113
+ );
3114
+ }
3115
+ const negotiated = await deps.connect(
3116
+ rendezvous,
3117
+ (transport) => requestLightningSend(ctx.wallet, deps.arkServerUrl, transport, {
3118
+ invoice: facts,
3119
+ ...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
3120
+ })
3121
+ );
3122
+ const swap = { ...negotiated, invoice: facts, rendezvous };
3123
+ return {
3124
+ railId: SOLVER_LIGHTNING_RAIL,
3125
+ // `requestLightningSend` refuses a quote that reprices the
3126
+ // invoice, so the spread is a fee on top.
3127
+ amount: facts.amountSats,
3128
+ fee: swap.fundAmount - facts.amountSats,
3129
+ total: swap.fundAmount,
3130
+ meta: {
3131
+ rfqId: swap.rfqId,
3132
+ validUntil: swap.quote.valid_until,
3133
+ paymentHash: facts.paymentHash,
3134
+ invoiceExpiresAt: facts.expiresAt,
3135
+ solverPubkey: rendezvous.solverPubkey
3136
+ },
3137
+ send: async () => (0, import_sdk12.makeHandle)(SOLVER_LIGHTNING_RAIL, async (emit) => {
3138
+ assertFundable({
3139
+ quote: swap.quote,
3140
+ invoiceExpiresAt: facts.expiresAt,
3141
+ now: Math.floor(Date.now() / 1e3)
3142
+ });
3143
+ await deps.persist(swap);
3144
+ await ctx.wallet.send({
3145
+ address: swap.address,
3146
+ amount: swap.fundAmount
3147
+ });
3148
+ emit({ status: "sent" });
3149
+ const result = { railId: SOLVER_LIGHTNING_RAIL, swapId: swap.rfqId };
3150
+ if (!deps.awaitSettlement) return result;
3151
+ let preimage;
3152
+ try {
3153
+ ({ preimage } = await deps.awaitSettlement(swap));
3154
+ } catch (e) {
3155
+ console.warn(
3156
+ `${SOLVER_LIGHTNING_RAIL}: settlement watch failed; the payment is sent`,
3157
+ e
3158
+ );
3159
+ return result;
3160
+ }
3161
+ const settled = { ...result, ...preimage !== void 0 && { preimage } };
3162
+ emit({ status: "settled", result: settled });
3163
+ return settled;
3164
+ })
3165
+ };
3166
+ }
3167
+ };
3168
+ }
2823
3169
 
2824
- // src/refund.ts
2825
- var import_base11 = require("@scure/base");
3170
+ // src/payment/crossAsset.ts
3171
+ var import_sdk13 = require("@arkade-os/sdk");
3172
+ var CROSS_ASSET_RAIL = "cross-asset";
3173
+ var DEFAULT_CARRIER_SATS = 330;
3174
+ var parseAssetId = (assetId) => {
3175
+ try {
3176
+ return import_sdk13.asset.AssetId.fromString(assetId);
3177
+ } catch {
3178
+ return void 0;
3179
+ }
3180
+ };
3181
+ function crossAssetRail(deps) {
3182
+ const planFor = async (asset3) => {
3183
+ const markets = await deps.discover();
3184
+ const found = findMarket(markets, BTC_ASSET_ID, asset3.assetId);
3185
+ const market = found?.market;
3186
+ if (!market) return void 0;
3187
+ const plan = await deps.quote(market, found.give, asset3.amount);
3188
+ const [balance, dust] = await Promise.all([deps.btcBalance(), deps.dust()]);
3189
+ return { plan, error: validatePlan(plan, balance, dust) };
3190
+ };
3191
+ return {
3192
+ id: CROSS_ASSET_RAIL,
3193
+ match: (req) => (0, import_sdk13.arkTarget)(req.raw) !== void 0,
3194
+ available: async (req) => {
3195
+ const assets = (0, import_sdk13.assetsOf)(req);
3196
+ if (assets.length !== 1) return false;
3197
+ const [asset3] = assets;
3198
+ if (asset3.assetId === BTC_ASSET_ID) return false;
3199
+ if (typeof asset3.amount !== "bigint" || asset3.amount <= 0n) return false;
3200
+ if (!parseAssetId(asset3.assetId)) return false;
3201
+ const planned = await planFor(asset3);
3202
+ return planned !== void 0 && planned.error === void 0;
3203
+ },
3204
+ quote: async (req, ctx) => {
3205
+ const payTo = (0, import_sdk13.arkTarget)(req.raw);
3206
+ const asset3 = (0, import_sdk13.resolveAssetAmount)(CROSS_ASSET_RAIL, req);
3207
+ const wantAsset = parseAssetId(asset3.assetId);
3208
+ if (!wantAsset) {
3209
+ throw new Error(
3210
+ `${CROSS_ASSET_RAIL}: ${asset3.assetId} is not an asset id (expected 34 bytes of hex)`
3211
+ );
3212
+ }
3213
+ const planned = await planFor(asset3);
3214
+ if (!planned) {
3215
+ throw new Error(`${CROSS_ASSET_RAIL}: no market swaps BTC into ${asset3.assetId}`);
3216
+ }
3217
+ if (planned.error) {
3218
+ throw new Error(
3219
+ `${CROSS_ASSET_RAIL}: cannot swap into ${asset3.assetId} \u2014 ${planned.error}`
3220
+ );
3221
+ }
3222
+ const { plan } = planned;
3223
+ if (!Number.isSafeInteger(Number(plan.deposit.atomic))) {
3224
+ throw new Error(
3225
+ `${CROSS_ASSET_RAIL}: deposit of ${plan.deposit.atomic} does not fit a JS number`
3226
+ );
3227
+ }
3228
+ const depositSats = Number(plan.deposit.atomic);
3229
+ const carrier = deps.carrierSats ?? DEFAULT_CARRIER_SATS;
3230
+ return {
3231
+ railId: CROSS_ASSET_RAIL,
3232
+ // OPEN QUESTION for review: `total = amount + fee` forces the
3233
+ // purchase price into `fee`, which reads as a service charge.
3234
+ // A third field instead? `assets.spent` labels it correctly.
3235
+ amount: carrier,
3236
+ fee: depositSats,
3237
+ total: depositSats + carrier,
3238
+ assets: {
3239
+ delivered: asset3,
3240
+ spent: { assetId: BTC_ASSET_ID, amount: plan.deposit.atomic }
3241
+ },
3242
+ meta: {
3243
+ market: plan.market.pair,
3244
+ priceDisplay: plan.priceDisplay,
3245
+ give: plan.give
3246
+ },
3247
+ send: async () => (0, import_sdk13.makeHandle)(CROSS_ASSET_RAIL, async (emit) => {
3248
+ const offer = await createOffer(ctx.wallet, deps.arkServerUrl, {
3249
+ wantAmount: asset3.amount,
3250
+ wantAsset,
3251
+ ...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
3252
+ });
3253
+ const swap = {
3254
+ phase: "quoted",
3255
+ offerHex: offer.offerHex,
3256
+ address: offer.address,
3257
+ swapPkScript: offer.swapPkScript,
3258
+ depositSats,
3259
+ asset: asset3,
3260
+ payTo,
3261
+ plan
3262
+ };
3263
+ await deps.persist(swap);
3264
+ await ctx.wallet.send({
3265
+ address: offer.address,
3266
+ amount: depositSats,
3267
+ extensions: [offer.extension]
3268
+ });
3269
+ emit({ status: "sent" });
3270
+ await deps.awaitFill(swap);
3271
+ await deps.persist({ ...swap, phase: "filled" });
3272
+ await deps.persist({ ...swap, phase: "paying" });
3273
+ const txid = await ctx.wallet.send({
3274
+ address: payTo,
3275
+ amount: carrier,
3276
+ assets: [asset3]
3277
+ });
3278
+ await deps.persist({ ...swap, phase: "settled", txid });
3279
+ const result = {
3280
+ railId: CROSS_ASSET_RAIL,
3281
+ txid,
3282
+ swapId: offer.offerHex
3283
+ };
3284
+ emit({ status: "settled", result });
3285
+ return result;
3286
+ })
3287
+ };
3288
+ }
3289
+ };
3290
+ }
3291
+
3292
+ // src/reveal.ts
3293
+ var import_base13 = require("@scure/base");
3294
+ var import_legacy3 = require("@noble/hashes/legacy.js");
2826
3295
  var import_sha24 = require("@noble/hashes/sha2.js");
2827
- var import_sdk11 = require("@arkade-os/sdk");
3296
+ var import_sdk14 = require("@arkade-os/sdk");
3297
+ var CovclaimdRevealError = class extends Error {
3298
+ name = "CovclaimdRevealError";
3299
+ status;
3300
+ detail;
3301
+ retryable;
3302
+ constructor(what, status, detail) {
3303
+ super(`covclaimd ${what} failed: ${status || "no response"} ${detail}`.trimEnd());
3304
+ this.status = status;
3305
+ this.detail = detail;
3306
+ this.retryable = status === 0 || status === 429 || status >= 500;
3307
+ }
3308
+ };
3309
+ var decodeKey = (value, field) => {
3310
+ if (typeof value !== "string") throw new Error(`covclaimd info: ${field} is not a string`);
3311
+ let bytes;
3312
+ try {
3313
+ bytes = import_base13.hex.decode(value);
3314
+ } catch {
3315
+ throw new Error(`covclaimd info: ${field} is not hex`);
3316
+ }
3317
+ if (bytes.length !== 33) {
3318
+ throw new Error(`covclaimd info: ${field} must be 33-byte compressed, got ${bytes.length}`);
3319
+ }
3320
+ return bytes;
3321
+ };
3322
+ var LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
3323
+ var assertTransportSecure = (baseUrl, allowInsecureHttp) => {
3324
+ const { protocol, hostname } = new URL(baseUrl);
3325
+ if (protocol === "https:" || allowInsecureHttp) return;
3326
+ if (protocol === "http:" && LOOPBACK.has(hostname)) return;
3327
+ throw new Error(
3328
+ `covclaimd URL must be https (got ${protocol}//${hostname}): the key it serves is the one the preimage gets sealed to. Pass allowInsecureHttp to override.`
3329
+ );
3330
+ };
3331
+ var covclaimdClient = (baseUrl, options = {}) => {
3332
+ assertTransportSecure(baseUrl, options.allowInsecureHttp === true);
3333
+ const url = baseUrl.replace(/\/+$/, "");
3334
+ const fetchImpl = options.fetchImpl ?? fetch;
3335
+ const timeoutMs = options.timeoutMs ?? 3e4;
3336
+ const send = async (what, path, init) => {
3337
+ let response;
3338
+ try {
3339
+ response = await fetchImpl(`${url}${path}`, {
3340
+ ...init,
3341
+ // a redirect would move the key fetch off the vetted origin
3342
+ redirect: "error",
3343
+ signal: AbortSignal.timeout(timeoutMs)
3344
+ });
3345
+ } catch (error) {
3346
+ throw new CovclaimdRevealError(what, 0, String(error));
3347
+ }
3348
+ if (!response.ok) {
3349
+ throw new CovclaimdRevealError(what, response.status, await response.text());
3350
+ }
3351
+ return response;
3352
+ };
3353
+ const readJson = async (what, response) => {
3354
+ const body = await response.text();
3355
+ try {
3356
+ return JSON.parse(body);
3357
+ } catch {
3358
+ throw new CovclaimdRevealError(
3359
+ what,
3360
+ response.status,
3361
+ `non-JSON body: ${body.slice(0, 200)}`
3362
+ );
3363
+ }
3364
+ };
3365
+ return {
3366
+ async info() {
3367
+ const response = await send("info", "/v1/preimage/covclaimd-pubkey", {
3368
+ method: "GET"
3369
+ });
3370
+ const body = await readJson("info", response);
3371
+ return {
3372
+ covclaimdPubkey: decodeKey(body?.covclaimd_pub_key, "covclaimd_pub_key"),
3373
+ emulatorPubkey: import_base13.hex.encode(decodeKey(body?.emulator_pub_key, "emulator_pub_key"))
3374
+ };
3375
+ },
3376
+ async reveal(params) {
3377
+ await send("reveal", "/v1/reveal", {
3378
+ method: "POST",
3379
+ headers: { "content-type": "application/json" },
3380
+ body: JSON.stringify({
3381
+ swap_address: params.swapAddress,
3382
+ packet: { ciphertext: params.ciphertext, arkade_script: params.arkadeScript },
3383
+ taptree: params.taptree
3384
+ })
3385
+ });
3386
+ }
3387
+ };
3388
+ };
3389
+ function revealFieldsFromScript(script) {
3390
+ if (!script.nonInteractiveClaimArkadeScript) {
3391
+ throw new Error("this covenant has no non-interactive claim leaf to reveal against");
3392
+ }
3393
+ return {
3394
+ arkadeScript: import_base13.base64.encode(script.nonInteractiveClaimArkadeScript),
3395
+ taptree: import_base13.hex.encode(script.encode())
3396
+ };
3397
+ }
3398
+ async function revealClaimPacket(client, input) {
3399
+ const fields = revealFieldsFromScript(input.script);
3400
+ if (import_base13.hex.encode(import_sdk14.ArkAddress.decode(input.address).pkScript) !== import_base13.hex.encode(input.script.pkScript)) {
3401
+ throw new Error("address does not belong to this covenant");
3402
+ }
3403
+ if (import_base13.hex.encode((0, import_legacy3.ripemd160)((0, import_sha24.sha256)(input.preimage))) !== import_base13.hex.encode(input.script.options.preimageHash)) {
3404
+ throw new Error("preimage does not match the covenant's payment hash");
3405
+ }
3406
+ const { ciphertext } = await sealClaimPacket({
3407
+ preimage: input.preimage,
3408
+ covclaimdPubkey: (await client.info()).covclaimdPubkey
3409
+ });
3410
+ await client.reveal({ swapAddress: input.address, ciphertext, ...fields });
3411
+ return { ciphertext };
3412
+ }
3413
+
3414
+ // src/claim.ts
3415
+ var import_base15 = require("@scure/base");
3416
+ var import_legacy4 = require("@noble/hashes/legacy.js");
3417
+ var import_sha26 = require("@noble/hashes/sha2.js");
3418
+ var import_sdk16 = require("@arkade-os/sdk");
3419
+
3420
+ // src/refund.ts
3421
+ var import_base14 = require("@scure/base");
3422
+ var import_sha25 = require("@noble/hashes/sha2.js");
3423
+ var import_sdk15 = require("@arkade-os/sdk");
2828
3424
  var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2829
3425
  var isRfqTerminal = (state) => RFQ_TERMINAL_STATES.includes(state);
2830
3426
  var RFQ_RESOLVED_STATES = ["settled", "refunded"];
@@ -2871,7 +3467,7 @@ var LockupNeedsRecoveryError = class extends Error {
2871
3467
  }
2872
3468
  };
2873
3469
  async function findLockupVtxos(indexer, swapPkScript) {
2874
- const scripts = [import_base11.hex.encode(swapPkScript)];
3470
+ const scripts = [import_base14.hex.encode(swapPkScript)];
2875
3471
  const [spendable, recoverable] = await Promise.all([
2876
3472
  indexer.getVtxos({ scripts, spendableOnly: true }),
2877
3473
  indexer.getVtxos({ scripts, recoverableOnly: true })
@@ -2897,16 +3493,16 @@ async function findLockupVtxos(indexer, swapPkScript) {
2897
3493
  }
2898
3494
  return out;
2899
3495
  }
2900
- var hashesTo = (candidate, paymentHash) => import_base11.hex.encode((0, import_sha24.sha256)(candidate)) === paymentHash;
3496
+ var hashesTo = (candidate, paymentHash) => import_base14.hex.encode((0, import_sha25.sha256)(candidate)) === paymentHash;
2901
3497
  var candidateWitnessItems = (tx, inputIndex) => [
2902
- ...(0, import_sdk11.getArkPsbtFields)(tx, inputIndex, import_sdk11.ConditionWitness).flat(),
3498
+ ...(0, import_sdk15.getArkPsbtFields)(tx, inputIndex, import_sdk15.ConditionWitness).flat(),
2903
3499
  ...tx.getInput(inputIndex).finalScriptWitness ?? []
2904
3500
  ];
2905
3501
  async function readLockupFate(indexer, input) {
2906
- const { vtxos } = await indexer.getVtxos({ scripts: [import_base11.hex.encode(input.swapPkScript)] });
3502
+ const { vtxos } = await indexer.getVtxos({ scripts: [import_base14.hex.encode(input.swapPkScript)] });
2907
3503
  const all = vtxos ?? [];
2908
3504
  if (all.length === 0) return { fate: "unknown" };
2909
- const exited = all.filter((vtxo) => vtxo.isUnrolled && !(0, import_sdk11.hasTerminalSpend)(vtxo));
3505
+ const exited = all.filter((vtxo) => vtxo.isUnrolled && !(0, import_sdk15.hasTerminalSpend)(vtxo));
2910
3506
  if (exited.length > 0) {
2911
3507
  return {
2912
3508
  fate: "exited",
@@ -2916,7 +3512,7 @@ async function readLockupFate(indexer, input) {
2916
3512
  const spentBy = /* @__PURE__ */ new Map();
2917
3513
  let everySpendNamed = true;
2918
3514
  for (const vtxo of all) {
2919
- if (!(0, import_sdk11.hasTerminalSpend)(vtxo)) return { fate: "open" };
3515
+ if (!(0, import_sdk15.hasTerminalSpend)(vtxo)) return { fate: "open" };
2920
3516
  if (vtxo.spentBy)
2921
3517
  spentBy.set(vtxo.spentBy, {
2922
3518
  checkpointTxid: vtxo.spentBy,
@@ -2930,7 +3526,7 @@ async function readLockupFate(indexer, input) {
2930
3526
  for (const raw of txs) {
2931
3527
  let tx;
2932
3528
  try {
2933
- tx = import_sdk11.Transaction.fromPSBT(import_base11.base64.decode(raw));
3529
+ tx = import_sdk15.Transaction.fromPSBT(import_base14.base64.decode(raw));
2934
3530
  } catch {
2935
3531
  continue;
2936
3532
  }
@@ -2938,7 +3534,7 @@ async function readLockupFate(indexer, input) {
2938
3534
  for (let i = 0; i < tx.inputsLength; i++) {
2939
3535
  const spent = tx.getInput(i);
2940
3536
  if (!spent.txid) continue;
2941
- const txid = import_base11.hex.encode(spent.txid);
3537
+ const txid = import_base14.hex.encode(spent.txid);
2942
3538
  if (!all.some((vtxo) => vtxo.txid === txid && vtxo.vout === spent.index)) continue;
2943
3539
  for (const candidate of candidateWitnessItems(tx, i)) {
2944
3540
  if (hashesTo(candidate, input.paymentHash)) {
@@ -2967,14 +3563,14 @@ async function pushRefundWithoutReceiver(ark, input) {
2967
3563
  const info = await ark.getInfo();
2968
3564
  let serverUnrollScript;
2969
3565
  try {
2970
- serverUnrollScript = import_sdk11.CSVMultisigTapscript.decode(import_base11.hex.decode(info.checkpointTapscript));
3566
+ serverUnrollScript = import_sdk15.CSVMultisigTapscript.decode(import_base14.hex.decode(info.checkpointTapscript));
2971
3567
  } catch {
2972
3568
  throw new Error("invalid checkpointTapscript from the Arkade server");
2973
3569
  }
2974
3570
  const leaf = input.script.refundWithoutReceiver();
2975
3571
  const tapTree = input.script.encode();
2976
3572
  const amount = input.vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
2977
- const { arkTx, checkpoints } = (0, import_sdk11.buildOffchainTx)(
3573
+ const { arkTx, checkpoints } = (0, import_sdk15.buildOffchainTx)(
2978
3574
  input.vtxos.map((vtxo) => ({
2979
3575
  txid: vtxo.txid,
2980
3576
  vout: vtxo.vout,
@@ -2987,18 +3583,18 @@ async function pushRefundWithoutReceiver(ark, input) {
2987
3583
  );
2988
3584
  const signedArkTx = await input.sender.sign(arkTx);
2989
3585
  const submitted = await ark.submitTx(
2990
- import_base11.base64.encode(signedArkTx.toPSBT()),
2991
- checkpoints.map((c) => import_base11.base64.encode(c.toPSBT()))
3586
+ import_base14.base64.encode(signedArkTx.toPSBT()),
3587
+ checkpoints.map((c) => import_base14.base64.encode(c.toPSBT()))
2992
3588
  );
2993
- (0, import_sdk11.assertSubmittedArkTxid)(submitted, signedArkTx, "refundWithoutReceiver");
2994
- const matched = (0, import_sdk11.matchServerCheckpoints)(
3589
+ (0, import_sdk15.assertSubmittedArkTxid)(submitted, signedArkTx, "refundWithoutReceiver");
3590
+ const matched = (0, import_sdk15.matchServerCheckpoints)(
2995
3591
  submitted.signedCheckpointTxs,
2996
3592
  checkpoints,
2997
3593
  "refundWithoutReceiver"
2998
3594
  );
2999
3595
  const finalCheckpoints = await Promise.all(
3000
3596
  matched.map(
3001
- async ({ server }) => import_base11.base64.encode((await input.sender.sign(server, [0])).toPSBT())
3597
+ async ({ server }) => import_base14.base64.encode((await input.sender.sign(server, [0])).toPSBT())
3002
3598
  )
3003
3599
  );
3004
3600
  await ark.finalizeTx(submitted.arkTxid, finalCheckpoints);
@@ -3093,20 +3689,20 @@ async function pushClaim(ark, input) {
3093
3689
  }
3094
3690
  }
3095
3691
  const committed = input.script.options.preimageHash;
3096
- if (import_base12.hex.encode((0, import_legacy3.ripemd160)((0, import_sha25.sha256)(input.preimage))) !== import_base12.hex.encode(committed)) {
3692
+ if (import_base15.hex.encode((0, import_legacy4.ripemd160)((0, import_sha26.sha256)(input.preimage))) !== import_base15.hex.encode(committed)) {
3097
3693
  throw new Error("preimage does not match the covenant's payment hash");
3098
3694
  }
3099
3695
  const info = await ark.getInfo();
3100
3696
  let serverUnrollScript;
3101
3697
  try {
3102
- serverUnrollScript = import_sdk12.CSVMultisigTapscript.decode(import_base12.hex.decode(info.checkpointTapscript));
3698
+ serverUnrollScript = import_sdk16.CSVMultisigTapscript.decode(import_base15.hex.decode(info.checkpointTapscript));
3103
3699
  } catch {
3104
3700
  throw new Error("invalid checkpointTapscript from the Arkade server");
3105
3701
  }
3106
3702
  const leaf = input.script.claim();
3107
3703
  const tapTree = input.script.encode();
3108
- const arkTxid = await (0, import_sdk12.signAndSubmitOffchainTx)({
3109
- identity: (0, import_sdk12.claimWithPreimageIdentity)(input.receiver, input.preimage),
3704
+ const arkTxid = await (0, import_sdk16.signAndSubmitOffchainTx)({
3705
+ identity: (0, import_sdk16.claimWithPreimageIdentity)(input.receiver, input.preimage),
3110
3706
  provider: ark,
3111
3707
  inputs: input.vtxos.map((vtxo) => ({
3112
3708
  txid: vtxo.txid,
@@ -3153,7 +3749,7 @@ async function claimReceiveLockup(indexer, ark, input) {
3153
3749
  }
3154
3750
 
3155
3751
  // src/refundBlocked.ts
3156
- var import_sdk13 = require("@arkade-os/sdk");
3752
+ var import_sdk17 = require("@arkade-os/sdk");
3157
3753
  var RefundNotLocallyPossibleError = class extends Error {
3158
3754
  constructor(reason, message, options) {
3159
3755
  super(message, options);
@@ -3170,16 +3766,16 @@ async function senderIdentityForSwapRecord(wallet, record) {
3170
3766
  );
3171
3767
  }
3172
3768
  try {
3173
- return await (0, import_sdk13.contractSigner)(wallet, record.signingDescriptor);
3769
+ return await (0, import_sdk17.contractSigner)(wallet, record.signingDescriptor);
3174
3770
  } catch (cause) {
3175
- if (cause instanceof import_sdk13.WalletCannotSignError) {
3771
+ if (cause instanceof import_sdk17.WalletCannotSignError) {
3176
3772
  throw new RefundNotLocallyPossibleError(
3177
3773
  "unsignable-wallet",
3178
3774
  `this wallet holds ${record.signingDescriptor} but cannot sign with it; attach its signer`,
3179
3775
  { cause }
3180
3776
  );
3181
3777
  }
3182
- if (cause instanceof import_sdk13.ForeignDescriptorError) {
3778
+ if (cause instanceof import_sdk17.ForeignDescriptorError) {
3183
3779
  throw new RefundNotLocallyPossibleError(
3184
3780
  "foreign-descriptor",
3185
3781
  `this wallet cannot derive ${record.signingDescriptor}; the swap was created on another wallet`,
@@ -3214,7 +3810,7 @@ function arkadeRefunder(deps) {
3214
3810
  }
3215
3811
 
3216
3812
  // src/swapManager.ts
3217
- var import_base13 = require("@scure/base");
3813
+ var import_base16 = require("@scure/base");
3218
3814
  function nextOnchainAction(input) {
3219
3815
  switch (input.phase.phase) {
3220
3816
  case "unfunded":
@@ -3662,7 +4258,7 @@ var RfqSwapManager = class {
3662
4258
  // ── internals ────────────────────────────────────────────────────────────
3663
4259
  track(swap) {
3664
4260
  this.monitored.set(swap.rfqId, swap);
3665
- this.byLockupScript.set(import_base13.hex.encode(swap.lockupPkScript), swap);
4261
+ this.byLockupScript.set(import_base16.hex.encode(swap.lockupPkScript), swap);
3666
4262
  }
3667
4263
  /** Drops the swap from BOTH indexes. The event index is the one that stops
3668
4264
  * a late event finding a swap that is gone; `pollSwap`'s own
@@ -3671,7 +4267,7 @@ var RfqSwapManager = class {
3671
4267
  * them from silently re-driving a cancelled swap. */
3672
4268
  untrack(rfqId) {
3673
4269
  const swap = this.monitored.get(rfqId);
3674
- if (swap) this.byLockupScript.delete(import_base13.hex.encode(swap.lockupPkScript));
4270
+ if (swap) this.byLockupScript.delete(import_base16.hex.encode(swap.lockupPkScript));
3675
4271
  this.monitored.delete(rfqId);
3676
4272
  this.refundRefused.delete(rfqId);
3677
4273
  this.lastClaimError.delete(rfqId);
@@ -3733,7 +4329,7 @@ var RfqSwapManager = class {
3733
4329
  if (!lockup) {
3734
4330
  try {
3735
4331
  const [existing] = await contracts.getContracts({
3736
- script: import_base13.hex.encode(swap.lockupPkScript)
4332
+ script: import_base16.hex.encode(swap.lockupPkScript)
3737
4333
  });
3738
4334
  if (existing) {
3739
4335
  this.registered.set(swap.rfqId, true);
@@ -3752,13 +4348,13 @@ var RfqSwapManager = class {
3752
4348
  );
3753
4349
  return;
3754
4350
  }
3755
- const script = import_base13.hex.encode(lockup.script.pkScript);
3756
- if (script !== import_base13.hex.encode(swap.lockupPkScript)) {
4351
+ const script = import_base16.hex.encode(lockup.script.pkScript);
4352
+ if (script !== import_base16.hex.encode(swap.lockupPkScript)) {
3757
4353
  this.registered.set(swap.rfqId, false);
3758
4354
  this.emitFailed(
3759
4355
  swap,
3760
4356
  new Error(
3761
- `swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${import_base13.hex.encode(swap.lockupPkScript)}`
4357
+ `swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${import_base16.hex.encode(swap.lockupPkScript)}`
3762
4358
  )
3763
4359
  );
3764
4360
  return;
@@ -3777,7 +4373,7 @@ var RfqSwapManager = class {
3777
4373
  * script for its whole life. Best-effort — the swap is over either way. */
3778
4374
  retireContract(swap) {
3779
4375
  if (!this.deps.contracts || !this.registered.get(swap.rfqId)) return;
3780
- void this.deps.contracts.setContractWatchState(import_base13.hex.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
4376
+ void this.deps.contracts.setContractWatchState(import_base16.hex.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
3781
4377
  }
3782
4378
  arm() {
3783
4379
  if (!this.running) return;
@@ -4250,8 +4846,8 @@ var errorMessage = (error) => error instanceof Error ? error.message : String(er
4250
4846
  var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
4251
4847
 
4252
4848
  // src/activity.ts
4253
- var import_sdk14 = require("@arkade-os/sdk");
4254
- var import_base14 = require("@scure/base");
4849
+ var import_sdk18 = require("@arkade-os/sdk");
4850
+ var import_base17 = require("@scure/base");
4255
4851
  var LABELS = {
4256
4852
  lightning_send: "Lightning send",
4257
4853
  lightning_receive: "Lightning receive",
@@ -4326,7 +4922,7 @@ async function activityInputOf(record, indexer) {
4326
4922
  async function lockupTxids(indexer, record, wantFunding) {
4327
4923
  let script;
4328
4924
  try {
4329
- script = import_base14.hex.encode(import_sdk14.ArkAddress.decode(record.lockupAddress).pkScript);
4925
+ script = import_base17.hex.encode(import_sdk18.ArkAddress.decode(record.lockupAddress).pkScript);
4330
4926
  } catch {
4331
4927
  return [];
4332
4928
  }
@@ -4348,6 +4944,8 @@ async function lockupTxids(indexer, record, wantFunding) {
4348
4944
  ARKADE_BTC,
4349
4945
  AddressMismatch,
4350
4946
  BTC_ASSET_ID,
4947
+ CROSS_ASSET_RAIL,
4948
+ CovclaimdRevealError,
4351
4949
  InMemoryAssetSwapRepository,
4352
4950
  IndexedDbAssetSwapRepository,
4353
4951
  LIGHTNING_BTC,
@@ -4380,6 +4978,8 @@ async function lockupTxids(indexer, record, wantFunding) {
4380
4978
  RfqSwapManager,
4381
4979
  RfqSwapOriginRequired,
4382
4980
  SOLO_REFUND_HEADROOM_SECONDS,
4981
+ SOLVER_LIGHTNING_RAIL,
4982
+ SOLVER_ONCHAIN_RAIL,
4383
4983
  SWAP_LOCKUP_CONTRACT_KIND,
4384
4984
  SWAP_LOCKUP_CONTRACT_LABEL,
4385
4985
  SWAP_LOCKUP_CONTRACT_TYPE,
@@ -4396,13 +4996,16 @@ async function lockupTxids(indexer, record, wantFunding) {
4396
4996
  buildHtlcClaim,
4397
4997
  buildHtlcRefund,
4398
4998
  cancelOffer,
4999
+ chainSourceFrom,
4399
5000
  claimOnchainFill,
4400
5001
  claimReceiveLockup,
4401
5002
  classifyDepositSpend,
4402
5003
  classifyOnchainHtlc,
4403
5004
  classifySpend,
5005
+ covclaimdClient,
4404
5006
  createOffer,
4405
5007
  createRfqSwapRecord,
5008
+ crossAssetRail,
4406
5009
  decodeOffer,
4407
5010
  deriveLightningReceive,
4408
5011
  deriveOnchainReceive,
@@ -4417,6 +5020,7 @@ async function lockupTxids(indexer, record, wantFunding) {
4417
5020
  httpTransport,
4418
5021
  isRfqSwapTerminal,
4419
5022
  isRfqTerminal,
5023
+ l1ScriptForAddress,
4420
5024
  lightningReceiveRequest,
4421
5025
  lightningSendRequest,
4422
5026
  lightningSendVtxoScript,
@@ -4447,6 +5051,8 @@ async function lockupTxids(indexer, record, wantFunding) {
4447
5051
  requestOnchainSend,
4448
5052
  restoreAssetSwaps,
4449
5053
  retireSettledOfferContracts,
5054
+ revealClaimPacket,
5055
+ revealFieldsFromScript,
4450
5056
  rfqClaimSecretOf,
4451
5057
  rfqPair,
4452
5058
  rfqSecretsProfile,
@@ -4456,6 +5062,11 @@ async function lockupTxids(indexer, record, wantFunding) {
4456
5062
  sealClaimPacket,
4457
5063
  senderIdentityForSwapRecord,
4458
5064
  shouldRetainRfqSwap,
5065
+ solverLightningRail,
5066
+ solverLightningRendezvous,
5067
+ solverOnchainRail,
5068
+ solverOnchainRendezvous,
5069
+ solverRendezvous,
4459
5070
  spendTxidsOf,
4460
5071
  spendUpdate,
4461
5072
  swapActivityResolver,