@morpho-dev/router 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.
@@ -5,6 +5,7 @@ var chains$1 = require('viem/chains');
5
5
  var v4 = require('zod/v4');
6
6
  var zodOpenapi = require('zod-openapi');
7
7
  var viem = require('viem');
8
+ var jsBase64 = require('js-base64');
8
9
  var nodeServer = require('@hono/node-server');
9
10
  var hono = require('hono');
10
11
  var fs = require('fs');
@@ -8256,8 +8257,6 @@ function* batch(array, batchSize) {
8256
8257
  yield array.slice(i, i + batchSize);
8257
8258
  }
8258
8259
  }
8259
-
8260
- // src/utils/cursor.ts
8261
8260
  function validateCursor(cursor) {
8262
8261
  if (!cursor || typeof cursor !== "object") {
8263
8262
  throw new Error("Cursor must be an object");
@@ -8328,11 +8327,11 @@ function validateCursor(cursor) {
8328
8327
  return true;
8329
8328
  }
8330
8329
  function encodeCursor(c) {
8331
- return Buffer.from(JSON.stringify(c)).toString("base64url");
8330
+ return jsBase64.Base64.encodeURL(JSON.stringify(c));
8332
8331
  }
8333
8332
  function decodeCursor(token) {
8334
8333
  if (!token) return null;
8335
- const decoded = JSON.parse(Buffer.from(token, "base64url").toString());
8334
+ const decoded = JSON.parse(jsBase64.Base64.decode(token));
8336
8335
  validateCursor(decoded);
8337
8336
  return decoded;
8338
8337
  }
@@ -8444,19 +8443,33 @@ var GetOffersQueryParams = v4.z.object({
8444
8443
  example: "1500000000000000000"
8445
8444
  }),
8446
8445
  // Time range
8447
- min_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8448
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
8449
- example: "1700000000"
8450
- }),
8451
- max_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8452
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
8453
- example: "1800000000"
8454
- }),
8455
- min_expiry: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8446
+ min_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
8447
+ try {
8448
+ return mempool.Maturity.from(maturity);
8449
+ } catch (e) {
8450
+ ctx.addIssue({
8451
+ code: "custom",
8452
+ message: e.message
8453
+ });
8454
+ return v4.z.NEVER;
8455
+ }
8456
+ }).optional(),
8457
+ max_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
8458
+ try {
8459
+ return mempool.Maturity.from(maturity);
8460
+ } catch (e) {
8461
+ ctx.addIssue({
8462
+ code: "custom",
8463
+ message: e.message
8464
+ });
8465
+ return v4.z.NEVER;
8466
+ }
8467
+ }).optional(),
8468
+ min_expiry: v4.z.coerce.number().int().optional().meta({
8456
8469
  description: "Minimum expiry timestamp (Unix timestamp in seconds)",
8457
8470
  example: "1700000000"
8458
8471
  }),
8459
- max_expiry: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8472
+ max_expiry: v4.z.coerce.number().int().optional().meta({
8460
8473
  description: "Maximum expiry timestamp (Unix timestamp in seconds)",
8461
8474
  example: "1800000000"
8462
8475
  }),
