@arkade-os/swap 0.0.17 → 0.0.19

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
@@ -84,6 +84,7 @@ __export(index_exports, {
84
84
  arkadeAssetLeg: () => arkadeAssetLeg,
85
85
  arkadeRefunder: () => arkadeRefunder,
86
86
  arkadeSwapRequest: () => arkadeSwapRequest,
87
+ assertArkadeFundable: () => assertArkadeFundable,
87
88
  assertFundable: () => assertFundable,
88
89
  assertReceivable: () => assertReceivable,
89
90
  awaitLockupFunding: () => awaitLockupFunding,
@@ -92,6 +93,7 @@ __export(index_exports, {
92
93
  buildHtlcClaim: () => buildHtlcClaim,
93
94
  buildHtlcRefund: () => buildHtlcRefund,
94
95
  cancelOffer: () => cancelOffer,
96
+ canonicalAssetAmount: () => canonicalAssetAmount,
95
97
  chainSourceFrom: () => chainSourceFrom,
96
98
  claimOnchainFill: () => claimOnchainFill,
97
99
  claimPacketShape: () => claimPacketShape,
@@ -132,6 +134,8 @@ __export(index_exports, {
132
134
  newPreimage: () => newPreimage,
133
135
  newRfqId: () => newRfqId,
134
136
  nextOnchainAction: () => nextOnchainAction,
137
+ normalizeMakerPkScript: () => normalizeMakerPkScript,
138
+ normalizeMakerPublicKey: () => normalizeMakerPublicKey,
135
139
  offerTermsFromQuote: () => offerTermsFromQuote,
136
140
  offerVtxoScript: () => offerVtxoScript,
137
141
  onchainHtlcScript: () => onchainHtlcScript,
@@ -149,6 +153,7 @@ __export(index_exports, {
149
153
  registerAssetSwapRestore: () => registerAssetSwapRestore,
150
154
  registerLockupContract: () => registerLockupContract,
151
155
  relayTransport: () => relayTransport,
156
+ requestArkadeSwap: () => requestArkadeSwap,
152
157
  requestLightningReceive: () => requestLightningReceive,
153
158
  requestLightningSend: () => requestLightningSend,
154
159
  requestOnchainReceive: () => requestOnchainReceive,
@@ -186,6 +191,7 @@ __export(index_exports, {
186
191
  updateRfqSwapRecord: () => updateRfqSwapRecord,
187
192
  validatePlan: () => validatePlan,
188
193
  verifyLockupAddress: () => verifyLockupAddress,
194
+ verifyOfferAddress: () => verifyOfferAddress,
189
195
  verifyReceiveInvoice: () => verifyReceiveInvoice,
190
196
  watchOfferSwaps: () => watchOfferSwaps
191
197
  });
@@ -1064,12 +1070,14 @@ var discoverMarkets = async (options) => {
1064
1070
  fetchImpl,
1065
1071
  useCache = true
1066
1072
  } = options;
1067
- if (!registry || !(0, import_solver_discovery.isNetwork)(network)) return [];
1068
- const cached = repository && await readMarketsCache(repository, network, registry);
1073
+ if (!(0, import_solver_discovery.isNetwork)(network)) return [];
1074
+ const registries = registry ? [registry] : void 0;
1075
+ const registryKey = registries?.[0] ?? (0, import_solver_discovery.registryIndexUrl)(network);
1076
+ const cached = repository && await readMarketsCache(repository, network, registryKey);
1069
1077
  if (useCache && cached && Date.now() - cached.fetchedAt < MARKETS_CACHE_TTL_MS)
1070
1078
  return cached.markets;
1071
1079
  const { markets, sources, warnings } = await (0, import_solver_discovery.discover)({
1072
- registries: [registry],
1080
+ registries,
1073
1081
  localCards,
1074
1082
  network,
1075
1083
  fetchImpl
@@ -1079,7 +1087,7 @@ var discoverMarkets = async (options) => {
1079
1087
  if (!reachable && cached) return cached.markets;
1080
1088
  if (reachable && repository) {
1081
1089
  try {
1082
- await repository.saveCachedMarkets(network, registry, {
1090
+ await repository.saveCachedMarkets(network, registryKey, {
1083
1091
  markets,
1084
1092
  fetchedAt: Date.now()
1085
1093
  });
@@ -1980,6 +1988,7 @@ function onchainSendProfile(result) {
1980
1988
  network: result.l1Network,
1981
1989
  htlcAddress: result.htlc.address,
1982
1990
  minConfirmations: result.minConfirmations,
1991
+ expectedAmount: result.expectedAmount,
1983
1992
  payoutPkScript: import_base5.hex.encode(result.payoutPkScript)
1984
1993
  };
1985
1994
  }
@@ -1991,6 +2000,7 @@ var OnchainSendCorridor = {
1991
2000
  project: (swap) => {
1992
2001
  const send = swap;
1993
2002
  return {
2003
+ expectedAmount: send.expectedAmount,
1994
2004
  ...send.funding ? { funding: send.funding } : {},
1995
2005
  ...send.claimTxid ? { claimTxid: send.claimTxid } : {}
1996
2006
  };
@@ -2007,6 +2017,11 @@ var OnchainSendCorridor = {
2007
2017
  `onchain_send record carries no usable minConfirmations (${String(profile.minConfirmations)}); the confirmation gate cannot be checked \u2014 refusing to restore a swap that would claim an unconfirmed fill`
2008
2018
  );
2009
2019
  }
2020
+ if (!Number.isSafeInteger(profile.expectedAmount) || profile.expectedAmount <= 0) {
2021
+ throw new Error(
2022
+ `onchain_send record carries no usable expectedAmount (${String(profile.expectedAmount)}); the funded value cannot be checked \u2014 refusing to restore a swap that would claim an underfunded fill`
2023
+ );
2024
+ }
2010
2025
  const htlc = onchainHtlcScript(
2011
2026
  {
2012
2027
  // From the profile, not the covenant: the lockup commits to
@@ -2029,6 +2044,7 @@ var OnchainSendCorridor = {
2029
2044
  paymentHash,
2030
2045
  htlc,
2031
2046
  minConfirmations: profile.minConfirmations,
2047
+ expectedAmount: profile.expectedAmount,
2032
2048
  // Optional here, required at the write: throwing on an older
2033
2049
  // record would strand the refund it is still owed.
2034
2050
  ...profile.payoutPkScript ? { payoutPkScript: import_base5.hex.decode(profile.payoutPkScript) } : {},
@@ -2419,6 +2435,12 @@ var solverHex = (value, field) => {
2419
2435
  throw new Error(`solver sent malformed hex for ${field}`);
2420
2436
  }
2421
2437
  };
2438
+ var quoteSats = (value, field) => {
2439
+ if (typeof value !== "number" || !Number.isSafeInteger(value)) {
2440
+ throw new Error(`HTLC quote carries a non-sats ${field}: ${String(value)}`);
2441
+ }
2442
+ return value;
2443
+ };
2422
2444
  var ARKADE_BTC = "arkade:BTC";
2423
2445
  var LIGHTNING_BTC = "lightning:BTC";
2424
2446
  var ONCHAIN_BTC = "onchain:BTC";
@@ -2530,12 +2552,12 @@ var lightningSendRequest = (input) => ({
2530
2552
  var arkadeSwapRequest = (input) => {
2531
2553
  if (!input.wantAsset && !input.offerAsset) {
2532
2554
  throw new Error(
2533
- "set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC) \u2014 with neither set both legs are BTC, which is not a swap"
2555
+ "set at least one of wantAsset or offerAsset \u2014 with neither set both legs are BTC, which is not a swap"
2534
2556
  );
2535
2557
  }
2536
- if (input.wantAsset && input.offerAsset) {
2558
+ if (input.amountSide !== void 0 && input.amountSide !== "from") {
2537
2559
  throw new Error(
2538
- "set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC) \u2014 asset->asset is nameable on the wire but no solver quotes it yet"
2560
+ 'arkade\u2194arkade quotes are exact-in only: use amountSide "from" with the deposit amount'
2539
2561
  );
2540
2562
  }
2541
2563
  const pair = rfqPair(
@@ -2548,16 +2570,59 @@ var arkadeSwapRequest = (input) => {
2548
2570
  type: "rfq_request",
2549
2571
  rfq_id: input.rfqId,
2550
2572
  pair,
2551
- amount_side: input.amountSide,
2552
- amount: input.amount,
2553
- // The pair is the only place the asset ids appear. Repeating them here
2554
- // would be a key the solver's `.strict()` profile schema does not
2555
- // declare, and an undeclared key is `unsupported_payload` — a refusal,
2556
- // not an ignored extra. Empty, not absent: `profile` is required on
2557
- // every other request shape this wire carries.
2558
- profile: {}
2573
+ amount_side: "from",
2574
+ amount: canonicalAssetAmount(input.amount),
2575
+ // The pair names the assets; the profile names the trader. Both are
2576
+ // covenant parameters, and the solver's `.strict()` schema refuses
2577
+ // anything else — an undeclared key is `unsupported_payload`.
2578
+ profile: {
2579
+ maker_pk_script: normalizeMakerPkScript(input.makerPkScript),
2580
+ maker_public_key: normalizeMakerPublicKey(input.makerPublicKey)
2581
+ }
2559
2582
  };
2560
2583
  };
2584
+ var canonicalAssetAmount = (amount) => {
2585
+ if (typeof amount === "bigint") {
2586
+ if (amount <= 0n) throw new Error(`amount must be positive, got ${amount}`);
2587
+ return amount.toString();
2588
+ }
2589
+ if (typeof amount === "number") {
2590
+ if (!Number.isSafeInteger(amount) || amount <= 0) {
2591
+ throw new Error(`amount must be a positive safe integer, got ${amount}`);
2592
+ }
2593
+ return String(amount);
2594
+ }
2595
+ if (!/^(0|[1-9][0-9]*)$/.test(amount) || BigInt(amount) <= 0n) {
2596
+ throw new Error(`amount must be a canonical decimal string of atomic units, got ${amount}`);
2597
+ }
2598
+ return amount;
2599
+ };
2600
+ var normalizeHexBytes = (value, expectedBytes, label) => {
2601
+ if (value === void 0 || value === null) {
2602
+ throw new Error(`${label} is required`);
2603
+ }
2604
+ const bytes = typeof value === "string" ? solverHex(value, label) : value;
2605
+ if (!(bytes instanceof Uint8Array) || bytes.length !== expectedBytes) {
2606
+ const got = bytes instanceof Uint8Array ? bytes.length : typeof bytes;
2607
+ throw new Error(`${label} must be ${expectedBytes} bytes, got ${got}`);
2608
+ }
2609
+ return import_base10.hex.encode(bytes);
2610
+ };
2611
+ var normalizeMakerPkScript = (value) => {
2612
+ const encoded = normalizeHexBytes(value, 34, "maker_pk_script");
2613
+ if (!encoded.startsWith("5100") && !encoded.startsWith("5120")) {
2614
+ throw new Error("maker_pk_script must be a taproot scriptPubKey (OP_1 <32-byte program>)");
2615
+ }
2616
+ return encoded;
2617
+ };
2618
+ var normalizeMakerPublicKey = (value) => {
2619
+ const raw = typeof value === "string" ? value : import_base10.hex.encode(value);
2620
+ const bytes = solverHex(raw, "maker_public_key");
2621
+ if (bytes.length === 33) {
2622
+ throw new Error("maker_public_key must be x-only (32 bytes), not compressed (33 bytes)");
2623
+ }
2624
+ return normalizeHexBytes(bytes, 32, "maker_public_key");
2625
+ };
2561
2626
  var MIN_HEADROOM_SECONDS = 90 * 60;
2562
2627
  var gateError = (reason, message) => {
2563
2628
  const error = new Error(message);
@@ -2605,6 +2670,9 @@ var assertFundable = (input) => {
2605
2670
  }
2606
2671
  if (input.now >= input.quote.valid_until)
2607
2672
  fail("quote_expired", "quote expired \u2014 request a fresh one");
2673
+ if (quoteSats(input.quote.from_amount, "from_amount") <= 0 || quoteSats(input.quote.to_amount, "to_amount") <= 0) {
2674
+ fail("non_positive_amount", "quote carries a non-positive amount");
2675
+ }
2608
2676
  if (input.quote.refund_locktime !== void 0 && input.quote.refund_locktime - input.now < MIN_HEADROOM_SECONDS) {
2609
2677
  fail("insufficient_headroom", "refund deadline headroom below 90 minutes");
2610
2678
  }
@@ -2634,13 +2702,12 @@ var assertFundable = (input) => {
2634
2702
  `maxFee.referenceRate must be a positive finite number, got ${referenceRate}`
2635
2703
  );
2636
2704
  }
2637
- const fee = sameAsset ? input.quote.from_amount - input.quote.to_amount : Math.ceil(
2638
- (input.quote.from_amount * referenceRate - input.quote.to_amount) / referenceRate
2639
- );
2640
- const allowed = Math.max(
2641
- sats ?? 0,
2642
- Math.floor(input.quote.from_amount * (bps ?? 0) / 1e4)
2705
+ const fromSats = quoteSats(input.quote.from_amount, "from_amount");
2706
+ const toSats = quoteSats(input.quote.to_amount, "to_amount");
2707
+ const fee = sameAsset ? fromSats - toSats : Math.ceil(
2708
+ (fromSats * referenceRate - toSats) / referenceRate
2643
2709
  );
2710
+ const allowed = Math.max(sats ?? 0, Math.floor(fromSats * (bps ?? 0) / 1e4));
2644
2711
  if (fee > allowed) {
2645
2712
  fail("fee_too_high", `fee ${fee} exceeds the ${allowed} this client allows`);
2646
2713
  }
@@ -2844,7 +2911,7 @@ function lightningSendVtxoScript(params) {
2844
2911
  unilateralClaimDelay: seconds(params.claimDelay),
2845
2912
  unilateralRefundDelay: seconds(unilateralRefundDelay(params.claimDelay)),
2846
2913
  unilateralRefundWithoutReceiverDelay: seconds(
2847
- unilateralRefundWithoutReceiverDelay(params.claimDelay)
2914
+ params.refundWithoutReceiverDelay ?? unilateralRefundWithoutReceiverDelay(params.claimDelay)
2848
2915
  ),
2849
2916
  nonInteractiveParameters: {
2850
2917
  receiverPkScript: params.receiverPkScript,
@@ -2866,16 +2933,28 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2866
2933
  if (quote.refund_locktime === void 0) {
2867
2934
  throw new Error("lightning-send quote is missing refund_locktime");
2868
2935
  }
2936
+ const now = Math.floor(Date.now() / 1e3);
2937
+ const claimDelay = unilateralClaimDelay(Number(info.unilateralExitDelay));
2938
+ const refundWithoutReceiverDelay = quote.profile?.refund_without_receiver_delay;
2939
+ if (refundWithoutReceiverDelay === void 0) {
2940
+ throw new Error("lightning-send quote is missing profile.refund_without_receiver_delay");
2941
+ }
2942
+ if (!Number.isSafeInteger(refundWithoutReceiverDelay) || refundWithoutReceiverDelay < claimDelay || refundWithoutReceiverDelay % SEQUENCE_GRANULARITY_SECONDS !== 0 || refundWithoutReceiverDelay > 65535 * SEQUENCE_GRANULARITY_SECONDS) {
2943
+ throw new Error("lightning-send quote carries an invalid refund_without_receiver_delay");
2944
+ }
2945
+ if (refundWithoutReceiverDelay < quote.refund_locktime - now) {
2946
+ throw new Error("lightning-send quote lets the solo refund open before refund_locktime");
2947
+ }
2869
2948
  const receiverPkScriptHex = quote.profile?.receiver_pk_script;
2870
2949
  if (receiverPkScriptHex === void 0) {
2871
2950
  throw new Error("lightning-send quote is missing profile.receiver_pk_script");
2872
2951
  }
2873
- if (quote.to_amount !== params.invoice.amountSats) {
2952
+ if (quoteSats(quote.to_amount, "to_amount") !== params.invoice.amountSats) {
2874
2953
  throw new Error(
2875
2954
  `quote to_amount ${quote.to_amount} does not match the invoice's ${params.invoice.amountSats}`
2876
2955
  );
2877
2956
  }
2878
- if (quote.from_amount < quote.to_amount) {
2957
+ if (quoteSats(quote.from_amount, "from_amount") < quoteSats(quote.to_amount, "to_amount")) {
2879
2958
  throw new Error(
2880
2959
  `quote from_amount ${quote.from_amount} is below the invoice amount \u2014 a negative spread is not a quote`
2881
2960
  );
@@ -2887,7 +2966,8 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2887
2966
  refundLocktime: quote.refund_locktime,
2888
2967
  serverPubkey,
2889
2968
  paymentHash: params.invoice.paymentHash,
2890
- claimDelay: unilateralClaimDelay(Number(info.unilateralExitDelay)),
2969
+ claimDelay,
2970
+ refundWithoutReceiverDelay,
2891
2971
  emulatorPubkey: (0, import_sdk10.toXOnly)(
2892
2972
  import_base10.hex.decode((0, import_sdk10.resolveEmulatorPubkey)(network, params.emulatorPubkey)),
2893
2973
  "emulator signer key"
@@ -2911,7 +2991,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2911
2991
  assertFundable({
2912
2992
  quote,
2913
2993
  invoiceExpiresAt: params.invoice.expiresAt,
2914
- now: Math.floor(Date.now() / 1e3)
2994
+ now
2915
2995
  });
2916
2996
  await registerLockupContract(await wallet.getContractManager(), script, address);
2917
2997
  return {
@@ -2920,7 +3000,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2920
3000
  address,
2921
3001
  // What the lockup must carry: the quote's `from_amount` — the invoice
2922
3002
  // PLUS the corridor's fee, never the bare invoice amount.
2923
- fundAmount: quote.from_amount,
3003
+ fundAmount: quoteSats(quote.from_amount, "from_amount"),
2924
3004
  swapPkScript: script.pkScript,
2925
3005
  script,
2926
3006
  refundAddress,
@@ -2930,11 +3010,91 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2930
3010
  };
2931
3011
  }
2932
3012
  var offerTermsFromQuote = (quote, assets) => {
2933
- if (Boolean(assets.wantAsset) === Boolean(assets.offerAsset)) {
2934
- throw new Error("set exactly one of wantAsset or offerAsset");
3013
+ if (!assets.wantAsset && !assets.offerAsset) {
3014
+ throw new Error("set at least one of wantAsset or offerAsset");
3015
+ }
3016
+ const wantAmount = BigInt(quote.to_amount);
3017
+ if (assets.wantAsset) return { wantAmount, wantAsset: assets.wantAsset };
3018
+ return { wantAmount, offerAsset: assets.offerAsset };
3019
+ };
3020
+ var verifyOfferAddress = (quote, derived) => {
3021
+ const quotedAddress = quote.profile?.offer_address;
3022
+ const quotedScript = quote.profile?.offer_pk_script;
3023
+ const derivedScript = import_base10.hex.encode(derived.swapPkScript);
3024
+ if (quotedAddress !== derived.address || quotedScript !== derivedScript) {
3025
+ throw new AddressMismatch([derived.address], quotedAddress);
3026
+ }
3027
+ return derived;
3028
+ };
3029
+ var assertArkadeFundable = (input) => {
3030
+ const now = input.now ?? Math.floor(Date.now() / 1e3);
3031
+ if (!Number.isSafeInteger(input.quote.valid_until)) {
3032
+ throw gateError("quote_expired", "quote carries no valid_until");
3033
+ }
3034
+ if (now >= input.quote.valid_until) {
3035
+ throw gateError("quote_expired", `quote lapsed at ${input.quote.valid_until} (now ${now})`);
3036
+ }
3037
+ if (BigInt(input.quote.from_amount) <= 0n || BigInt(input.quote.to_amount) <= 0n) {
3038
+ throw gateError("quote_expired", "quote carries a non-positive amount");
2935
3039
  }
2936
- return { wantAmount: BigInt(quote.to_amount), ...assets };
2937
3040
  };
3041
+ async function requestArkadeSwap(wallet, arkServerUrl, transport, params) {
3042
+ if (!params.wantAsset && !params.offerAsset) {
3043
+ throw new Error("set at least one of wantAsset or offerAsset; BTC-to-BTC is not a swap");
3044
+ }
3045
+ const rfqId = params.rfqId ?? newRfqId();
3046
+ const [makerAddress, makerPublicKey] = await Promise.all([
3047
+ wallet.getAddress(),
3048
+ wallet.identity.xOnlyPublicKey()
3049
+ ]);
3050
+ const makerPkScript = import_sdk10.ArkAddress.decode(makerAddress).pkScript;
3051
+ const pair = rfqPair(
3052
+ params.offerAsset ? arkadeAssetLeg(params.offerAsset) : ARKADE_BTC,
3053
+ params.wantAsset ? arkadeAssetLeg(params.wantAsset) : ARKADE_BTC
3054
+ );
3055
+ const quote = await transport.requestQuote(
3056
+ arkadeSwapRequest({
3057
+ rfqId,
3058
+ ...params.offerAsset !== void 0 ? { offerAsset: params.offerAsset } : {},
3059
+ ...params.wantAsset !== void 0 ? { wantAsset: params.wantAsset } : {},
3060
+ amount: params.amount,
3061
+ makerPkScript,
3062
+ makerPublicKey
3063
+ })
3064
+ );
3065
+ if (quote.pair !== pair) {
3066
+ throw new Error(`solver quoted ${JSON.stringify(quote.pair)}, not the requested ${pair}`);
3067
+ }
3068
+ assertArkadeFundable({ quote, ...params.now !== void 0 ? { now: params.now } : {} });
3069
+ const terms = offerTermsFromQuote(quote, {
3070
+ ...params.offerAsset !== void 0 ? { offerAsset: params.offerAsset } : {},
3071
+ ...params.wantAsset !== void 0 ? { wantAsset: params.wantAsset } : {}
3072
+ });
3073
+ if (BigInt(quote.from_amount).toString() !== canonicalAssetAmount(params.amount)) {
3074
+ throw new Error(
3075
+ `quote from_amount ${quote.from_amount} does not match the requested ${canonicalAssetAmount(params.amount)}`
3076
+ );
3077
+ }
3078
+ const offer = await createOffer(wallet, arkServerUrl, {
3079
+ wantAmount: terms.wantAmount,
3080
+ ...terms.wantAsset !== void 0 ? { wantAsset: terms.wantAsset } : {},
3081
+ ...terms.offerAsset !== void 0 ? { offerAsset: terms.offerAsset } : {},
3082
+ ...params.emulatorPubkey !== void 0 ? { emulatorPubkey: params.emulatorPubkey } : {}
3083
+ });
3084
+ verifyOfferAddress(quote, offer);
3085
+ const fundAmount = BigInt(quote.from_amount);
3086
+ return {
3087
+ rfqId,
3088
+ quote,
3089
+ pair,
3090
+ address: offer.address,
3091
+ fundAmount,
3092
+ carrierSats: params.offerAsset !== void 0 ? ASSET_CARRIER_SATS : 0n,
3093
+ swapPkScript: offer.swapPkScript,
3094
+ offerHex: offer.offerHex,
3095
+ extension: offer.extension
3096
+ };
3097
+ }
2938
3098
  var l1NetworkFromArk = (network) => network === "bitcoin" ? "bitcoin" : network === "regtest" ? "regtest" : "testnet";
2939
3099
  var onchainSendRequest = (input) => ({
2940
3100
  v: 1,
@@ -3025,7 +3185,8 @@ function deriveOnchainSend(input) {
3025
3185
  l1Network: input.l1Network,
3026
3186
  refundLocktime,
3027
3187
  htlcLocktime,
3028
- minConfirmations
3188
+ minConfirmations,
3189
+ expectedAmount: quoteSats(quote.to_amount, "to_amount")
3029
3190
  };
3030
3191
  }
3031
3192
  async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
@@ -3088,7 +3249,8 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
3088
3249
  rfqId,
3089
3250
  quote,
3090
3251
  address: derived.address,
3091
- fundAmount: quote.from_amount,
3252
+ fundAmount: quoteSats(quote.from_amount, "from_amount"),
3253
+ expectedAmount: quoteSats(quote.to_amount, "to_amount"),
3092
3254
  swapPkScript: derived.swapPkScript,
3093
3255
  script: derived.script,
3094
3256
  refundAddress,
@@ -3101,7 +3263,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
3101
3263
  };
3102
3264
  }
3103
3265
  var assertQuotedAmount = (quote, amountSide, amount) => {
3104
- const quoted = amountSide === "from" ? quote.from_amount : quote.to_amount;
3266
+ const quoted = amountSide === "from" ? quoteSats(quote.from_amount, "from_amount") : quoteSats(quote.to_amount, "to_amount");
3105
3267
  if (quoted !== amount) {
3106
3268
  throw new Error(
3107
3269
  `quote ${amountSide === "from" ? "from_amount" : "to_amount"} ${quoted} does not match the requested ${amount} \u2014 not this trade's quote`
@@ -3160,7 +3322,7 @@ var assertReceivable = (input) => {
3160
3322
  `a payment at the deadline would leave under ${minClaimWindow}s to claim before the solver's refund opens`
3161
3323
  );
3162
3324
  }
3163
- if (input.maxPayAmount !== void 0 && input.quote.from_amount > input.maxPayAmount) {
3325
+ if (input.maxPayAmount !== void 0 && quoteSats(input.quote.from_amount, "from_amount") > input.maxPayAmount) {
3164
3326
  throw gateError(
3165
3327
  "price_too_high",
3166
3328
  `quote asks ${input.quote.from_amount} sats, above the ${input.maxPayAmount} ceiling`
@@ -3291,8 +3453,8 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
3291
3453
  rfqId,
3292
3454
  quote,
3293
3455
  invoice: derived.invoice,
3294
- payAmount: quote.from_amount,
3295
- expectedAmount: quote.to_amount,
3456
+ payAmount: quoteSats(quote.from_amount, "from_amount"),
3457
+ expectedAmount: quoteSats(quote.to_amount, "to_amount"),
3296
3458
  invoiceExpiresAt: payDeadline,
3297
3459
  address: derived.address,
3298
3460
  swapPkScript: derived.swapPkScript,
@@ -3418,8 +3580,8 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
3418
3580
  rfqId,
3419
3581
  quote,
3420
3582
  address: derived.address,
3421
- fundAmount: quote.from_amount,
3422
- expectedAmount: quote.to_amount,
3583
+ fundAmount: quoteSats(quote.from_amount, "from_amount"),
3584
+ expectedAmount: quoteSats(quote.to_amount, "to_amount"),
3423
3585
  swapPkScript: derived.swapPkScript,
3424
3586
  script: derived.script,
3425
3587
  htlc: derived.htlc,
@@ -5131,6 +5293,21 @@ var RfqSwapManager = class {
5131
5293
  });
5132
5294
  if (action === "claim" && phase.phase === "claimable") {
5133
5295
  if (swap.claimTxid) return "continue";
5296
+ if (!Number.isSafeInteger(swap.expectedAmount) || swap.expectedAmount <= 0) {
5297
+ this.block(
5298
+ swap,
5299
+ `expectedAmount is not a positive number of sats (${String(swap.expectedAmount)}), so the funded value cannot be checked \u2014 refusing to publish the preimage`
5300
+ );
5301
+ return "handled";
5302
+ }
5303
+ const filled = Number(phase.utxo.amount);
5304
+ if (!Number.isFinite(filled) || filled < swap.expectedAmount) {
5305
+ this.block(
5306
+ swap,
5307
+ `fill holds ${filled} sats, below the agreed ${swap.expectedAmount} \u2014 refusing to publish the preimage`
5308
+ );
5309
+ return "handled";
5310
+ }
5134
5311
  if (this.callbacks && !this.callbacks.claimOnchain) {
5135
5312
  this.block(
5136
5313
  swap,
@@ -5184,6 +5361,8 @@ var RfqSwapManager = class {
5184
5361
  if (pushed) {
5185
5362
  swap.refundArkTxid = pushed.arkTxid;
5186
5363
  this.touch(swap);
5364
+ } else if (now < swap.refundLocktime + REFUND_MTP_LAG_SECONDS) {
5365
+ return;
5187
5366
  }
5188
5367
  this.setState(swap, "refunded");
5189
5368
  this.emitAction(swap, "refundArkade");
@@ -5552,6 +5731,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5552
5731
  arkadeAssetLeg,
5553
5732
  arkadeRefunder,
5554
5733
  arkadeSwapRequest,
5734
+ assertArkadeFundable,
5555
5735
  assertFundable,
5556
5736
  assertReceivable,
5557
5737
  awaitLockupFunding,
@@ -5560,6 +5740,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5560
5740
  buildHtlcClaim,
5561
5741
  buildHtlcRefund,
5562
5742
  cancelOffer,
5743
+ canonicalAssetAmount,
5563
5744
  chainSourceFrom,
5564
5745
  claimOnchainFill,
5565
5746
  claimPacketShape,
@@ -5600,6 +5781,8 @@ async function lockupTxids(indexer, record, wantFunding) {
5600
5781
  newPreimage,
5601
5782
  newRfqId,
5602
5783
  nextOnchainAction,
5784
+ normalizeMakerPkScript,
5785
+ normalizeMakerPublicKey,
5603
5786
  offerTermsFromQuote,
5604
5787
  offerVtxoScript,
5605
5788
  onchainHtlcScript,
@@ -5617,6 +5800,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5617
5800
  registerAssetSwapRestore,
5618
5801
  registerLockupContract,
5619
5802
  relayTransport,
5803
+ requestArkadeSwap,
5620
5804
  requestLightningReceive,
5621
5805
  requestLightningSend,
5622
5806
  requestOnchainReceive,
@@ -5654,6 +5838,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5654
5838
  updateRfqSwapRecord,
5655
5839
  validatePlan,
5656
5840
  verifyLockupAddress,
5841
+ verifyOfferAddress,
5657
5842
  verifyReceiveInvoice,
5658
5843
  watchOfferSwaps
5659
5844
  });
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ProvisionedKey, ProvisionedClaimSecret, ArkTxInput, asset, RelativeTimelock, IWallet, EmulatorProvider, arkade, RestIndexerProvider, Transaction, IContractManager, OnchainProvider, PaymentRail, Asset, VHTLC, Identity, ActivityResolver } from '@arkade-os/sdk';
2
- import { S as SwapSecretsProjection, R as RfqSwapRecord, A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry, b as RefundArkProvider, c as RefundIndexer, L as LockupVtxo, d as RfqSwap, e as ArkadeRefundResult, f as LockupSpendIndexer, g as RfqSwapState } from './repository-rwU1Sv2-.cjs';
3
- export { h as AssetSwapStatus, i as AvailableRfqSwapManagerCallbacks, B as BTC_ASSET_ID, I as InMemoryAssetSwapRepository, j as LightningReceiveSwap, k as LightningSendSwap, l as LockupFate, m as LockupNeedsRecoveryError, n as LockupParams, o as LockupSpend, O as OnchainSendAction, p as OnchainSendSwap, P as PersistableRfqSwap, q as PreimageBlockedReason, r as PreimageNotRecoverableError, s as REFUND_MTP_LAG_SECONDS, t as RFQ_RESOLVED_STATES, u as RFQ_SWAP_RETENTION_SECONDS, v as RFQ_SWAP_TERMINAL_STATES, w as RefundOutcome, x as RfqRestoreFailure, y as RfqRestoreOptions, z as RfqRestoreResult, C as RfqSwapActionName, D as RfqSwapLockup, E as RfqSwapManager, F as RfqSwapManagerCallbacks, G as RfqSwapManagerConfig, H as RfqSwapManagerDeps, J as RfqSwapManagerEvents, K as RfqSwapOrigin, N as RfqSwapOriginRequired, Q as RfqSwapOutcome, T as RfqSwapRecordStore, U as SwapContractRegistry, V as addAssetSwap, W as awaitRfqResolution, X as createRfqSwapRecord, Y as findLockupVtxos, Z as getAssetSwaps, _ as getAssetSwapsOrThrow, $ as isRfqSwapTerminal, a0 as isRfqTerminal, a1 as nextOnchainAction, a2 as preimageForSwapRecord, a3 as pushRefundWithoutReceiver, a4 as readLockupFate, a5 as rebuildRfqSwap, a6 as refundIfUnresolved, a7 as rfqSwapOriginOf, a8 as shouldRetainRfqSwap, a9 as swapSecretsToRecord, aa as updateAssetSwap, ab as updateAssetSwapBestEffort, ac as updateRfqSwapRecord } from './repository-rwU1Sv2-.cjs';
2
+ import { S as SwapSecretsProjection, R as RfqSwapRecord, A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry, b as RefundArkProvider, c as RefundIndexer, L as LockupVtxo, d as RfqSwap, e as ArkadeRefundResult, f as LockupSpendIndexer, g as RfqSwapState } from './repository-BXIcmIIC.cjs';
3
+ export { h as AssetSwapStatus, i as AvailableRfqSwapManagerCallbacks, B as BTC_ASSET_ID, I as InMemoryAssetSwapRepository, j as LightningReceiveSwap, k as LightningSendSwap, l as LockupFate, m as LockupNeedsRecoveryError, n as LockupParams, o as LockupSpend, O as OnchainSendAction, p as OnchainSendSwap, P as PersistableRfqSwap, q as PreimageBlockedReason, r as PreimageNotRecoverableError, s as REFUND_MTP_LAG_SECONDS, t as RFQ_RESOLVED_STATES, u as RFQ_SWAP_RETENTION_SECONDS, v as RFQ_SWAP_TERMINAL_STATES, w as RefundOutcome, x as RfqRestoreFailure, y as RfqRestoreOptions, z as RfqRestoreResult, C as RfqSwapActionName, D as RfqSwapLockup, E as RfqSwapManager, F as RfqSwapManagerCallbacks, G as RfqSwapManagerConfig, H as RfqSwapManagerDeps, J as RfqSwapManagerEvents, K as RfqSwapOrigin, N as RfqSwapOriginRequired, Q as RfqSwapOutcome, T as RfqSwapRecordStore, U as SwapContractRegistry, V as addAssetSwap, W as awaitRfqResolution, X as createRfqSwapRecord, Y as findLockupVtxos, Z as getAssetSwaps, _ as getAssetSwapsOrThrow, $ as isRfqSwapTerminal, a0 as isRfqTerminal, a1 as nextOnchainAction, a2 as preimageForSwapRecord, a3 as pushRefundWithoutReceiver, a4 as readLockupFate, a5 as rebuildRfqSwap, a6 as refundIfUnresolved, a7 as rfqSwapOriginOf, a8 as shouldRetainRfqSwap, a9 as swapSecretsToRecord, aa as updateAssetSwap, ab as updateAssetSwapBestEffort, ac as updateRfqSwapRecord } from './repository-BXIcmIIC.cjs';
4
4
  import { Network, LocalCardInput, DiscoveredMarket, Side, OfferPlan } from '@arkade-os/solver-discovery';
5
- import { O as OnchainNetwork, a as OnchainHtlc, b as OnchainHtlcParams, C as ChainSource, R as RfqTransport, r as requestOnchainSend, I as InvoiceFacts, c as requestLightningSend } from './rfq-DirJn_4k.cjs';
6
- export { A as ARKADE_ASSET, d as ARKADE_BTC, e as AddressMismatch, f as ChainUtxo, H as HtlcUtxo, L as LIGHTNING_BTC, g as LIGHTNING_RECEIVE_PAIR, h as LIGHTNING_SEND_PAIR, i as LOCKTIME_THRESHOLD, j as LightningReceiveTreeParams, k as LightningSendTreeParams, M as MAX_MIN_CONFIRMATIONS, l as MIN_CLAIM_WINDOW_SECONDS, m as MIN_HEADROOM_SECONDS, n as ONCHAIN_BTC, o as ONCHAIN_CLAIM_MARGIN_SECONDS, p as ONCHAIN_DUST_SATS, q as ONCHAIN_ORDER_MARGIN_SECONDS, s as ONCHAIN_RECEIVE_PAIR, t as ONCHAIN_SECONDS_PER_BLOCK, u as ONCHAIN_SEND_PAIR, v as OnchainHtlcPhase, w as RFQ_REFUSAL_ERROR_CODES, x as RFQ_TERMINAL_STATES, y as RelaySocket, z as RfqQuote, B as RfqRefusalDetail, D as RfqRefusalErrorCode, E as RfqRefusalReason, F as RfqRefusalUnit, G as RfqStatus, S as SOLO_REFUND_HEADROOM_SECONDS, J as SwapRefusal, K as arkadeAssetLeg, N as arkadeSwapRequest, P as assertFundable, Q as assertReceivable, T as awaitOnchainFill, U as buildHtlcClaim, V as buildHtlcRefund, W as claimOnchainFill, X as classifyOnchainHtlc, Y as deriveLightningReceive, Z as deriveOnchainReceive, _ as deriveOnchainSend, $ as extractPreimage, a0 as httpTransport, a1 as isRfqRefusalErrorCode, a2 as l1ScriptForAddress, a3 as lightningReceiveRequest, a4 as lightningSendRequest, a5 as lightningSendVtxoScript, a6 as newPreimage, a7 as newRfqId, a8 as offerTermsFromQuote, a9 as onchainHtlcScript, aa as onchainReceiveRequest, ab as onchainSendRequest, ac as paymentHashOf, ad as receiveVtxoScript, ae as relayTransport, af as requestLightningReceive, ag as requestOnchainReceive, ah as rfqPair, ai as unilateralClaimDelay, aj as unilateralRefundDelay, ak as unilateralRefundWithoutReceiverDelay, al as verifyLockupAddress, am as verifyReceiveInvoice } from './rfq-DirJn_4k.cjs';
5
+ import { O as OnchainNetwork, a as OnchainHtlc, b as OnchainHtlcParams, C as ChainSource, R as RfqTransport, r as requestOnchainSend, I as InvoiceFacts, c as requestLightningSend } from './rfq-D96aLvCA.cjs';
6
+ export { A as ARKADE_ASSET, d as ARKADE_BTC, e as AddressMismatch, f as ChainUtxo, H as HtlcUtxo, L as LIGHTNING_BTC, g as LIGHTNING_RECEIVE_PAIR, h as LIGHTNING_SEND_PAIR, i as LOCKTIME_THRESHOLD, j as LightningReceiveTreeParams, k as LightningSendTreeParams, M as MAX_MIN_CONFIRMATIONS, l as MIN_CLAIM_WINDOW_SECONDS, m as MIN_HEADROOM_SECONDS, n as ONCHAIN_BTC, o as ONCHAIN_CLAIM_MARGIN_SECONDS, p as ONCHAIN_DUST_SATS, q as ONCHAIN_ORDER_MARGIN_SECONDS, s as ONCHAIN_RECEIVE_PAIR, t as ONCHAIN_SECONDS_PER_BLOCK, u as ONCHAIN_SEND_PAIR, v as OnchainHtlcPhase, w as RFQ_REFUSAL_ERROR_CODES, x as RFQ_TERMINAL_STATES, y as RelaySocket, z as RfqQuote, B as RfqRefusalDetail, D as RfqRefusalErrorCode, E as RfqRefusalReason, F as RfqRefusalUnit, G as RfqStatus, S as SOLO_REFUND_HEADROOM_SECONDS, J as SwapRefusal, K as arkadeAssetLeg, N as arkadeSwapRequest, P as assertArkadeFundable, Q as assertFundable, T as assertReceivable, U as awaitOnchainFill, V as buildHtlcClaim, W as buildHtlcRefund, X as canonicalAssetAmount, Y as claimOnchainFill, Z as classifyOnchainHtlc, _ as deriveLightningReceive, $ as deriveOnchainReceive, a0 as deriveOnchainSend, a1 as extractPreimage, a2 as httpTransport, a3 as isRfqRefusalErrorCode, a4 as l1ScriptForAddress, a5 as lightningReceiveRequest, a6 as lightningSendRequest, a7 as lightningSendVtxoScript, a8 as newPreimage, a9 as newRfqId, aa as normalizeMakerPkScript, ab as normalizeMakerPublicKey, ac as offerTermsFromQuote, ad as onchainHtlcScript, ae as onchainReceiveRequest, af as onchainSendRequest, ag as paymentHashOf, ah as receiveVtxoScript, ai as relayTransport, aj as requestArkadeSwap, ak as requestLightningReceive, al as requestOnchainReceive, am as rfqPair, an as unilateralClaimDelay, ao as unilateralRefundDelay, ap as unilateralRefundWithoutReceiverDelay, aq as verifyLockupAddress, ar as verifyOfferAddress, as as verifyReceiveInvoice } from './rfq-D96aLvCA.cjs';
7
7
 
8
8
  /**
9
9
  * Which wallet key signs this leg. Stored at `profile.signer`.
@@ -141,6 +141,7 @@ interface OnchainSendProfile extends Record<string, unknown> {
141
141
  htlcAddress: string;
142
142
  /** `profile.min_confirmations`; gates when the fill is claimable. */
143
143
  minConfirmations: number;
144
+ expectedAmount: number;
144
145
  /** Where the claim PAYS, hex. The spender's own choice, so nothing else
145
146
  * gives it back — and `buildHtlcClaim` needs it. */
146
147
  payoutPkScript: string;
@@ -183,6 +184,7 @@ declare function onchainSendProfile(result: {
183
184
  htlcParams: OnchainHtlcParams;
184
185
  l1Network: OnchainNetwork;
185
186
  minConfirmations: number;
187
+ expectedAmount: number;
186
188
  payoutPkScript: Uint8Array;
187
189
  }): Omit<OnchainSendProfile, "signer" | "hashlock">;
188
190
 
@@ -503,7 +505,11 @@ declare const QUOTE_OPTIONS: {
503
505
  declare const makeCachedFeedFetch: (ttlMs?: number, fetchImpl?: typeof fetch) => typeof fetch;
504
506
  interface DiscoverMarketsOptions {
505
507
  network: Network;
506
- /** The network's solver registry index URL; no registry means no markets. */
508
+ /** Overrides the registry this network publishes by default. Omit (or pass
509
+ * `undefined`) to follow `@arkade-os/solver-discovery`'s per-network
510
+ * default index, which the library can move in a release without a client
511
+ * change; this package never hardcodes the URL. The markets cache is keyed
512
+ * by the URL actually followed, default included. */
507
513
  registryUrl: string | undefined;
508
514
  /** Backs the 1-hour markets cache and its stale fallback. Omit for a
509
515
  * one-shot discovery that always hits the registry. */
@@ -523,9 +529,11 @@ interface DiscoverMarketsOptions {
523
529
  useCache?: boolean;
524
530
  }
525
531
  /**
526
- * Markets from the network's solver registry; [] when none is configured.
527
- * Registry content changes rarely, so results are cached for an hour and a
528
- * stale cache backstops an unreachable registry (quotes stay live either way).
532
+ * Markets from this network's solver registry — the caller's `registryUrl` when
533
+ * given, else the network's published default. Only an unrecognised network
534
+ * yields []. Registry content changes rarely, so results are cached for an hour
535
+ * and a stale cache backstops an unreachable registry (quotes stay live either
536
+ * way).
529
537
  */
530
538
  declare const discoverMarkets: (options: DiscoverMarketsOptions) => Promise<DiscoveredMarket[]>;
531
539
  /** Best market for a from/to pair, in either orientation. `give` is the side
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ProvisionedKey, ProvisionedClaimSecret, ArkTxInput, asset, RelativeTimelock, IWallet, EmulatorProvider, arkade, RestIndexerProvider, Transaction, IContractManager, OnchainProvider, PaymentRail, Asset, VHTLC, Identity, ActivityResolver } from '@arkade-os/sdk';
2
- import { S as SwapSecretsProjection, R as RfqSwapRecord, A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry, b as RefundArkProvider, c as RefundIndexer, L as LockupVtxo, d as RfqSwap, e as ArkadeRefundResult, f as LockupSpendIndexer, g as RfqSwapState } from './repository-DH5zjdw4.js';
3
- export { h as AssetSwapStatus, i as AvailableRfqSwapManagerCallbacks, B as BTC_ASSET_ID, I as InMemoryAssetSwapRepository, j as LightningReceiveSwap, k as LightningSendSwap, l as LockupFate, m as LockupNeedsRecoveryError, n as LockupParams, o as LockupSpend, O as OnchainSendAction, p as OnchainSendSwap, P as PersistableRfqSwap, q as PreimageBlockedReason, r as PreimageNotRecoverableError, s as REFUND_MTP_LAG_SECONDS, t as RFQ_RESOLVED_STATES, u as RFQ_SWAP_RETENTION_SECONDS, v as RFQ_SWAP_TERMINAL_STATES, w as RefundOutcome, x as RfqRestoreFailure, y as RfqRestoreOptions, z as RfqRestoreResult, C as RfqSwapActionName, D as RfqSwapLockup, E as RfqSwapManager, F as RfqSwapManagerCallbacks, G as RfqSwapManagerConfig, H as RfqSwapManagerDeps, J as RfqSwapManagerEvents, K as RfqSwapOrigin, N as RfqSwapOriginRequired, Q as RfqSwapOutcome, T as RfqSwapRecordStore, U as SwapContractRegistry, V as addAssetSwap, W as awaitRfqResolution, X as createRfqSwapRecord, Y as findLockupVtxos, Z as getAssetSwaps, _ as getAssetSwapsOrThrow, $ as isRfqSwapTerminal, a0 as isRfqTerminal, a1 as nextOnchainAction, a2 as preimageForSwapRecord, a3 as pushRefundWithoutReceiver, a4 as readLockupFate, a5 as rebuildRfqSwap, a6 as refundIfUnresolved, a7 as rfqSwapOriginOf, a8 as shouldRetainRfqSwap, a9 as swapSecretsToRecord, aa as updateAssetSwap, ab as updateAssetSwapBestEffort, ac as updateRfqSwapRecord } from './repository-DH5zjdw4.js';
2
+ import { S as SwapSecretsProjection, R as RfqSwapRecord, A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry, b as RefundArkProvider, c as RefundIndexer, L as LockupVtxo, d as RfqSwap, e as ArkadeRefundResult, f as LockupSpendIndexer, g as RfqSwapState } from './repository-B6VPDGHC.js';
3
+ export { h as AssetSwapStatus, i as AvailableRfqSwapManagerCallbacks, B as BTC_ASSET_ID, I as InMemoryAssetSwapRepository, j as LightningReceiveSwap, k as LightningSendSwap, l as LockupFate, m as LockupNeedsRecoveryError, n as LockupParams, o as LockupSpend, O as OnchainSendAction, p as OnchainSendSwap, P as PersistableRfqSwap, q as PreimageBlockedReason, r as PreimageNotRecoverableError, s as REFUND_MTP_LAG_SECONDS, t as RFQ_RESOLVED_STATES, u as RFQ_SWAP_RETENTION_SECONDS, v as RFQ_SWAP_TERMINAL_STATES, w as RefundOutcome, x as RfqRestoreFailure, y as RfqRestoreOptions, z as RfqRestoreResult, C as RfqSwapActionName, D as RfqSwapLockup, E as RfqSwapManager, F as RfqSwapManagerCallbacks, G as RfqSwapManagerConfig, H as RfqSwapManagerDeps, J as RfqSwapManagerEvents, K as RfqSwapOrigin, N as RfqSwapOriginRequired, Q as RfqSwapOutcome, T as RfqSwapRecordStore, U as SwapContractRegistry, V as addAssetSwap, W as awaitRfqResolution, X as createRfqSwapRecord, Y as findLockupVtxos, Z as getAssetSwaps, _ as getAssetSwapsOrThrow, $ as isRfqSwapTerminal, a0 as isRfqTerminal, a1 as nextOnchainAction, a2 as preimageForSwapRecord, a3 as pushRefundWithoutReceiver, a4 as readLockupFate, a5 as rebuildRfqSwap, a6 as refundIfUnresolved, a7 as rfqSwapOriginOf, a8 as shouldRetainRfqSwap, a9 as swapSecretsToRecord, aa as updateAssetSwap, ab as updateAssetSwapBestEffort, ac as updateRfqSwapRecord } from './repository-B6VPDGHC.js';
4
4
  import { Network, LocalCardInput, DiscoveredMarket, Side, OfferPlan } from '@arkade-os/solver-discovery';
5
- import { O as OnchainNetwork, a as OnchainHtlc, b as OnchainHtlcParams, C as ChainSource, R as RfqTransport, r as requestOnchainSend, I as InvoiceFacts, c as requestLightningSend } from './rfq-DirJn_4k.js';
6
- export { A as ARKADE_ASSET, d as ARKADE_BTC, e as AddressMismatch, f as ChainUtxo, H as HtlcUtxo, L as LIGHTNING_BTC, g as LIGHTNING_RECEIVE_PAIR, h as LIGHTNING_SEND_PAIR, i as LOCKTIME_THRESHOLD, j as LightningReceiveTreeParams, k as LightningSendTreeParams, M as MAX_MIN_CONFIRMATIONS, l as MIN_CLAIM_WINDOW_SECONDS, m as MIN_HEADROOM_SECONDS, n as ONCHAIN_BTC, o as ONCHAIN_CLAIM_MARGIN_SECONDS, p as ONCHAIN_DUST_SATS, q as ONCHAIN_ORDER_MARGIN_SECONDS, s as ONCHAIN_RECEIVE_PAIR, t as ONCHAIN_SECONDS_PER_BLOCK, u as ONCHAIN_SEND_PAIR, v as OnchainHtlcPhase, w as RFQ_REFUSAL_ERROR_CODES, x as RFQ_TERMINAL_STATES, y as RelaySocket, z as RfqQuote, B as RfqRefusalDetail, D as RfqRefusalErrorCode, E as RfqRefusalReason, F as RfqRefusalUnit, G as RfqStatus, S as SOLO_REFUND_HEADROOM_SECONDS, J as SwapRefusal, K as arkadeAssetLeg, N as arkadeSwapRequest, P as assertFundable, Q as assertReceivable, T as awaitOnchainFill, U as buildHtlcClaim, V as buildHtlcRefund, W as claimOnchainFill, X as classifyOnchainHtlc, Y as deriveLightningReceive, Z as deriveOnchainReceive, _ as deriveOnchainSend, $ as extractPreimage, a0 as httpTransport, a1 as isRfqRefusalErrorCode, a2 as l1ScriptForAddress, a3 as lightningReceiveRequest, a4 as lightningSendRequest, a5 as lightningSendVtxoScript, a6 as newPreimage, a7 as newRfqId, a8 as offerTermsFromQuote, a9 as onchainHtlcScript, aa as onchainReceiveRequest, ab as onchainSendRequest, ac as paymentHashOf, ad as receiveVtxoScript, ae as relayTransport, af as requestLightningReceive, ag as requestOnchainReceive, ah as rfqPair, ai as unilateralClaimDelay, aj as unilateralRefundDelay, ak as unilateralRefundWithoutReceiverDelay, al as verifyLockupAddress, am as verifyReceiveInvoice } from './rfq-DirJn_4k.js';
5
+ import { O as OnchainNetwork, a as OnchainHtlc, b as OnchainHtlcParams, C as ChainSource, R as RfqTransport, r as requestOnchainSend, I as InvoiceFacts, c as requestLightningSend } from './rfq-D96aLvCA.js';
6
+ export { A as ARKADE_ASSET, d as ARKADE_BTC, e as AddressMismatch, f as ChainUtxo, H as HtlcUtxo, L as LIGHTNING_BTC, g as LIGHTNING_RECEIVE_PAIR, h as LIGHTNING_SEND_PAIR, i as LOCKTIME_THRESHOLD, j as LightningReceiveTreeParams, k as LightningSendTreeParams, M as MAX_MIN_CONFIRMATIONS, l as MIN_CLAIM_WINDOW_SECONDS, m as MIN_HEADROOM_SECONDS, n as ONCHAIN_BTC, o as ONCHAIN_CLAIM_MARGIN_SECONDS, p as ONCHAIN_DUST_SATS, q as ONCHAIN_ORDER_MARGIN_SECONDS, s as ONCHAIN_RECEIVE_PAIR, t as ONCHAIN_SECONDS_PER_BLOCK, u as ONCHAIN_SEND_PAIR, v as OnchainHtlcPhase, w as RFQ_REFUSAL_ERROR_CODES, x as RFQ_TERMINAL_STATES, y as RelaySocket, z as RfqQuote, B as RfqRefusalDetail, D as RfqRefusalErrorCode, E as RfqRefusalReason, F as RfqRefusalUnit, G as RfqStatus, S as SOLO_REFUND_HEADROOM_SECONDS, J as SwapRefusal, K as arkadeAssetLeg, N as arkadeSwapRequest, P as assertArkadeFundable, Q as assertFundable, T as assertReceivable, U as awaitOnchainFill, V as buildHtlcClaim, W as buildHtlcRefund, X as canonicalAssetAmount, Y as claimOnchainFill, Z as classifyOnchainHtlc, _ as deriveLightningReceive, $ as deriveOnchainReceive, a0 as deriveOnchainSend, a1 as extractPreimage, a2 as httpTransport, a3 as isRfqRefusalErrorCode, a4 as l1ScriptForAddress, a5 as lightningReceiveRequest, a6 as lightningSendRequest, a7 as lightningSendVtxoScript, a8 as newPreimage, a9 as newRfqId, aa as normalizeMakerPkScript, ab as normalizeMakerPublicKey, ac as offerTermsFromQuote, ad as onchainHtlcScript, ae as onchainReceiveRequest, af as onchainSendRequest, ag as paymentHashOf, ah as receiveVtxoScript, ai as relayTransport, aj as requestArkadeSwap, ak as requestLightningReceive, al as requestOnchainReceive, am as rfqPair, an as unilateralClaimDelay, ao as unilateralRefundDelay, ap as unilateralRefundWithoutReceiverDelay, aq as verifyLockupAddress, ar as verifyOfferAddress, as as verifyReceiveInvoice } from './rfq-D96aLvCA.js';
7
7
 
8
8
  /**
9
9
  * Which wallet key signs this leg. Stored at `profile.signer`.
@@ -141,6 +141,7 @@ interface OnchainSendProfile extends Record<string, unknown> {
141
141
  htlcAddress: string;
142
142
  /** `profile.min_confirmations`; gates when the fill is claimable. */
143
143
  minConfirmations: number;
144
+ expectedAmount: number;
144
145
  /** Where the claim PAYS, hex. The spender's own choice, so nothing else
145
146
  * gives it back — and `buildHtlcClaim` needs it. */
146
147
  payoutPkScript: string;
@@ -183,6 +184,7 @@ declare function onchainSendProfile(result: {
183
184
  htlcParams: OnchainHtlcParams;
184
185
  l1Network: OnchainNetwork;
185
186
  minConfirmations: number;
187
+ expectedAmount: number;
186
188
  payoutPkScript: Uint8Array;
187
189
  }): Omit<OnchainSendProfile, "signer" | "hashlock">;
188
190
 
@@ -503,7 +505,11 @@ declare const QUOTE_OPTIONS: {
503
505
  declare const makeCachedFeedFetch: (ttlMs?: number, fetchImpl?: typeof fetch) => typeof fetch;
504
506
  interface DiscoverMarketsOptions {
505
507
  network: Network;
506
- /** The network's solver registry index URL; no registry means no markets. */
508
+ /** Overrides the registry this network publishes by default. Omit (or pass
509
+ * `undefined`) to follow `@arkade-os/solver-discovery`'s per-network
510
+ * default index, which the library can move in a release without a client
511
+ * change; this package never hardcodes the URL. The markets cache is keyed
512
+ * by the URL actually followed, default included. */
507
513
  registryUrl: string | undefined;
508
514
  /** Backs the 1-hour markets cache and its stale fallback. Omit for a
509
515
  * one-shot discovery that always hits the registry. */
@@ -523,9 +529,11 @@ interface DiscoverMarketsOptions {
523
529
  useCache?: boolean;
524
530
  }
525
531
  /**
526
- * Markets from the network's solver registry; [] when none is configured.
527
- * Registry content changes rarely, so results are cached for an hour and a
528
- * stale cache backstops an unreachable registry (quotes stay live either way).
532
+ * Markets from this network's solver registry — the caller's `registryUrl` when
533
+ * given, else the network's published default. Only an unrecognised network
534
+ * yields []. Registry content changes rarely, so results are cached for an hour
535
+ * and a stale cache backstops an unreachable registry (quotes stay live either
536
+ * way).
529
537
  */
530
538
  declare const discoverMarkets: (options: DiscoverMarketsOptions) => Promise<DiscoveredMarket[]>;
531
539
  /** Best market for a from/to pair, in either orientation. `give` is the side