@arkade-os/swap 0.0.18 → 0.0.20

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,7 @@ 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"
2534
- );
2535
- }
2536
- if (input.wantAsset && input.offerAsset) {
2537
- 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"
2555
+ "set at least one of wantAsset or offerAsset \u2014 with neither set both legs are BTC, which is not a swap"
2539
2556
  );
2540
2557
  }
2541
2558
  const pair = rfqPair(
@@ -2548,16 +2565,59 @@ var arkadeSwapRequest = (input) => {
2548
2565
  type: "rfq_request",
2549
2566
  rfq_id: input.rfqId,
2550
2567
  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: {}
2568
+ amount_side: input.amountSide ?? "from",
2569
+ amount: canonicalAssetAmount(input.amount),
2570
+ // The pair names the assets; the profile names the trader. Both are
2571
+ // covenant parameters, and the solver's `.strict()` schema refuses
2572
+ // anything else — an undeclared key is `unsupported_payload`.
2573
+ profile: {
2574
+ maker_pk_script: normalizeMakerPkScript(input.makerPkScript),
2575
+ maker_public_key: normalizeMakerPublicKey(input.makerPublicKey)
2576
+ }
2559
2577
  };
2560
2578
  };
2579
+ var canonicalAssetAmount = (amount) => {
2580
+ if (typeof amount === "bigint") {
2581
+ if (amount <= 0n) throw new Error(`amount must be positive, got ${amount}`);
2582
+ return amount.toString();
2583
+ }
2584
+ if (typeof amount === "number") {
2585
+ if (!Number.isSafeInteger(amount) || amount <= 0) {
2586
+ throw new Error(`amount must be a positive safe integer, got ${amount}`);
2587
+ }
2588
+ return String(amount);
2589
+ }
2590
+ if (!/^(0|[1-9][0-9]*)$/.test(amount) || BigInt(amount) <= 0n) {
2591
+ throw new Error(`amount must be a canonical decimal string of atomic units, got ${amount}`);
2592
+ }
2593
+ return amount;
2594
+ };
2595
+ var normalizeHexBytes = (value, expectedBytes, label) => {
2596
+ if (value === void 0 || value === null) {
2597
+ throw new Error(`${label} is required`);
2598
+ }
2599
+ const bytes = typeof value === "string" ? solverHex(value, label) : value;
2600
+ if (!(bytes instanceof Uint8Array) || bytes.length !== expectedBytes) {
2601
+ const got = bytes instanceof Uint8Array ? bytes.length : typeof bytes;
2602
+ throw new Error(`${label} must be ${expectedBytes} bytes, got ${got}`);
2603
+ }
2604
+ return import_base10.hex.encode(bytes);
2605
+ };
2606
+ var normalizeMakerPkScript = (value) => {
2607
+ const encoded = normalizeHexBytes(value, 34, "maker_pk_script");
2608
+ if (!encoded.startsWith("5100") && !encoded.startsWith("5120")) {
2609
+ throw new Error("maker_pk_script must be a taproot scriptPubKey (OP_1 <32-byte program>)");
2610
+ }
2611
+ return encoded;
2612
+ };
2613
+ var normalizeMakerPublicKey = (value) => {
2614
+ const raw = typeof value === "string" ? value : import_base10.hex.encode(value);
2615
+ const bytes = solverHex(raw, "maker_public_key");
2616
+ if (bytes.length === 33) {
2617
+ throw new Error("maker_public_key must be x-only (32 bytes), not compressed (33 bytes)");
2618
+ }
2619
+ return normalizeHexBytes(bytes, 32, "maker_public_key");
2620
+ };
2561
2621
  var MIN_HEADROOM_SECONDS = 90 * 60;