@@ -8478,51 +8491,81 @@ var GetOffersQueryParams = v4.z.object({
8478
8491
  {
8479
8492
  message: "Collateral tuple must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. Oracle and lltv are optional. Asset must be 0x + 40 hex chars, oracle must be 0x + 40 hex chars, lltv must be a number (e.g., 80.5)."
8480
8493
  }
8481
- ).transform((val) => {
8494
+ ).transform((val, ctx) => {
8482
8495
  return val.split("#").map((tuple) => {
8483
8496
  const parts = tuple.split(":");
8484
8497
  if (parts.length === 0 || !parts[0]) {
8485
- throw new v4.z.ZodError([
8486
- {
8487
- code: "custom",
8488
- message: "Asset address is required for each collateral tuple",
8489
- path: ["collateral_tuple"],
8490
- input: val
8491
- }
8492
- ]);
8498
+ ctx.addIssue({
8499
+ code: "custom",
8500
+ message: "Asset address is required for each collateral tuple",
8501
+ path: ["asset"],
8502
+ input: val
8503
+ });
8504
+ return v4.z.NEVER;
8493
8505
  }
8494
8506
  const asset = parts[0]?.toLowerCase();
8495
8507
  const oracle = parts[1]?.toLowerCase();
8496
8508
  const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
8497
8509
  if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
8498
- throw new v4.z.ZodError([
8499
- {
8510
+ ctx.addIssue({
8511
+ code: "custom",
8512
+ message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
8513
+ path: ["lltv"],
8514
+ input: val
8515
+ });
8516
+ return v4.z.NEVER;
8517
+ }
8518
+ let lltvValue;
8519
+ if (lltv !== void 0) {
8520
+ try {
8521
+ lltvValue = mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
8522
+ } catch (e) {
8523
+ ctx.issues.push({
8500
8524
  code: "custom",
8501
- message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
8502
- path: ["collateral_tuple"],
8503
- input: val
8504
- }
8505
- ]);
8525
+ message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
8526
+ input: lltv,
8527
+ path: ["lltv"]
8528
+ });
8529
+ return v4.z.NEVER;
8530
+ }
8506
8531
  }
8507
8532
  return {
8508
8533
  asset,
8509
8534
  oracle,
8510
- lltv
8535
+ lltv: lltvValue
8511
8536
  };
8512
8537
  });
8513
8538
  }).optional().meta({
8514
8539
  description: "Filter by collateral combinations in format: asset:oracle:lltv#asset2:oracle2:lltv2. Oracle and lltv are optional. Use # to separate multiple combinations.",
8515
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:8000#0x9876543210987654321098765432109876543210::8000"
8540
+ example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:94.5"
8516
8541
  }),
8517
- min_lltv: v4.z.string().regex(/^\d+(\.\d+)?$/, {
8518
- message: "Min LLTV must be a valid number"
8519
- }).transform((val) => parseFloat(val)).pipe(v4.z.number().min(0).max(100)).optional().meta({
8542
+ min_lltv: v4.z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).transform((lltv, ctx) => {
8543
+ try {
8544
+ return mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
8545
+ } catch (e) {
8546
+ ctx.addIssue({
8547
+ code: "custom",
8548
+ message: e.message,
8549
+ input: lltv
8550
+ });
8551
+ return v4.z.NEVER;
8552
+ }
8553
+ }).optional().meta({
8520
8554
  description: "Minimum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 80.5 = 80.5%)",
8521
8555
  example: "80.5"
8522
8556
  }),
8523
- max_lltv: v4.z.string().regex(/^\d+(\.\d+)?$/, {
8524
- message: "Max LLTV must be a valid number"
8525
- }).transform((val) => parseFloat(val)).pipe(v4.z.number().min(0).max(100)).optional().meta({
8557
+ max_lltv: v4.z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).transform((lltv, ctx) => {
8558
+ try {
8559
+ return mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
8560
+ } catch (e) {
8561
+ ctx.addIssue({
8562
+ code: "custom",
8563
+ message: e.message,
8564
+ input: lltv
8565
+ });
8566
+ return v4.z.NEVER;
8567
+ }
8568
+ }).optional().meta({
8526
8569
  description: "Maximum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 95.5 = 95.5%)",
8527
8570
  example: "95.5"
8528
8571
  }),
@@ -8615,68 +8658,89 @@ var MatchOffersQueryParams = v4.z.object({
8615
8658
  }),
8616
8659
  // Collateral filtering
8617
8660
  collaterals: v4.z.string().regex(
8618
- /^(0x[a-fA-F0-9]{40}:0x[a-fA-F0-9]{40}:\d+)(#0x[a-fA-F0-9]{40}:0x[a-fA-F0-9]{40}:\d+)*$/,
8661
+ /^(0x[a-fA-F0-9]{40}:0x[a-fA-F0-9]{40}:[0-9]+(\.[0-9]+)?)(#0x[a-fA-F0-9]{40}:0x[a-fA-F0-9]{40}:[0-9]+(\.[0-9]+)?)*$/,
8619
8662
  {
8620
8663
  message: "Collaterals must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. All fields are required for each collateral."
8621
8664
  }
8622
- ).transform((val) => {
8665
+ ).transform((val, ctx) => {
8623
8666
  return val.split("#").map((collateral) => {
8624
8667
  const parts = collateral.split(":");
8625
8668
  if (parts.length !== 3) {
8626
- throw new v4.z.ZodError([
8627
- {
8628
- code: "custom",
8629
- message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
8630
- path: ["collaterals"],
8631
- input: val
8632
- }
8633
- ]);
8669
+ ctx.addIssue({
8670
+ code: "custom",
8671
+ message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
8672
+ path: ["collaterals"],
8673
+ input: val
8674
+ });
8675
+ return v4.z.NEVER;
8634
8676
  }
8635
8677
  const [asset, oracle, lltvStr] = parts;
8636
8678
  if (!asset || !oracle || !lltvStr) {
8637
- throw new v4.z.ZodError([
8638
- {
8639
- code: "custom",
8640
- message: "Asset, oracle, and lltv are all required for each collateral",
8641
- path: ["collaterals"],
8642
- input: val
8643
- }
8644
- ]);
8679
+ ctx.addIssue({
8680
+ code: "custom",
8681
+ message: "Asset, oracle, and lltv are all required for each collateral",
8682
+ path: ["collaterals"],
8683
+ input: val
8684
+ });
8645
8685
  }
8646
- const lltv = BigInt(lltvStr);
8647
- if (lltv <= 0n) {
8648
- throw new v4.z.ZodError([
8649
- {
8686
+ let lltvValue;
8687
+ if (lltvStr !== void 0) {
8688
+ try {
8689
+ lltvValue = mempool.LLTV.from(viem.parseUnits(lltvStr, 16));
8690
+ } catch (e) {
8691
+ ctx.issues.push({
8650
8692
  code: "custom",
8651
- message: "LLTV must be a positive number",
8652
- path: ["collaterals"],
8653
- input: val
8654
- }
8655
- ]);
8693
+ message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
8694
+ input: lltvStr,
8695
+ path: ["lltv"]
8696
+ });
8697
+ return v4.z.NEVER;
8698
+ }
8656
8699
  }
8657
8700
  return {
8658
8701
  asset: asset.toLowerCase(),
8659
8702
  oracle: oracle.toLowerCase(),
8660
- lltv
8703
+ lltv: lltvValue
8661
8704
  };
8662
8705
  });
8663
8706
  }).optional().meta({
8664
8707
  description: "Collateral requirements in format: asset:oracle:lltv#asset2:oracle2:lltv2. Use # to separate multiple collaterals.",
8665
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:800000000000000000#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:900000000000000000"
8708
+ example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:94.5"
8666
8709
  }),
8667
8710
  // Maturity filtering
8668
- maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8669
- description: "Exact maturity timestamp (Unix timestamp in seconds)",
8670
- example: "1700000000"
8671
- }),
8672
- min_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8673
- description: "Minimum maturity timestamp (Unix timestamp in seconds, inclusive)",
8674
- example: "1700000000"
8675
- }),
8676
- max_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8677
- description: "Maximum maturity timestamp (Unix timestamp in seconds, inclusive)",
8678
- example: "1800000000"
8679
- }),
8711
+ maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
8712
+ try {
8713
+ return mempool.Maturity.from(maturity);
8714
+ } catch (e) {
8715
+ ctx.addIssue({
8716
+ code: "custom",
8717
+ message: e.message
8718
+ });
8719
+ return v4.z.NEVER;
8720
+ }
8721
+ }).optional(),
8722
+ min_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
8723
+ try {
8724
+ return mempool.Maturity.from(maturity);
8725
+ } catch (e) {
8726
+ ctx.addIssue({
8727
+ code: "custom",
8728
+ message: e.message
8729
+ });
8730
+ return v4.z.NEVER;
8731
+ }
8732
+ }).optional(),
8733
+ max_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
8734
+ try {
8735
+ return mempool.Maturity.from(maturity);
8736
+ } catch (e) {
8737
+ ctx.addIssue({
8738
+ code: "custom",
8739
+ message: e.message
8740
+ });
8741
+ return v4.z.NEVER;
8742
+ }
8743
+ }).optional(),
8680
8744
  // Asset and creator filtering
