@morpho-dev/router 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.
Files changed (32) hide show
  1. package/dist/index.browser.d.cts +139 -12
  2. package/dist/index.browser.d.ts +139 -12
  3. package/dist/index.browser.js +533 -92
  4. package/dist/index.browser.js.map +1 -1
  5. package/dist/index.browser.mjs +534 -89
  6. package/dist/index.browser.mjs.map +1 -1
  7. package/dist/index.node.d.cts +132 -750
  8. package/dist/index.node.d.ts +132 -750
  9. package/dist/index.node.js +895 -1302
  10. package/dist/index.node.js.map +1 -1
  11. package/dist/index.node.mjs +894 -1291
  12. package/dist/index.node.mjs.map +1 -1
  13. package/package.json +3 -12
  14. package/dist/drizzle/0000_add-offers-table.sql +0 -37
  15. package/dist/drizzle/0001_create_offer_status_relation.sql +0 -10
  16. package/dist/drizzle/0002_add_created_at_in_offer_status_relation.sql +0 -3
  17. package/dist/drizzle/0003_add-cursor-indices-to-offers.sql +0 -6
  18. package/dist/drizzle/0004_offer-start.sql +0 -1
  19. package/dist/drizzle/0005_rename-price-token-buy.sql +0 -8
  20. package/dist/drizzle/0006_rename-buy.sql +0 -3
  21. package/dist/drizzle/0007_rename-offering.sql +0 -3
  22. package/dist/drizzle/0008_add-consumed-relation.sql +0 -10
  23. package/dist/drizzle/meta/0000_snapshot.json +0 -344
  24. package/dist/drizzle/meta/0001_snapshot.json +0 -426
  25. package/dist/drizzle/meta/0002_snapshot.json +0 -439
  26. package/dist/drizzle/meta/0003_snapshot.json +0 -553
  27. package/dist/drizzle/meta/0004_snapshot.json +0 -559
  28. package/dist/drizzle/meta/0005_snapshot.json +0 -559
  29. package/dist/drizzle/meta/0006_snapshot.json +0 -559
  30. package/dist/drizzle/meta/0007_snapshot.json +0 -559
  31. package/dist/drizzle/meta/0008_snapshot.json +0 -635
  32. package/dist/drizzle/meta/_journal.json +0 -69
@@ -68,7 +68,7 @@ var chains = {
68
68
  }
69
69
  };
70
70
 
71
- // src/core/Client.ts
71
+ // src/core/router/Client.ts
72
72
  var Client_exports = {};
73
73
  __export(Client_exports, {
74
74
  HttpForbiddenError: () => HttpForbiddenError,
@@ -139,6 +139,18 @@ var InvalidRouterOfferError = class extends mempool.Errors.BaseError {
139
139
  }
140
140
  };
141
141
 
142
+ // src/utils/index.ts
143
+ var utils_exports = {};
144
+ __export(utils_exports, {
145
+ batch: () => batch,
146
+ decodeCursor: () => decodeCursor,
147
+ encodeCursor: () => encodeCursor,
148
+ poll: () => poll,
149
+ retry: () => retry,
150
+ validateCursor: () => validateCursor,
151
+ wait: () => wait
152
+ });
153
+
142
154
  // src/utils/batch.ts
143
155
  function* batch(array, batchSize) {
144
156
  for (let i = 0; i < array.length; i += batchSize) {
@@ -249,6 +261,20 @@ function poll(fn, { interval }) {
249
261
  return unwatch;
250
262
  }
251
263
 
264
+ // src/utils/retry.ts
265
+ var retry = async (fn, attempts = 3, delayMs = 50) => {
266
+ let lastErr;
267
+ for (let i = 0; i < attempts; i++) {
268
+ try {
269
+ return await fn();
270
+ } catch (err) {
271
+ lastErr = err;
272
+ if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
273
+ }
274
+ }
275
+ throw lastErr;
276
+ };
277
+
252
278
  // src/core/apiSchema/requests.ts
253
279
  var MAX_LIMIT = 100;
254
280
  var DEFAULT_LIMIT = 20;
@@ -319,19 +345,33 @@ var GetOffersQueryParams = v4.z.object({
319
345
  example: "1500000000000000000"
320
346
  }),
321
347
  // Time range
322
- min_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
323
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
324
- example: "1700000000"
325
- }),
326
- max_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
327
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
328
- example: "1800000000"
329
- }),
330
- min_expiry: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
348
+ min_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
349
+ try {
350
+ return mempool.Maturity.from(maturity);
351
+ } catch (e) {
352
+ ctx.addIssue({
353
+ code: "custom",
354
+ message: e.message
355
+ });
356
+ return v4.z.NEVER;
357
+ }
358
+ }).optional(),
359
+ max_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
360
+ try {
361
+ return mempool.Maturity.from(maturity);
362
+ } catch (e) {
363
+ ctx.addIssue({
364
+ code: "custom",
365
+ message: e.message
366
+ });
367
+ return v4.z.NEVER;
368
+ }
369
+ }).optional(),
370
+ min_expiry: v4.z.coerce.number().int().optional().meta({
331
371
  description: "Minimum expiry timestamp (Unix timestamp in seconds)",
332
372
  example: "1700000000"
333
373
  }),
