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