8681
8745
  loan_token: v4.z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
8682
8746
  message: "Loan asset must be a valid Ethereum address"
@@ -8741,8 +8805,10 @@ var schemas = {
8741
8805
  function parse(action, query) {
8742
8806
  return schemas[action].parse(query);
8743
8807
  }
8744
- function safeParse(action, query) {
8745
- return schemas[action].safeParse(query);
8808
+ function safeParse(action, query, error) {
8809
+ return schemas[action].safeParse(query, {
8810
+ error
8811
+ });
8746
8812
  }
8747
8813
 
8748
8814
  // src/core/apiSchema/openapi.ts
@@ -8794,7 +8860,7 @@ var paths = {
8794
8860
  }
8795
8861
  }
8796
8862
  },
8797
- "/v1/match-offers": {
8863
+ "/v1/offers/match": {
8798
8864
  get: {
8799
8865
  summary: "Match offers",
8800
8866
  description: "Find offers that match specific criteria",
@@ -8957,16 +9023,16 @@ async function get(config, parameters) {
8957
9023
  } else if (lltv !== void 0) {
8958
9024
  result += `:`;
8959
9025
  }
8960
- if (lltv !== void 0) result += `:${lltv}`;
9026
+ if (lltv !== void 0) result += `:${viem.formatUnits(lltv, 16)}`;
8961
9027
  return result;
8962
9028
  }).join("#");
8963
9029
  url.searchParams.set("collateral_tuple", tupleStr);
8964
9030
  }