334
- max_expiry: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
374
+ max_expiry: v4.z.coerce.number().int().optional().meta({
335
375
  description: "Maximum expiry timestamp (Unix timestamp in seconds)",
336
376
  example: "1800000000"
337
377
  }),
@@ -353,51 +393,81 @@ var GetOffersQueryParams = v4.z.object({
353
393
  {
354
394
  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)."
355
395
  }
356
- ).transform((val) => {
396
+ ).transform((val, ctx) => {
357
397
  return val.split("#").map((tuple) => {
358
398
  const parts = tuple.split(":");
359
399
  if (parts.length === 0 || !parts[0]) {
360
- throw new v4.z.ZodError([
361
- {
362
- code: "custom",
363
- message: "Asset address is required for each collateral tuple",
364
- path: ["collateral_tuple"],
365
- input: val
366
- }
367
- ]);
400
+ ctx.addIssue({
401
+ code: "custom",
402
+ message: "Asset address is required for each collateral tuple",
403
+ path: ["asset"],
404
+ input: val
405
+ });
406
+ return v4.z.NEVER;
368
407
  }
369
408
  const asset = parts[0]?.toLowerCase();
370
409
  const oracle = parts[1]?.toLowerCase();
371
410
  const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
372
411
  if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
373
- throw new v4.z.ZodError([
374
- {
412
+ ctx.addIssue({
413
+ code: "custom",
414
+ message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
415
+ path: ["lltv"],
416
+ input: val
417
+ });
418
+ return v4.z.NEVER;
419
+ }
420
+ let lltvValue;
421
+ if (lltv !== void 0) {
422
+ try {
423
+ lltvValue = mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
424
+ } catch (e) {
425
+ ctx.issues.push({
375
426
  code: "custom",
376
- message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
377
- path: ["collateral_tuple"],
378
- input: val
379
- }
380
- ]);
427
+ message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
428
+ input: lltv,
429
+ path: ["lltv"]
430
+ });
431
+ return v4.z.NEVER;
432
+ }
381
433
  }
382
434
  return {
383
435
  asset,
384
436
  oracle,
385
- lltv
437
+ lltv: lltvValue
386
438
  };
387
439
  });
388
440
  }).optional().meta({
389
441
  description: "Filter by collateral combinations in format: asset:oracle:lltv#asset2:oracle2:lltv2. Oracle and lltv are optional. Use # to separate multiple combinations.",
390
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:8000#0x9876543210987654321098765432109876543210::8000"
442
+ example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:94.5"
391
443
  }),
392
- min_lltv: v4.z.string().regex(/^\d+(\.\d+)?$/, {
393
- message: "Min LLTV must be a valid number"
394
- }).transform((val) => parseFloat(val)).pipe(v4.z.number().min(0).max(100)).optional().meta({
444
+ 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) => {
445
+ try {
446
+ return mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
447
+ } catch (e) {
448
+ ctx.addIssue({
449
+ code: "custom",
450
+ message: e.message,
451
+ input: lltv
452
+ });
453
+ return v4.z.NEVER;
454
+ }
455
+ }).optional().meta({
395
456
  description: "Minimum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 80.5 = 80.5%)",
396
457
  example: "80.5"
397
458
  }),
398
- max_lltv: v4.z.string().regex(/^\d+(\.\d+)?$/, {
399
- message: "Max LLTV must be a valid number"
400
- }).transform((val) => parseFloat(val)).pipe(v4.z.number().min(0).max(100)).optional().meta({
459
+ 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) => {
460
+ try {
461
+ return mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
462
+ } catch (e) {
463
+ ctx.addIssue({
464
+ code: "custom",
465
+ message: e.message,
466
+ input: lltv
467
+ });
468
+ return v4.z.NEVER;
469
+ }
470
+ }).optional().meta({
401
471
  description: "Maximum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 95.5 = 95.5%)",
402
472
  example: "95.5"
403
473
  }),
