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