8965
9031
  if (parameters.minLltv !== void 0) {
8966
- url.searchParams.set("min_lltv", parameters.minLltv.toString());
9032
+ url.searchParams.set("min_lltv", viem.formatUnits(parameters.minLltv, 16));
8967
9033
  }
8968
9034
  if (parameters.maxLltv !== void 0) {
8969
- url.searchParams.set("max_lltv", parameters.maxLltv.toString());
9035
+ url.searchParams.set("max_lltv", viem.formatUnits(parameters.maxLltv, 16));
8970
9036
  }
8971
9037
  if (parameters.sortBy) {
8972
9038
  url.searchParams.set("sort_by", parameters.sortBy);
@@ -8988,14 +9054,14 @@ async function get(config, parameters) {
8988
9054
  };
8989
9055
  }
8990
9056
  async function match(config, parameters) {
8991
- const url = new URL(`${config.url.toString()}v1/match-offers`);
9057
+ const url = new URL(`${config.url.toString()}v1/offers/match`);
8992
9058
  url.searchParams.set("side", parameters.side);
8993
9059
  url.searchParams.set("chain_id", parameters.chainId.toString());
8994
9060
  if (parameters.rate !== void 0) {
8995
9061
  url.searchParams.set("rate", parameters.rate.toString());
8996
9062
  }
8997
9063
  if (parameters.collaterals?.length) {
8998
- const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${lltv}`).join("#");
9064
+ const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${viem.formatUnits(lltv, 16)}`).join("#");
8999
9065
  url.searchParams.set("collaterals", collateralsStr);
9000
9066
  }