2562
2622
  var gateError = (reason, message) => {
2563
2623
  const error = new Error(message);
@@ -2605,6 +2665,9 @@ var assertFundable = (input) => {
2605
2665
  }
2606
2666
  if (input.now >= input.quote.valid_until)
2607
2667
  fail("quote_expired", "quote expired \u2014 request a fresh one");
2668
+ if (quoteSats(input.quote.from_amount, "from_amount") <= 0 || quoteSats(input.quote.to_amount, "to_amount") <= 0) {
2669
+ fail("non_positive_amount", "quote carries a non-positive amount");
2670
+ }
2608
2671
  if (input.quote.refund_locktime !== void 0 && input.quote.refund_locktime - input.now < MIN_HEADROOM_SECONDS) {
2609
2672
  fail("insufficient_headroom", "refund deadline headroom below 90 minutes");
2610
2673
  }
@@ -2634,13 +2697,12 @@ var assertFundable = (input) => {
2634
2697
  `maxFee.referenceRate must be a positive finite number, got ${referenceRate}`
2635
2698
  );
2636
2699
  }
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)
2700
+ const fromSats = quoteSats(input.quote.from_amount, "from_amount");
2701
+ const toSats = quoteSats(input.quote.to_amount, "to_amount");
2702
+ const fee = sameAsset ? fromSats - toSats : Math.ceil(
2703
+ (fromSats * referenceRate - toSats) / referenceRate
2643
2704
  );
2705
+ const allowed = Math.max(sats ?? 0, Math.floor(fromSats * (bps ?? 0) / 1e4));
2644
2706
  if (fee > allowed) {
2645
2707
  fail("fee_too_high", `fee ${fee} exceeds the ${allowed} this client allows`);
2646
2708
  }
@@ -2882,12 +2944,12 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2882
2944
  if (receiverPkScriptHex === void 0) {
2883
2945
  throw new Error("lightning-send quote is missing profile.receiver_pk_script");
2884
2946
  }