@@ -490,68 +560,89 @@ var MatchOffersQueryParams = v4.z.object({
490
560
  }),
491
561
  // Collateral filtering
492
562
  collaterals: v4.z.string().regex(
493
- /^(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+)*$/,
563
+ /^(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]+)?)*$/,
494
564
  {
495
565
  message: "Collaterals must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. All fields are required for each collateral."
496
566
  }
497
- ).transform((val) => {
567
+ ).transform((val, ctx) => {
498
568
  return val.split("#").map((collateral) => {
499
569
  const parts = collateral.split(":");
500
570
  if (parts.length !== 3) {
501
- throw new v4.z.ZodError([
502
- {
503
- code: "custom",
504
- message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
505
- path: ["collaterals"],
506
- input: val
507
- }
508
- ]);
571
+ ctx.addIssue({
572
+ code: "custom",
573
+ message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
574
+ path: ["collaterals"],
575
+ input: val
576
+ });
577
+ return v4.z.NEVER;
509
578
  }
510
579
  const [asset, oracle, lltvStr] = parts;
511
580
  if (!asset || !oracle || !lltvStr) {
512
- throw new v4.z.ZodError([
513
- {
514
- code: "custom",
515
- message: "Asset, oracle, and lltv are all required for each collateral",
516
- path: ["collaterals"],
517
- input: val
518
- }
519
- ]);
581
+ ctx.addIssue({
582
+ code: "custom",
583
+ message: "Asset, oracle, and lltv are all required for each collateral",
584
+ path: ["collaterals"],
585
+ input: val
586
+ });
520
587
  }
521
- const lltv = BigInt(lltvStr);
522
- if (lltv <= 0n) {
523
- throw new v4.z.ZodError([
524
- {
588
+ let lltvValue;
589
+ if (lltvStr !== void 0) {
590
+ try {
591
+ lltvValue = mempool.LLTV.from(viem.parseUnits(lltvStr, 16));
592
+ } catch (e) {
593
+ ctx.issues.push({
525
594
  code: "custom",
526
- message: "LLTV must be a positive number",
527
- path: ["collaterals"],
528
- input: val
529
- }
530
- ]);
595
+ message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
596
+ input: lltvStr,
597
+ path: ["lltv"]
598
+ });
599
+ return v4.z.NEVER;
600
+ }
531
601
  }
532
602
  return {
533
603
  asset: asset.toLowerCase(),
534
604
  oracle: oracle.toLowerCase(),
535
- lltv
605
+ lltv: lltvValue
536
606
  };
537
607
  });
538
608
  }).optional().meta({
539
609
  description: "Collateral requirements in format: asset:oracle:lltv#asset2:oracle2:lltv2. Use # to separate multiple collaterals.",
540
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:800000000000000000#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:900000000000000000"
610
+ example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:94.5"
541
611
  }),
542
612
  // Maturity filtering
543
- maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
544
- description: "Exact maturity timestamp (Unix timestamp in seconds)",
545
- example: "1700000000"
546
- }),
547
- min_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
548
- description: "Minimum maturity timestamp (Unix timestamp in seconds, inclusive)",
549
- example: "1700000000"
550
- }),
551
- max_maturity: v4.z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
552
- description: "Maximum maturity timestamp (Unix timestamp in seconds, inclusive)",
553
- example: "1800000000"
554
- }),
613
+ maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
614
+ try {
615
+ return mempool.Maturity.from(maturity);
616
+ } catch (e) {
617
+ ctx.addIssue({
618
+ code: "custom",
619
+ message: e.message
620
+ });
621
+ return v4.z.NEVER;
622
+ }
623
+ }).optional(),
624
+ min_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
625
+ try {
626
+ return mempool.Maturity.from(maturity);
627
+ } catch (e) {
628
+ ctx.addIssue({
629
+ code: "custom",
630
+ message: e.message
631
+ });
632
+ return v4.z.NEVER;
633
+ }
634
+ }).optional(),
635
+ max_maturity: v4.z.coerce.number().int().positive().transform((maturity, ctx) => {
636
+ try {
637
+ return mempool.Maturity.from(maturity);
638
+ } catch (e) {
639
+ ctx.addIssue({
640
+ code: "custom",
641
+ message: e.message
642
+ });
643
+ return v4.z.NEVER;
644
+ }
645
+ }).optional(),
555
646
  // Asset and creator filtering