9001
9067
  if (parameters.maturity !== void 0) {
@@ -9033,12 +9099,12 @@ async function getApi(config, url) {
9033
9099
  const pathname = url.pathname;
9034
9100
  let action;
9035
9101
  switch (true) {
9102
+ case pathname.includes("/v1/offers/match"):
9103
+ action = "match_offers";
9104
+ break;
9036
9105
  case pathname.includes("/v1/offers"):
9037
9106
  action = "get_offers";
9038
9107
  break;
9039
- case pathname.includes("/v1/match-offers"):
9040
- action = "match_offers";
9041
- break;
9042
9108
  default:
9043
9109
  throw new HttpGetOffersFailedError("Unknown endpoint", {
9044
9110
  details: `Unsupported path: ${pathname}`
@@ -9249,6 +9315,7 @@ function memory(parameters) {
9249
9315
  }
9250
9316
  creators && (creators = creators.map((c) => c.toLowerCase()));
9251
9317
  loanTokens && (loanTokens = loanTokens.map((lt) => lt.toLowerCase()));
9318
+ callbackAddresses && (callbackAddresses = callbackAddresses.map((ca) => ca.toLowerCase()));
9252
9319
  collateralAssets && (collateralAssets = collateralAssets.map((ca) => ca.toLowerCase()));
9253
9320
  collateralOracles && (collateralOracles = collateralOracles.map((co) => co.toLowerCase()));
9254
9321
  collateralTuple && (collateralTuple = collateralTuple.map((ct) => ({
@@ -9285,12 +9352,8 @@ function memory(parameters) {
9285
9352
  )
9286
9353
  )
9287
9354
  ));
9288
- minLltv && (offers = offers.filter(
9289
- (o) => o.collaterals.every((c) => c.lltv >= mempool.LLTV.from(viem.parseUnits(minLltv.toString(), 16)))
9290
- ));
9291
- maxLltv && (offers = offers.filter(
9292
- (o) => o.collaterals.every((c) => c.lltv <= mempool.LLTV.from(viem.parseUnits(maxLltv.toString(), 16)))
9293
- ));
9355
+ minLltv && (offers = offers.filter((o) => o.collaterals.every((c) => c.lltv >= minLltv)));
9356
+ maxLltv && (offers = offers.filter((o) => o.collaterals.every((c) => c.lltv <= maxLltv)));
9294
9357
  offers = offers.sort((a, b) => sort(sortBy, sortOrder, a, b));
9295
9358
  let nextCursor = null;
9296
9359
  if (offers.length > limit) {
@@ -9337,8 +9400,8 @@ function memory(parameters) {
9337
9400
  limit = 20
9338
9401
  } = params;
9339
9402
  const now = mempool.Time.now();
9340
- const buy = side !== "buy";
9341
- const sortOrder = buy ? "asc" : "desc";
9403
+ const isBuying = side === "buy";
9404
+ const sortOrder = isBuying ? "desc" : "asc";
9342
9405
  let offers = Array.from(map.values()).map((o) => ({
9343
9406
  ...o,
9344
9407
  consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
@@ -9351,22 +9414,27 @@ function memory(parameters) {
9351
9414
  offers = offers.filter(
9352
9415
  (o) => sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)
9353
9416
  );
9354
- offers = offers.filter((o) => o.hash !== cursor.hash);
9355
9417
  }
9356
- offers = offers.filter((o) => o.buy === buy);
9418
+ offers = offers.filter((o) => o.buy === !isBuying);
9357
9419
  offers = offers.filter((o) => o.chainId === BigInt(chainId));
9358
9420
  offers = offers.filter((o) => o.expiry >= now);
9359
- rate && (offers = offers.filter((o) => buy ? o.rate >= rate : o.rate <= rate));
9421
+ rate && (offers = offers.filter((o) => isBuying ? o.rate >= rate : o.rate <= rate));
9360
9422
  collaterals.length > 0 && (offers = offers.filter(
9361
- (o) => buy ? collaterals.every((c) => {
9362
- return o.collaterals.some(
9363
- (oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9364
- );
9365
- }) : o.collaterals.every((oc) => {
9366
- return collaterals.some(
9367
- (c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9368
- );
9369
- })
9423
+ (o) => isBuying ? (
9424
+ // when wanting to buy, sell offer collaterals ⊆ user buy collaterals
9425
+ o.collaterals.every((oc) => {
9426
+ return collaterals.some(
9427
+ (c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9428
+ );
9429
+ })
9430
+ ) : (
9431
+ // when wanting to sell, user sell collaterals ⊆ buy offer collaterals
9432
+ collaterals.every((c) => {
9433
+ return o.collaterals.some(
9434
+ (oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9435
+ );
9436
+ })
9437
+ )
9370
9438
  ));
9371
9439
  maturity && (offers = offers.filter((o) => o.maturity === maturity));
9372
9440
  minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
@@ -9404,6 +9472,7 @@ function memory(parameters) {
9404
9472
  }
9405
9473
  offers = Array.from(byGroup.values());
9406
9474
  offers = offers.sort((a, b) => sort("rate", sortOrder, a, b));
9475
+ cursor && (offers = offers.filter((o) => o.hash !== cursor.hash));
9407
9476
  let nextCursor = null;
9408
9477
  if (offers.length > limit) {
9409
9478
  const last = offers[limit - 1];
@@ -9436,9 +9505,12 @@ function memory(parameters) {
9436
9505
  return deleted;
9437
9506
  },
9438
9507
  updateStatus: async (parameters2) => {
9439
- if (!map.has(parameters2.offerHash.toLowerCase())) return;
9440
- map.set(parameters2.offerHash.toLowerCase(), {
9441
- ...map.get(parameters2.offerHash.toLowerCase()),
9508
+ const key = parameters2.offerHash.toLowerCase();
9509
+ const existing = map.get(key);
9510
+ if (!existing) return;
9511
+ if (existing.status === parameters2.status) return;
9512
+ map.set(key, {
9513
+ ...existing,
9442
9514
  status: parameters2.status,
9443
9515
  metadata: parameters2.metadata
9444
9516
  });
@@ -9504,7 +9576,7 @@ async function serve(parameters) {
9504
9576
  return mempool.Mempool.error(err, c);
9505
9577
  }
9506
9578
  });
9507
- app.get("/v1/match-offers", async (c) => {
9579
+ app.get("/v1/offers/match", async (c) => {
9508
9580
  try {
9509
9581
  const params = parse("match_offers", c.req.query());
9510
9582
  const offers = await store.findMatchingOffers({