2885
- if (quote.to_amount !== params.invoice.amountSats) {
2947
+ if (quoteSats(quote.to_amount, "to_amount") !== params.invoice.amountSats) {
2886
2948
  throw new Error(
2887
2949
  `quote to_amount ${quote.to_amount} does not match the invoice's ${params.invoice.amountSats}`
2888
2950
  );
2889
2951
  }
2890
- if (quote.from_amount < quote.to_amount) {
2952
+ if (quoteSats(quote.from_amount, "from_amount") < quoteSats(quote.to_amount, "to_amount")) {
2891
2953
  throw new Error(
2892
2954
  `quote from_amount ${quote.from_amount} is below the invoice amount \u2014 a negative spread is not a quote`
2893
2955
  );
@@ -2933,7 +2995,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2933
2995
  address,
2934
2996
  // What the lockup must carry: the quote's `from_amount` — the invoice
2935
2997
  // PLUS the corridor's fee, never the bare invoice amount.
2936
- fundAmount: quote.from_amount,
2998
+ fundAmount: quoteSats(quote.from_amount, "from_amount"),
2937
2999
  swapPkScript: script.pkScript,
2938
3000
  script,
2939
3001
  refundAddress,
@@ -2943,11 +3005,116 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
2943
3005
  };
2944
3006
  }
2945
3007
  var offerTermsFromQuote = (quote, assets) => {
2946
- if (Boolean(assets.wantAsset) === Boolean(assets.offerAsset)) {
2947
- throw new Error("set exactly one of wantAsset or offerAsset");
3008
+ if (!assets.wantAsset && !assets.offerAsset) {
3009
+ throw new Error("set at least one of wantAsset or offerAsset");
2948
3010
  }
2949
- return { wantAmount: BigInt(quote.to_amount), ...assets };
3011
+ const wantAmount = BigInt(quote.to_amount);
3012
+ if (assets.wantAsset) return { wantAmount, wantAsset: assets.wantAsset };
3013
+ return { wantAmount, offerAsset: assets.offerAsset };
3014
+ };
3015
+ var verifyOfferAddress = (quote, derived) => {
3016
+ const quotedAddress = quote.profile?.offer_address;
3017
+ const quotedScript = quote.profile?.offer_pk_script;
3018
+ const derivedScript = import_base10.hex.encode(derived.swapPkScript);
3019
+ if (quotedAddress !== derived.address || quotedScript !== derivedScript) {
3020
+ throw new AddressMismatch([derived.address], quotedAddress);
3021
+ }
3022
+ return derived;
2950
3023
  };
3024
+ var assertArkadeFundable = (input) => {
3025
+ const now = input.now ?? Math.floor(Date.now() / 1e3);
3026
+ if (!Number.isSafeInteger(input.quote.valid_until)) {
3027
+ throw gateError("quote_expired", "quote carries no valid_until");
3028
+ }
3029
+ if (now >= input.quote.valid_until) {
3030
+ throw gateError("quote_expired", `quote lapsed at ${input.quote.valid_until} (now ${now})`);
3031
+ }
3032
+ if (BigInt(input.quote.from_amount) <= 0n || BigInt(input.quote.to_amount) <= 0n) {
3033
+ throw gateError("quote_expired", "quote carries a non-positive amount");
3034
+ }
3035
+ };
3036
+ var quoteCarrierSats = (quote) => {
3037
+ const published = quote.carrier_sats === void 0 ? 0n : BigInt(quote.carrier_sats);
3038
+ return published > 0n ? published : ASSET_CARRIER_SATS;
3039
+ };
3040
+ async function requestArkadeSwap(wallet, arkServerUrl, transport, params) {
3041
+ if (!params.wantAsset && !params.offerAsset) {
3042
+ throw new Error("set at least one of wantAsset or offerAsset; BTC-to-BTC is not a swap");
3043
+ }
3044
+ const rfqId = params.rfqId ?? newRfqId();
3045
+ const amountSide = params.amountSide ?? "from";
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
+ amountSide,
3062
+ makerPkScript,
3063
+ makerPublicKey
3064
+ })
3065
+ );
3066
+ if (quote.pair !== pair) {
3067
+ throw new Error(`solver quoted ${JSON.stringify(quote.pair)}, not the requested ${pair}`);
3068
+ }
3069
+ assertArkadeFundable({ quote, ...params.now !== void 0 ? { now: params.now } : {} });
3070
+ const terms = offerTermsFromQuote(quote, {
3071
+ ...params.offerAsset !== void 0 ? { offerAsset: params.offerAsset } : {},
3072
+ ...params.wantAsset !== void 0 ? { wantAsset: params.wantAsset } : {}
3073
+ });
3074
+ const quoted = amountSide === "from" ? quote.from_amount : quote.to_amount;
3075
+ if (BigInt(quoted).toString() !== canonicalAssetAmount(params.amount)) {
3076
+ throw new Error(
3077
+ `quote ${amountSide}_amount ${quoted} does not match the requested ${canonicalAssetAmount(params.amount)}`
3078
+ );
3079
+ }
3080
+ if (params.maxFromAmount !== void 0) {
3081
+ const cap = BigInt(canonicalAssetAmount(params.maxFromAmount));
3082
+ if (BigInt(quote.from_amount) > cap) {
3083
+ throw gateError(
3084
+ "quote_amount_rejected",
3085
+ `quote from_amount ${quote.from_amount} exceeds maxFromAmount ${cap}`
3086
+ );
3087
+ }
3088
+ }
3089
+ if (params.minToAmount !== void 0) {
3090
+ const floorAmount = BigInt(canonicalAssetAmount(params.minToAmount));
3091
+ if (BigInt(quote.to_amount) < floorAmount) {
3092
+ throw gateError(
3093
+ "quote_amount_rejected",
3094
+ `quote to_amount ${quote.to_amount} is under minToAmount ${floorAmount}`
3095
+ );
3096
+ }
3097
+ }
3098
+ const offer = await createOffer(wallet, arkServerUrl, {
3099
+ wantAmount: terms.wantAmount,
3100
+ ...terms.wantAsset !== void 0 ? { wantAsset: terms.wantAsset } : {},
3101
+ ...terms.offerAsset !== void 0 ? { offerAsset: terms.offerAsset } : {},
3102
+ ...params.emulatorPubkey !== void 0 ? { emulatorPubkey: params.emulatorPubkey } : {}
3103
+ });
3104
+ verifyOfferAddress(quote, offer);
3105
+ const fundAmount = BigInt(quote.from_amount);
3106
+ return {
3107
+ rfqId,
3108
+ quote,
3109
+ pair,
3110
+ address: offer.address,
3111
+ fundAmount,
3112
+ carrierSats: params.offerAsset !== void 0 ? quoteCarrierSats(quote) : 0n,
3113
+ swapPkScript: offer.swapPkScript,
3114
+ offerHex: offer.offerHex,
3115
+ extension: offer.extension
3116
+ };
3117
+ }
2951
3118
  var l1NetworkFromArk = (network) => network === "bitcoin" ? "bitcoin" : network === "regtest" ? "regtest" : "testnet";