556
647
  loan_token: v4.z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
557
648
  message: "Loan asset must be a valid Ethereum address"
@@ -613,8 +704,10 @@ var schemas = {
613
704
  get_offers: GetOffersQueryParams,
614
705
  match_offers: MatchOffersQueryParams
615
706
  };
616
- function safeParse(action, query) {
617
- return schemas[action].safeParse(query);
707
+ function safeParse(action, query, error) {
708
+ return schemas[action].safeParse(query, {
709
+ error
710
+ });
618
711
  }
619
712
 
620
713
  // src/core/apiSchema/openapi.ts
@@ -666,7 +759,7 @@ var paths = {
666
759
  }
667
760
  }
668
761
  },
669
- "/v1/match-offers": {
762
+ "/v1/offers/match": {
670
763
  get: {
671
764
  summary: "Match offers",
672
765
  description: "Find offers that match specific criteria",
@@ -740,7 +833,7 @@ function fromResponse(offerResponse) {
740
833
  };
741
834
  }
742
835
 
743
- // src/core/Client.ts
836
+ // src/core/router/Client.ts
744
837
  function connect(opts) {
745
838
  const u = new URL(opts?.url || "https://router.morpho.dev");
746
839
  if (u.protocol !== "http:" && u.protocol !== "https:") {
@@ -817,16 +910,16 @@ async function get(config, parameters) {
817
910
  } else if (lltv !== void 0) {
818
911
  result += `:`;
819
912
  }
820
- if (lltv !== void 0) result += `:${lltv}`;
913
+ if (lltv !== void 0) result += `:${viem.formatUnits(lltv, 16)}`;
821
914
  return result;
822
915
  }).join("#");
823
916
  url.searchParams.set("collateral_tuple", tupleStr);
824
917
  }
825
918
  if (parameters.minLltv !== void 0) {
826
- url.searchParams.set("min_lltv", parameters.minLltv.toString());
919
+ url.searchParams.set("min_lltv", viem.formatUnits(parameters.minLltv, 16));
827
920
  }
828
921
  if (parameters.maxLltv !== void 0) {
829
- url.searchParams.set("max_lltv", parameters.maxLltv.toString());
922
+ url.searchParams.set("max_lltv", viem.formatUnits(parameters.maxLltv, 16));
830
923
  }
831
924
  if (parameters.sortBy) {
832
925
  url.searchParams.set("sort_by", parameters.sortBy);
@@ -848,14 +941,14 @@ async function get(config, parameters) {
848
941
  };
849
942
  }
850
943
  async function match(config, parameters) {
851
- const url = new URL(`${config.url.toString()}v1/match-offers`);
944
+ const url = new URL(`${config.url.toString()}v1/offers/match`);
852
945
  url.searchParams.set("side", parameters.side);
853
946
  url.searchParams.set("chain_id", parameters.chainId.toString());
854
947
  if (parameters.rate !== void 0) {
855
948
  url.searchParams.set("rate", parameters.rate.toString());
856
949
  }
857
950
  if (parameters.collaterals?.length) {
858
- const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${lltv}`).join("#");
951
+ const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${viem.formatUnits(lltv, 16)}`).join("#");
859
952
  url.searchParams.set("collaterals", collateralsStr);
860
953
  }
861
954
  if (parameters.maturity !== void 0) {
@@ -896,7 +989,7 @@ async function getApi(config, url) {
896
989
  case pathname.includes("/v1/offers"):
897
990
  action = "get_offers";
898
991
  break;
899
- case pathname.includes("/v1/match-offers"):
992
+ case pathname.includes("/v1/offers/match"):
900
993
  action = "match_offers";
901
994
  break;
902
995
  default:
@@ -970,6 +1063,358 @@ var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
970
1063
  }
971
1064
  };
972
1065
 
1066
+ // src/OfferStore/index.ts
1067
+ var OfferStore_exports = {};
1068
+ __export(OfferStore_exports, {
1069
+ memory: () => memory
1070
+ });
1071
+ function memory(parameters) {
1072
+ const map = parameters.offers;
1073
+ const filled = parameters.filled;
1074
+ const create = async (parameters2) => {
1075
+ if (map.has(parameters2.offer.hash.toLowerCase())) return parameters2.offer.hash;
1076
+ map.set(parameters2.offer.hash.toLowerCase(), {
1077
+ ...parameters2.offer,
1078
+ status: parameters2.status,
1079
+ metadata: parameters2.metadata
1080
+ });
1081
+ const chainId = parameters2.offer.chainId;
1082
+ const address = parameters2.offer.offering.toLowerCase();
1083
+ const nonce = parameters2.offer.nonce;
1084
+ const filledForChain = filled.get(chainId) || /* @__PURE__ */ new Map();
1085
+ const filledForOffering = filledForChain.get(address) || /* @__PURE__ */ new Map();
1086
+ if (!filledForOffering.has(nonce)) filledForOffering.set(nonce, 0n);
1087
+ filledForChain.set(address, filledForOffering);
1088
+ filled.set(chainId, filledForChain);
1089
+ return Promise.resolve(parameters2.offer.hash);
1090
+ };
1091
+ const sort = (sortBy, sortOrder, a, b) => {
1092
+ sortBy = sortBy || "expiry";
1093
+ sortOrder = sortOrder || "desc";
1094
+ const sortKey = sortBy === "amount" ? "assets" : sortBy;
1095
+ if (a[sortKey] === b[sortKey]) {
1096
+ if (a.hash === b.hash) return 0;
1097
+ return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : b.hash > a.hash ? 1 : -1;
1098
+ }
1099
+ switch (sortBy) {
1100
+ case "rate":
1101
+ if (a.rate === b.rate) {
1102
+ if (a.hash === b.hash) return 0;
1103
+ return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
1104
+ }
1105
+ return sortOrder === "asc" ? a.rate > b.rate ? 1 : -1 : b.rate > a.rate ? 1 : -1;
1106
+ case "maturity":
1107
+ if (a.maturity === b.maturity) {
1108
+ if (a.hash === b.hash) return 0;
1109
+ return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
1110
+ }
1111
+ return sortOrder === "asc" ? a.maturity > b.maturity ? 1 : -1 : b.maturity > a.maturity ? 1 : -1;
1112
+ case "expiry":
1113
+ if (a.expiry === b.expiry) {
1114
+ if (a.hash === b.hash) return 0;
1115
+ return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
1116
+ }
1117
+ return sortOrder === "asc" ? a.expiry > b.expiry ? 1 : -1 : b.expiry > a.expiry ? 1 : -1;
1118
+ case "amount":
1119
+ if (a.assets === b.assets) {
1120
+ if (a.hash === b.hash) return 0;
1121
+ return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
1122
+ }
1123
+ return sortOrder === "asc" ? a.assets > b.assets ? 1 : -1 : b.assets > a.assets ? 1 : -1;
1124
+ default:
1125
+ if (a.expiry === b.expiry) {
1126
+ if (a.hash === b.hash) return 0;
1127
+ return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
1128
+ }
1129
+ return sortOrder === "asc" ? a.expiry > b.expiry ? 1 : -1 : b.expiry > a.expiry ? 1 : -1;
1130
+ }
1131
+ };
1132
+ return {
1133
+ create,
1134
+ createMany: async (parameters2) => {
1135
+ return Promise.all(
1136
+ parameters2.map((p) => create({ offer: p.offer, status: p.status, metadata: p.metadata }))
1137
+ );
1138
+ },
1139
+ getAll: async (params) => {
1140
+ const { query } = params || {};
1141
+ let {
1142
+ creators,
1143
+ side,
1144
+ chains: chains2,
1145
+ loanTokens,
1146
+ status = ["valid"],
1147
+ callbackAddresses,
1148
+ minAmount,
1149
+ maxAmount,
1150
+ minRate,
1151
+ maxRate,
1152
+ minMaturity,
1153
+ maxMaturity,
1154
+ minExpiry,
1155
+ maxExpiry,
1156
+ collateralAssets,
1157
+ collateralOracles,
1158
+ collateralTuple,
1159
+ minLltv,
1160
+ maxLltv,
1161
+ sortBy = "expiry",
1162
+ sortOrder = "desc",
1163
+ cursor: queryCursor,
1164
+ limit = 20
1165
+ } = query || {};
1166
+ const now = mempool.Time.now();
1167
+ const buy = side === "buy";
1168
+ let offers = Array.from(map.values()).map((o) => ({
1169
+ ...o,
1170
+ consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
1171
+ })).filter((o) => o.consumed < o.assets);
1172
+ const cursor = decodeCursor(queryCursor);
1173
+ if (cursor) {
1174
+ if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
1175
+ throw new Error("Cursor does not match the current sort parameters");
1176
+ }
1177
+ switch (cursor.sort) {
1178
+ case "rate":
1179
+ offers = offers.filter(
1180
+ (o) => (sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)) && (o.rate !== BigInt(cursor.rate) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
1181
+ );
1182
+ break;
1183
+ case "maturity":
1184
+ offers = offers.filter(
1185
+ (o) => (sortOrder === "asc" ? o.maturity >= BigInt(cursor.maturity) : o.maturity <= BigInt(cursor.maturity)) && (o.maturity !== mempool.Maturity.from(cursor.maturity) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
1186
+ );
1187
+ break;
1188
+ case "expiry":
1189
+ offers = offers.filter(
1190
+ (o) => (sortOrder === "asc" ? o.expiry >= cursor.expiry : o.expiry <= cursor.expiry) && (o.expiry !== cursor.expiry || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
1191
+ );
1192
+ break;
1193
+ case "amount":
1194
+ offers = offers.filter(
1195
+ (o) => (sortOrder === "asc" ? o.assets >= BigInt(cursor.assets) : o.assets <= BigInt(cursor.assets)) && (o.assets !== BigInt(cursor.assets) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
1196
+ );
1197
+ break;
1198
+ default:
1199
+ throw new Error("Invalid sort parameter");
1200
+ }
1201
+ offers = offers.filter((o) => o.hash !== cursor.hash);
1202
+ }
1203
+ creators && (creators = creators.map((c) => c.toLowerCase()));
1204
+ loanTokens && (loanTokens = loanTokens.map((lt) => lt.toLowerCase()));
1205
+ callbackAddresses && (callbackAddresses = callbackAddresses.map((ca) => ca.toLowerCase()));
1206
+ collateralAssets && (collateralAssets = collateralAssets.map((ca) => ca.toLowerCase()));
1207
+ collateralOracles && (collateralOracles = collateralOracles.map((co) => co.toLowerCase()));
1208
+ collateralTuple && (collateralTuple = collateralTuple.map((ct) => ({
1209
+ asset: ct.asset.toLowerCase(),
1210
+ oracle: ct.oracle?.toLowerCase()
1211
+ })));
1212
+ offers = offers.filter((o) => o.expiry >= now);
1213
+ creators && (offers = offers.filter((o) => creators.includes(o.offering.toLowerCase())));
1214
+ side && (offers = offers.filter((o) => o.buy === buy));
1215
+ chains2 && (offers = offers.filter((o) => chains2.includes(Number(o.chainId))));
1216
+ loanTokens && (offers = offers.filter((o) => loanTokens.includes(o.loanToken.toLowerCase())));
1217
+ status && (offers = offers.filter((o) => status.includes(o.status)));
1218
+ callbackAddresses && (offers = offers.filter(
1219
+ (o) => callbackAddresses.includes(o.callback.address.toLowerCase())
1220
+ ));
1221
+ minAmount && (offers = offers.filter((o) => o.assets >= minAmount));
1222
+ maxAmount && (offers = offers.filter((o) => o.assets <= maxAmount));
1223
+ minRate && (offers = offers.filter((o) => o.rate >= minRate));
1224
+ maxRate && (offers = offers.filter((o) => o.rate <= maxRate));
1225
+ minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
1226
+ maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
1227
+ minExpiry && (offers = offers.filter((o) => o.expiry >= minExpiry));
1228
+ maxExpiry && (offers = offers.filter((o) => o.expiry <= maxExpiry));
1229
+ collateralAssets && (offers = offers.filter(
1230
+ (o) => o.collaterals.some((c) => collateralAssets.includes(c.asset.toLowerCase()))
1231
+ ));
1232
+ collateralOracles && (offers = offers.filter(
1233
+ (o) => o.collaterals.some((c) => collateralOracles.includes(c.oracle.toLowerCase()))
1234
+ ));
1235
+ collateralTuple && (offers = offers.filter(
1236
+ (o) => o.collaterals.some(
1237
+ (c) => collateralTuple.some(
1238
+ (ct) => c.asset.toLowerCase() === ct.asset.toLowerCase() && (ct.oracle ? c.oracle.toLowerCase() === ct.oracle.toLowerCase() : true) && (ct.lltv ? c.lltv === mempool.LLTV.from(BigInt(ct.lltv)) : true)
1239
+ )
1240
+ )
1241
+ ));
1242
+ minLltv && (offers = offers.filter((o) => o.collaterals.every((c) => c.lltv >= minLltv)));
1243
+ maxLltv && (offers = offers.filter((o) => o.collaterals.every((c) => c.lltv <= maxLltv)));
1244
+ offers = offers.sort((a, b) => sort(sortBy, sortOrder, a, b));
1245
+ let nextCursor = null;
1246
+ if (offers.length > limit) {
1247
+ const last = offers[limit - 1];
1248
+ const base = {
1249
+ sort: sortBy,
1250
+ dir: sortOrder,
1251
+ hash: last.hash
1252
+ };
1253
+ switch (sortBy) {
1254
+ case "rate":
1255
+ base.rate = last.rate.toString();
1256
+ break;
1257
+ case "amount":
1258
+ base.assets = last.assets.toString();
1259
+ break;
1260
+ case "maturity":
1261
+ base.maturity = last.maturity;
1262
+ break;
1263
+ default:
1264
+ base.expiry = last.expiry;
1265
+ }
1266
+ nextCursor = encodeCursor(base);
1267
+ }
1268
+ offers = offers.slice(0, limit);
1269
+ return {
1270
+ offers,
1271
+ nextCursor
1272
+ };
1273
+ },
1274
+ findMatchingOffers: async (params) => {
1275
+ const {
1276
+ side,
1277
+ chainId,
1278
+ rate,
1279
+ collaterals = [],
1280
+ maturity,
1281
+ minMaturity,
1282
+ maxMaturity,
1283
+ loanToken,
1284
+ creator,
1285
+ status,
1286
+ cursor: queryCursor,
1287
+ limit = 20
1288
+ } = params;
1289
+ const now = mempool.Time.now();
1290
+ const isBuying = side === "buy";
1291
+ const sortOrder = isBuying ? "desc" : "asc";
1292
+ let offers = Array.from(map.values()).map((o) => ({
1293
+ ...o,
1294
+ consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
1295
+ })).filter((o) => o.consumed < o.assets);
1296
+ const cursor = decodeCursor(queryCursor);
1297
+ if (cursor) {
1298
+ if (cursor.sort !== "rate" || cursor.dir !== sortOrder) {
1299
+ throw new Error("Cursor does not match the current sort parameters");
1300
+ }
1301
+ offers = offers.filter(
1302
+ (o) => sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)
1303
+ );
1304
+ }
1305
+ offers = offers.filter((o) => o.buy === !isBuying);
1306
+ offers = offers.filter((o) => o.chainId === BigInt(chainId));
1307
+ offers = offers.filter((o) => o.expiry >= now);
1308
+ rate && (offers = offers.filter((o) => isBuying ? o.rate >= rate : o.rate <= rate));
1309
+ collaterals.length > 0 && (offers = offers.filter(
1310
+ (o) => isBuying ? (
1311
+ // when wanting to buy, sell offer collaterals ⊆ user buy collaterals
1312
+ o.collaterals.every((oc) => {
1313
+ return collaterals.some(
1314
+ (c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
1315
+ );
1316
+ })
1317
+ ) : (
1318
+ // when wanting to sell, user sell collaterals ⊆ buy offer collaterals
1319
+ collaterals.every((c) => {
1320
+ return o.collaterals.some(
1321
+ (oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
1322
+ );
1323
+ })
1324
+ )
1325
+ ));
1326
+ maturity && (offers = offers.filter((o) => o.maturity === maturity));
1327
+ minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
1328
+ maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
1329
+ loanToken && (offers = offers.filter((o) => o.loanToken.toLowerCase() === loanToken.toLowerCase()));
1330
+ creator && (offers = offers.filter((o) => o.offering.toLowerCase() === creator.toLowerCase()));
1331
+ status && (offers = offers.filter((o) => status.includes(o.status)));
1332
+ const byGroup = /* @__PURE__ */ new Map();
1333
+ for (const offer of offers) {
1334
+ const groupKey = `${offer.chainId}-${offer.offering.toLowerCase()}-${offer.nonce}-${offer.buy}`;
1335
+ const current = byGroup.get(groupKey);
1336
+ if (!current) {
1337
+ byGroup.set(groupKey, offer);
1338
+ continue;
1339
+ }
1340
+ const remainingCandidate = offer.assets - offer.consumed;
1341
+ const remainingCurrent = current.assets - current.consumed;
1342
+ let candidateIsBetter = false;
1343
+ if (offer.buy) {
1344
+ if (offer.rate !== current.rate) candidateIsBetter = offer.rate < current.rate;
1345
+ else if (remainingCandidate !== remainingCurrent)
1346
+ candidateIsBetter = remainingCandidate > remainingCurrent;
1347
+ else if (offer.maturity !== current.maturity)
1348
+ candidateIsBetter = offer.maturity > current.maturity;
1349
+ else candidateIsBetter = offer.hash < current.hash;
1350
+ } else {
1351
+ if (offer.rate !== current.rate) candidateIsBetter = offer.rate > current.rate;
1352
+ else if (remainingCandidate !== remainingCurrent)
1353
+ candidateIsBetter = remainingCandidate > remainingCurrent;
1354
+ else if (offer.maturity !== current.maturity)
1355
+ candidateIsBetter = offer.maturity > current.maturity;
1356
+ else candidateIsBetter = offer.hash < current.hash;
1357
+ }
1358
+ if (candidateIsBetter) byGroup.set(groupKey, offer);
1359
+ }
1360
+ offers = Array.from(byGroup.values());
1361
+ offers = offers.sort((a, b) => sort("rate", sortOrder, a, b));
1362
+ cursor && (offers = offers.filter((o) => o.hash !== cursor.hash));
1363
+ let nextCursor = null;
1364
+ if (offers.length > limit) {
1365
+ const last = offers[limit - 1];
1366
+ nextCursor = encodeCursor({
1367
+ sort: "rate",
1368
+ dir: sortOrder,
1369
+ hash: last.hash,
1370
+ rate: last.rate.toString()
1371
+ });
1372
+ }
1373
+ offers = offers.slice(0, limit);
1374
+ return {
1375
+ offers,
1376
+ nextCursor
1377
+ };
1378
+ },
1379
+ delete: async (hash) => {
1380
+ if (!map.has(hash.toLowerCase())) return false;
1381
+ map.delete(hash.toLowerCase());
1382
+ return true;
1383
+ },
1384
+ deleteMany: async (hashes) => {
1385
+ let deleted = 0;
1386
+ for (const hash of hashes) {
1387
+ if (map.has(hash.toLowerCase())) {
1388
+ map.delete(hash.toLowerCase());
1389
+ deleted++;
1390
+ }
1391
+ }
1392
+ return deleted;
1393
+ },
1394
+ updateStatus: async (parameters2) => {
1395
+ const key = parameters2.offerHash.toLowerCase();
1396
+ const existing = map.get(key);
1397
+ if (!existing) return;
1398
+ if (existing.status === parameters2.status) return;
1399
+ map.set(key, {
1400
+ ...existing,
1401
+ status: parameters2.status,
1402
+ metadata: parameters2.metadata
1403
+ });
1404
+ },
1405
+ updateConsumedAmount: async (parameters2) => {
1406
+ const chainId = parameters2.chainId;
1407
+ const address = parameters2.offering.toLowerCase();
1408
+ const nonce = parameters2.nonce;
1409
+ const filledForChain = filled.get(chainId) || /* @__PURE__ */ new Map();
1410
+ const filledForOffering = filledForChain.get(address) || /* @__PURE__ */ new Map();
1411
+ filledForOffering.set(nonce, parameters2.consumed);
1412
+ filledForChain.set(address, filledForOffering);
1413
+ filled.set(chainId, filledForChain);
1414
+ }
1415
+ };
1416
+ }
1417
+
973
1418
  // src/RouterEvent.ts
974
1419
  var RouterEvent_exports = {};
975
1420
  __export(RouterEvent_exports, {
@@ -1205,17 +1650,13 @@ function morpho(parameters) {
1205
1650
  }
1206
1651
 
1207
1652
  exports.Chain = Chain_exports;
1653
+ exports.OfferStore = OfferStore_exports;
1208
1654
  exports.Router = Client_exports;
1209
1655
  exports.RouterEvent = RouterEvent_exports;
1210
1656
  exports.RouterOffer = RouterOffer_exports;
1657
+ exports.Utils = utils_exports;
1211
1658
  exports.Validation = Validation_exports;
1212
1659
  exports.ValidationRule = ValidationRule_exports;
1213
- exports.batch = batch;
1214
- exports.decodeCursor = decodeCursor;
1215
- exports.encodeCursor = encodeCursor;
1216
- exports.poll = poll;
1217
- exports.validateCursor = validateCursor;
1218
- exports.wait = wait;
1219
1660
  Object.keys(mempool).forEach(function (k) {
1220
1661
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
1221
1662
  enumerable: true,