2952
3119
  var onchainSendRequest = (input) => ({
2953
3120
  v: 1,
@@ -3038,7 +3205,8 @@ function deriveOnchainSend(input) {
3038
3205
  l1Network: input.l1Network,
3039
3206
  refundLocktime,
3040
3207
  htlcLocktime,
3041
- minConfirmations
3208
+ minConfirmations,
3209
+ expectedAmount: quoteSats(quote.to_amount, "to_amount")
3042
3210
  };
3043
3211
  }
3044
3212
  async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
@@ -3101,7 +3269,8 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
3101
3269
  rfqId,
3102
3270
  quote,
3103
3271
  address: derived.address,
3104
- fundAmount: quote.from_amount,
3272
+ fundAmount: quoteSats(quote.from_amount, "from_amount"),
3273
+ expectedAmount: quoteSats(quote.to_amount, "to_amount"),
3105
3274
  swapPkScript: derived.swapPkScript,
3106
3275
  script: derived.script,
3107
3276
  refundAddress,
@@ -3114,7 +3283,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
3114
3283
  };
3115
3284
  }
3116
3285
  var assertQuotedAmount = (quote, amountSide, amount) => {
3117
- const quoted = amountSide === "from" ? quote.from_amount : quote.to_amount;
3286
+ const quoted = amountSide === "from" ? quoteSats(quote.from_amount, "from_amount") : quoteSats(quote.to_amount, "to_amount");
3118
3287
  if (quoted !== amount) {
3119
3288
  throw new Error(
3120
3289
  `quote ${amountSide === "from" ? "from_amount" : "to_amount"} ${quoted} does not match the requested ${amount} \u2014 not this trade's quote`
@@ -3173,7 +3342,7 @@ var assertReceivable = (input) => {
3173
3342
  `a payment at the deadline would leave under ${minClaimWindow}s to claim before the solver's refund opens`
3174
3343
  );
3175
3344
  }
3176
- if (input.maxPayAmount !== void 0 && input.quote.from_amount > input.maxPayAmount) {
3345
+ if (input.maxPayAmount !== void 0 && quoteSats(input.quote.from_amount, "from_amount") > input.maxPayAmount) {
3177
3346
  throw gateError(
3178
3347
  "price_too_high",
3179
3348
  `quote asks ${input.quote.from_amount} sats, above the ${input.maxPayAmount} ceiling`
@@ -3304,8 +3473,8 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
3304
3473
  rfqId,
3305
3474
  quote,
3306
3475
  invoice: derived.invoice,
3307
- payAmount: quote.from_amount,
3308
- expectedAmount: quote.to_amount,
3476
+ payAmount: quoteSats(quote.from_amount, "from_amount"),
3477
+ expectedAmount: quoteSats(quote.to_amount, "to_amount"),
3309
3478
  invoiceExpiresAt: payDeadline,
3310
3479
  address: derived.address,
3311
3480
  swapPkScript: derived.swapPkScript,
@@ -3431,8 +3600,8 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
3431
3600
  rfqId,
3432
3601
  quote,
3433
3602
  address: derived.address,
3434
- fundAmount: quote.from_amount,
3435
- expectedAmount: quote.to_amount,
3603
+ fundAmount: quoteSats(quote.from_amount, "from_amount"),
3604
+ expectedAmount: quoteSats(quote.to_amount, "to_amount"),
3436
3605
  swapPkScript: derived.swapPkScript,
3437
3606
  script: derived.script,
3438
3607
  htlc: derived.htlc,
@@ -5144,6 +5313,21 @@ var RfqSwapManager = class {
5144
5313
  });
5145
5314
  if (action === "claim" && phase.phase === "claimable") {
5146
5315
  if (swap.claimTxid) return "continue";
5316
+ if (!Number.isSafeInteger(swap.expectedAmount) || swap.expectedAmount <= 0) {
5317
+ this.block(
5318
+ swap,
5319
+ `expectedAmount is not a positive number of sats (${String(swap.expectedAmount)}), so the funded value cannot be checked \u2014 refusing to publish the preimage`
5320
+ );
5321
+ return "handled";
5322
+ }
5323
+ const filled = Number(phase.utxo.amount);
5324
+ if (!Number.isFinite(filled) || filled < swap.expectedAmount) {
5325
+ this.block(
5326
+ swap,
5327
+ `fill holds ${filled} sats, below the agreed ${swap.expectedAmount} \u2014 refusing to publish the preimage`
5328
+ );
5329
+ return "handled";
5330
+ }
5147
5331
  if (this.callbacks && !this.callbacks.claimOnchain) {
5148
5332
  this.block(
5149
5333
  swap,
@@ -5197,6 +5381,8 @@ var RfqSwapManager = class {
5197
5381
  if (pushed) {
5198
5382
  swap.refundArkTxid = pushed.arkTxid;
5199
5383
  this.touch(swap);
5384
+ } else if (now < swap.refundLocktime + REFUND_MTP_LAG_SECONDS) {
5385
+ return;
5200
5386
  }
5201
5387
  this.setState(swap, "refunded");
5202
5388
  this.emitAction(swap, "refundArkade");
@@ -5565,6 +5751,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5565
5751
  arkadeAssetLeg,
5566
5752
  arkadeRefunder,
5567
5753
  arkadeSwapRequest,
5754
+ assertArkadeFundable,
5568
5755
  assertFundable,
5569
5756
  assertReceivable,
5570
5757
  awaitLockupFunding,
@@ -5573,6 +5760,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5573
5760
  buildHtlcClaim,
5574
5761
  buildHtlcRefund,
5575
5762
  cancelOffer,
5763
+ canonicalAssetAmount,
5576
5764
  chainSourceFrom,
5577
5765
  claimOnchainFill,
5578
5766
  claimPacketShape,
@@ -5613,6 +5801,8 @@ async function lockupTxids(indexer, record, wantFunding) {
5613
5801
  newPreimage,
5614
5802
  newRfqId,
5615
5803
  nextOnchainAction,
5804
+ normalizeMakerPkScript,
5805
+ normalizeMakerPublicKey,
5616
5806
  offerTermsFromQuote,
5617
5807
  offerVtxoScript,
5618
5808
  onchainHtlcScript,
@@ -5630,6 +5820,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5630
5820
  registerAssetSwapRestore,
5631
5821
  registerLockupContract,
5632
5822
  relayTransport,
5823
+ requestArkadeSwap,
5633
5824
  requestLightningReceive,
5634
5825
  requestLightningSend,
5635
5826
  requestOnchainReceive,
@@ -5667,6 +5858,7 @@ async function lockupTxids(indexer, record, wantFunding) {
5667
5858
  updateRfqSwapRecord,
5668
5859
  validatePlan,
5669
5860
  verifyLockupAddress,
5861
+ verifyOfferAddress,
5670
5862
  verifyReceiveInvoice,
5671
5863
  watchOfferSwaps
5672
5864
  });
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-BwasQwkR.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-BwasQwkR.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-Cop29z0c.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-Cop29z0c.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-B_2hM0xk.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-B_2hM0xk.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-B95nPfaa.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-B95nPfaa.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-CucjFZN1.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-CucjFZN1.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-BrTpVPLy.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-BrTpVPLy.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-B_2hM0xk.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-B_2hM0xk.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-B95nPfaa.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-B95nPfaa.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