@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.
@@ -1,9 +1,9 @@
1
- import { Errors, Offer, Format, Time, Maturity, LLTV, Mempool } from '@morpho-dev/mempool';
1
+ import { Errors, LLTV, Maturity, Offer, Format, Time, Mempool } from '@morpho-dev/mempool';
2
2
  export * from '@morpho-dev/mempool';
3
3
  import { base, mainnet, optimism, foundry, abstractTestnet, acala, ancient8, ancient8Sepolia, anvil, apexTestnet, arbitrum, arbitrumGoerli, arbitrumNova, assetChainTestnet, astar, astarZkEVM, astarZkyoto, arbitrumSepolia, areonNetwork, areonNetworkTestnet, artelaTestnet, atletaOlympia, aurora, auroraTestnet, auroria, avalanche, avalancheFuji, b3Sepolia, b3, bahamut, baseGoerli, baseSepolia, beam, beamTestnet, bearNetworkChainMainnet, bearNetworkChainTestnet, berachainTestnet, berachainTestnetbArtio, bevmMainnet, bitkub, bitkubTestnet, bitTorrent, bitTorrentTestnet, blast, blastSepolia, bob, bobSepolia, boba, bobaSepolia, botanixTestnet, bronos, bronosTestnet, bsc, bscTestnet, bscGreenfield, btr, btrTestnet, bxn, bxnTestnet, canto, celo, celoAlfajores, chiliz, chips, classic, confluxESpace, confluxESpaceTestnet, coreDao, crab, cronos, cronoszkEVM, cronoszkEVMTestnet, cronosTestnet, crossbell, curtis, cyber, cyberTestnet, darwinia, dchain, dchainTestnet, defichainEvm, defichainEvmTestnet, degen, dfk, dodochainTestnet, dogechain, dreyerxMainnet, dreyerxTestnet, edgeless, edgelessTestnet, edgeware, edgewareTestnet, eon, eos, eosTestnet, etherlink, etherlinkTestnet, evmos, evmosTestnet, ekta, ektaTestnet, fantom, fantomSonicTestnet, fantomTestnet, fibo, filecoin, filecoinCalibration, filecoinHyperspace, flare, flareTestnet, flowPreviewnet, flowMainnet, flowTestnet, fluence, fluenceStage, fluenceTestnet, forma, fraxtal, fraxtalTestnet, funkiMainnet, funkiSepolia, fuse, fuseSparknet, iotex, iotexTestnet, jbc, jbcTestnet, karura, gobi, goerli, gnosis, gnosisChiado, gravity, ham, hardhat, harmonyOne, hashkeyTestnet, haqqMainnet, haqqTestedge2, hedera, hederaTestnet, hederaPreviewnet, holesky, immutableZkEvm, immutableZkEvmTestnet, inEVM, iota, iotaTestnet, kakarotSepolia, kava, kavaTestnet, kcc, klaytn, klaytnBaobab, kaia, kairos, koi, kroma, kromaSepolia, l3x, l3xTestnet, lightlinkPegasus, lightlinkPhoenix, linea, lineaGoerli, lineaSepolia, lineaTestnet, lisk, liskSepolia, localhost, lukso, luksoTestnet, lycan, lyra, mandala, manta, mantaSepoliaTestnet, mantaTestnet, mantle, mantleSepoliaTestnet, mantleTestnet, merlin, metachain, metachainIstanbul, metalL2, meter, meterTestnet, metis, metisGoerli, mev, mevTestnet, mintSepoliaTestnet, mode, modeTestnet, moonbaseAlpha, moonbeam, moonbeamDev, moonriver, morphHolesky, morphSepolia, nautilus, neonDevnet, neonMainnet, nexi, nexilix, oasys, oasisTestnet, okc, optimismGoerli, optimismSepolia, opBNB, opBNBTestnet, oortMainnetDev, otimDevnet, palm, palmTestnet, playfiAlbireo, pgn, pgnTestnet, phoenix, plinga, plumeTestnet, polygon, polygonAmoy, polygonMumbai, polygonZkEvm, polygonZkEvmCardona, polygonZkEvmTestnet, pulsechain, pulsechainV4, qMainnet, qTestnet, real, redbellyTestnet, redstone, reyaNetwork, rollux, rolluxTestnet, ronin, root, rootPorcini, rootstock, rootstockTestnet, rss3, rss3Sepolia, saigon, sapphire, sapphireTestnet, satoshiVM, satoshiVMTestnet, scroll, scrollSepolia, sei, seiDevnet, seiTestnet, sepolia, shapeSepolia, shimmer, shimmerTestnet, skaleBlockBrawlers, skaleCalypso, skaleCalypsoTestnet, skaleCryptoBlades, skaleCryptoColosseum, skaleEuropa, skaleEuropaTestnet, skaleExorde, skaleHumanProtocol, skaleNebula, skaleNebulaTestnet, skaleRazor, skaleTitan, skaleTitanTestnet, sketchpad, soneiumMinato, songbird, songbirdTestnet, sophonTestnet, spicy, shardeumSphinx, shibarium, shibariumTestnet, storyTestnet, stratis, syscoin, syscoinTestnet, taraxa, taiko, taikoHekla, taikoJolnir, taikoKatla, taikoTestnetSepolia, taraxaTestnet, telcoinTestnet, telos, telosTestnet, tenet, thaiChain, thunderTestnet, tron, unreal, vechain, wanchain, wanchainTestnet, wemix, wemixTestnet, xLayerTestnet, x1Testnet, xLayer, xai, xaiTestnet, xdc, xdcTestnet, xrSepolia, yooldoVerse, yooldoVerseTestnet, zetachain, zetachainAthensTestnet, zhejiang, zilliqa, zilliqaTestnet, zkFair, zkFairTestnet, zkLinkNova, zkLinkNovaSepoliaTestnet, zksync, zksyncInMemoryNode, zksyncLocalNode, zksyncSepoliaTestnet, zora, zoraSepolia, zoraTestnet, zircuitTestnet } from 'viem/chains';
4
4
  import { z } from 'zod/v4';
5
5
  import { createDocument } from 'zod-openapi';
6
- import { defineChain, hexToBytes as hexToBytes$1, bytesToHex as bytesToHex$1, maxUint256, parseUnits, keccak256 as keccak256$2, toHex, parseEther as parseEther$1, concatHex, pad, createClient, publicActions, walletActions, testActions, parseAbi as parseAbi$1, createTransport, withRetry, numberToHex as numberToHex$2, hexToBigInt as hexToBigInt$1, stringToHex as stringToHex$1, numberToBytes, encodeFunctionData as encodeFunctionData$1, createPublicClient, InternalRpcError, decodeFunctionResult as decodeFunctionResult$1 } from 'viem';
6
+ import { parseUnits, defineChain, hexToBytes as hexToBytes$1, bytesToHex as bytesToHex$1, maxUint256, formatUnits, keccak256 as keccak256$2, toHex, parseEther as parseEther$1, concatHex, pad, createClient, publicActions, walletActions, testActions, parseAbi as parseAbi$1, createTransport, withRetry, numberToHex as numberToHex$2, hexToBigInt as hexToBigInt$1, stringToHex as stringToHex$1, numberToBytes, encodeFunctionData as encodeFunctionData$1, createPublicClient, InternalRpcError, decodeFunctionResult as decodeFunctionResult$1 } from 'viem';
7
7
  import { serve as serve$1 } from '@hono/node-server';
8
8
  import { Hono } from 'hono';
9
9
  import fs from 'fs';
@@ -8419,19 +8419,33 @@ var GetOffersQueryParams = z.object({
8419
8419
  example: "1500000000000000000"
8420
8420
  }),
8421
8421
  // Time range
8422
- min_maturity: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8423
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
8424
- example: "1700000000"
8425
- }),
8426
- max_maturity: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8427
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
8428
- example: "1800000000"
8429
- }),
8430
- min_expiry: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8422
+ min_maturity: z.coerce.number().int().positive().transform((maturity, ctx) => {
8423
+ try {
8424
+ return Maturity.from(maturity);
8425
+ } catch (e) {
8426
+ ctx.addIssue({
8427
+ code: "custom",
8428
+ message: e.message
8429
+ });
8430
+ return z.NEVER;
8431
+ }
8432
+ }).optional(),
8433
+ max_maturity: z.coerce.number().int().positive().transform((maturity, ctx) => {
8434
+ try {
8435
+ return Maturity.from(maturity);
8436
+ } catch (e) {
8437
+ ctx.addIssue({
8438
+ code: "custom",
8439
+ message: e.message
8440
+ });
8441
+ return z.NEVER;
8442
+ }
8443
+ }).optional(),
8444
+ min_expiry: z.coerce.number().int().optional().meta({
8431
8445
  description: "Minimum expiry timestamp (Unix timestamp in seconds)",
8432
8446
  example: "1700000000"
8433
8447
  }),
8434
- max_expiry: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8448
+ max_expiry: z.coerce.number().int().optional().meta({
8435
8449
  description: "Maximum expiry timestamp (Unix timestamp in seconds)",
8436
8450
  example: "1800000000"
8437
8451
  }),
@@ -8453,51 +8467,81 @@ var GetOffersQueryParams = z.object({
8453
8467
  {
8454
8468
  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)."
8455
8469
  }
8456
- ).transform((val) => {
8470
+ ).transform((val, ctx) => {
8457
8471
  return val.split("#").map((tuple) => {
8458
8472
  const parts = tuple.split(":");
8459
8473
  if (parts.length === 0 || !parts[0]) {
8460
- throw new z.ZodError([
8461
- {
8462
- code: "custom",
8463
- message: "Asset address is required for each collateral tuple",
8464
- path: ["collateral_tuple"],
8465
- input: val
8466
- }
8467
- ]);
8474
+ ctx.addIssue({
8475
+ code: "custom",
8476
+ message: "Asset address is required for each collateral tuple",
8477
+ path: ["asset"],
8478
+ input: val
8479
+ });
8480
+ return z.NEVER;
8468
8481
  }
8469
8482
  const asset = parts[0]?.toLowerCase();
8470
8483
  const oracle = parts[1]?.toLowerCase();
8471
8484
  const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
8472
8485
  if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
8473
- throw new z.ZodError([
8474
- {
8486
+ ctx.addIssue({
8487
+ code: "custom",
8488
+ message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
8489
+ path: ["lltv"],
8490
+ input: val
8491
+ });
8492
+ return z.NEVER;
8493
+ }
8494
+ let lltvValue;
8495
+ if (lltv !== void 0) {
8496
+ try {
8497
+ lltvValue = LLTV.from(parseUnits(lltv.toString(), 16));
8498
+ } catch (e) {
8499
+ ctx.issues.push({
8475
8500
  code: "custom",
8476
- message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
8477
- path: ["collateral_tuple"],
8478
- input: val
8479
- }
8480
- ]);
8501
+ message: e instanceof LLTV.InvalidLLTVError || e instanceof LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
8502
+ input: lltv,
8503
+ path: ["lltv"]
8504
+ });
8505
+ return z.NEVER;
8506
+ }
8481
8507
  }
8482
8508
  return {
8483
8509
  asset,
8484
8510
  oracle,
8485
- lltv
8511
+ lltv: lltvValue
8486
8512
  };
8487
8513
  });
8488
8514
  }).optional().meta({
8489
8515
  description: "Filter by collateral combinations in format: asset:oracle:lltv#asset2:oracle2:lltv2. Oracle and lltv are optional. Use # to separate multiple combinations.",
8490
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:8000#0x9876543210987654321098765432109876543210::8000"
8516
+ example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:94.5"
8491
8517
  }),
8492
- min_lltv: z.string().regex(/^\d+(\.\d+)?$/, {
8493
- message: "Min LLTV must be a valid number"
8494
- }).transform((val) => parseFloat(val)).pipe(z.number().min(0).max(100)).optional().meta({
8518
+ min_lltv: z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).transform((lltv, ctx) => {
8519
+ try {
8520
+ return LLTV.from(parseUnits(lltv.toString(), 16));
8521
+ } catch (e) {
8522
+ ctx.addIssue({
8523
+ code: "custom",
8524
+ message: e.message,
8525
+ input: lltv
8526
+ });
8527
+ return z.NEVER;
8528
+ }
8529
+ }).optional().meta({
8495
8530
  description: "Minimum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 80.5 = 80.5%)",
8496
8531
  example: "80.5"
8497
8532
  }),
8498
- max_lltv: z.string().regex(/^\d+(\.\d+)?$/, {
8499
- message: "Max LLTV must be a valid number"
8500
- }).transform((val) => parseFloat(val)).pipe(z.number().min(0).max(100)).optional().meta({
8533
+ max_lltv: z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).transform((lltv, ctx) => {
8534
+ try {
8535
+ return LLTV.from(parseUnits(lltv.toString(), 16));
8536
+ } catch (e) {
8537
+ ctx.addIssue({
8538
+ code: "custom",
8539
+ message: e.message,
8540
+ input: lltv
8541
+ });
8542
+ return z.NEVER;
8543
+ }
8544
+ }).optional().meta({
8501
8545
  description: "Maximum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 95.5 = 95.5%)",
8502
8546
  example: "95.5"
8503
8547
  }),
@@ -8590,68 +8634,89 @@ var MatchOffersQueryParams = z.object({
8590
8634
  }),
8591
8635
  // Collateral filtering
8592
8636
  collaterals: z.string().regex(
8593
- /^(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+)*$/,
8637
+ /^(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]+)?)*$/,
8594
8638
  {
8595
8639
  message: "Collaterals must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. All fields are required for each collateral."
8596
8640
  }
8597
- ).transform((val) => {
8641
+ ).transform((val, ctx) => {
8598
8642
  return val.split("#").map((collateral) => {
8599
8643
  const parts = collateral.split(":");
8600
8644
  if (parts.length !== 3) {
8601
- throw new z.ZodError([
8602
- {
8603
- code: "custom",
8604
- message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
8605
- path: ["collaterals"],
8606
- input: val
8607
- }
8608
- ]);
8645
+ ctx.addIssue({
8646
+ code: "custom",
8647
+ message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
8648
+ path: ["collaterals"],
8649
+ input: val
8650
+ });
8651
+ return z.NEVER;
8609
8652
  }
8610
8653
  const [asset, oracle, lltvStr] = parts;
8611
8654
  if (!asset || !oracle || !lltvStr) {
8612
- throw new z.ZodError([
8613
- {
8614
- code: "custom",
8615
- message: "Asset, oracle, and lltv are all required for each collateral",
8616
- path: ["collaterals"],
8617
- input: val
8618
- }
8619
- ]);
8655
+ ctx.addIssue({
8656
+ code: "custom",
8657
+ message: "Asset, oracle, and lltv are all required for each collateral",
8658
+ path: ["collaterals"],
8659
+ input: val
8660
+ });
8620
8661
  }
8621
- const lltv = BigInt(lltvStr);
8622
- if (lltv <= 0n) {
8623
- throw new z.ZodError([
8624
- {
8662
+ let lltvValue;
8663
+ if (lltvStr !== void 0) {
8664
+ try {
8665
+ lltvValue = LLTV.from(parseUnits(lltvStr, 16));
8666
+ } catch (e) {
8667
+ ctx.issues.push({
8625
8668
  code: "custom",
8626
- message: "LLTV must be a positive number",
8627
- path: ["collaterals"],
8628
- input: val
8629
- }
8630
- ]);
8669
+ message: e instanceof LLTV.InvalidLLTVError || e instanceof LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
8670
+ input: lltvStr,
8671
+ path: ["lltv"]
8672
+ });
8673
+ return z.NEVER;
8674
+ }
8631
8675
  }
8632
8676
  return {
8633
8677
  asset: asset.toLowerCase(),
8634
8678
  oracle: oracle.toLowerCase(),
8635
- lltv
8679
+ lltv: lltvValue
8636
8680
  };
8637
8681
  });
8638
8682
  }).optional().meta({
8639
8683
  description: "Collateral requirements in format: asset:oracle:lltv#asset2:oracle2:lltv2. Use # to separate multiple collaterals.",
8640
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:800000000000000000#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:900000000000000000"
8684
+ example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:94.5"
8641
8685
  }),
8642
8686
  // Maturity filtering
8643
- maturity: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8644
- description: "Exact maturity timestamp (Unix timestamp in seconds)",
8645
- example: "1700000000"
8646
- }),
8647
- min_maturity: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8648
- description: "Minimum maturity timestamp (Unix timestamp in seconds, inclusive)",
8649
- example: "1700000000"
8650
- }),
8651
- max_maturity: z.bigint({ coerce: true }).min(0n).optional().transform((val) => val === void 0 ? void 0 : Number(val)).meta({
8652
- description: "Maximum maturity timestamp (Unix timestamp in seconds, inclusive)",
8653
- example: "1800000000"
8654
- }),
8687
+ maturity: z.coerce.number().int().positive().transform((maturity, ctx) => {
8688
+ try {
8689
+ return Maturity.from(maturity);
8690
+ } catch (e) {
8691
+ ctx.addIssue({
8692
+ code: "custom",
8693
+ message: e.message
8694
+ });
8695
+ return z.NEVER;
8696
+ }
8697
+ }).optional(),
8698
+ min_maturity: z.coerce.number().int().positive().transform((maturity, ctx) => {
8699
+ try {
8700
+ return Maturity.from(maturity);
8701
+ } catch (e) {
8702
+ ctx.addIssue({
8703
+ code: "custom",
8704
+ message: e.message
8705
+ });
8706
+ return z.NEVER;
8707
+ }
8708
+ }).optional(),
8709
+ max_maturity: z.coerce.number().int().positive().transform((maturity, ctx) => {
8710
+ try {
8711
+ return Maturity.from(maturity);
8712
+ } catch (e) {
8713
+ ctx.addIssue({
8714
+ code: "custom",
8715
+ message: e.message
8716
+ });
8717
+ return z.NEVER;
8718
+ }
8719
+ }).optional(),
8655
8720
  // Asset and creator filtering
8656
8721
  loan_token: z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
8657
8722
  message: "Loan asset must be a valid Ethereum address"
@@ -8716,8 +8781,10 @@ var schemas = {
8716
8781
  function parse(action, query) {
8717
8782
  return schemas[action].parse(query);
8718
8783
  }
8719
- function safeParse(action, query) {
8720
- return schemas[action].safeParse(query);
8784
+ function safeParse(action, query, error) {
8785
+ return schemas[action].safeParse(query, {
8786
+ error
8787
+ });
8721
8788
  }
8722
8789
 
8723
8790
  // src/core/apiSchema/openapi.ts
@@ -8769,7 +8836,7 @@ var paths = {
8769
8836
  }
8770
8837
  }
8771
8838
  },
8772
- "/v1/match-offers": {
8839
+ "/v1/offers/match": {
8773
8840
  get: {
8774
8841
  summary: "Match offers",
8775
8842
  description: "Find offers that match specific criteria",
@@ -8932,16 +8999,16 @@ async function get(config, parameters) {
8932
8999
  } else if (lltv !== void 0) {
8933
9000
  result += `:`;
8934
9001
  }
8935
- if (lltv !== void 0) result += `:${lltv}`;
9002
+ if (lltv !== void 0) result += `:${formatUnits(lltv, 16)}`;
8936
9003
  return result;
8937
9004
  }).join("#");
8938
9005
  url.searchParams.set("collateral_tuple", tupleStr);
8939
9006
  }
8940
9007
  if (parameters.minLltv !== void 0) {
8941
- url.searchParams.set("min_lltv", parameters.minLltv.toString());
9008
+ url.searchParams.set("min_lltv", formatUnits(parameters.minLltv, 16));
8942
9009
  }
8943
9010
  if (parameters.maxLltv !== void 0) {
8944
- url.searchParams.set("max_lltv", parameters.maxLltv.toString());
9011
+ url.searchParams.set("max_lltv", formatUnits(parameters.maxLltv, 16));
8945
9012
  }
8946
9013
  if (parameters.sortBy) {
8947
9014
  url.searchParams.set("sort_by", parameters.sortBy);
@@ -8963,14 +9030,14 @@ async function get(config, parameters) {
8963
9030
  };
8964
9031
  }
8965
9032
  async function match(config, parameters) {
8966
- const url = new URL(`${config.url.toString()}v1/match-offers`);
9033
+ const url = new URL(`${config.url.toString()}v1/offers/match`);
8967
9034
  url.searchParams.set("side", parameters.side);
8968
9035
  url.searchParams.set("chain_id", parameters.chainId.toString());
8969
9036
  if (parameters.rate !== void 0) {
8970
9037
  url.searchParams.set("rate", parameters.rate.toString());
8971
9038
  }
8972
9039
  if (parameters.collaterals?.length) {
8973
- const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${lltv}`).join("#");
9040
+ const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${formatUnits(lltv, 16)}`).join("#");
8974
9041
  url.searchParams.set("collaterals", collateralsStr);
8975
9042
  }
8976
9043
  if (parameters.maturity !== void 0) {
@@ -9011,7 +9078,7 @@ async function getApi(config, url) {
9011
9078
  case pathname.includes("/v1/offers"):
9012
9079
  action = "get_offers";
9013
9080
  break;
9014
- case pathname.includes("/v1/match-offers"):
9081
+ case pathname.includes("/v1/offers/match"):
9015
9082
  action = "match_offers";
9016
9083
  break;
9017
9084
  default:
@@ -9224,6 +9291,7 @@ function memory(parameters) {
9224
9291
  }
9225
9292
  creators && (creators = creators.map((c) => c.toLowerCase()));
9226
9293
  loanTokens && (loanTokens = loanTokens.map((lt) => lt.toLowerCase()));
9294
+ callbackAddresses && (callbackAddresses = callbackAddresses.map((ca) => ca.toLowerCase()));
9227
9295
  collateralAssets && (collateralAssets = collateralAssets.map((ca) => ca.toLowerCase()));
9228
9296
  collateralOracles && (collateralOracles = collateralOracles.map((co) => co.toLowerCase()));
9229
9297
  collateralTuple && (collateralTuple = collateralTuple.map((ct) => ({
@@ -9260,12 +9328,8 @@ function memory(parameters) {
9260
9328
  )
9261
9329
  )
9262
9330
  ));
9263
- minLltv && (offers = offers.filter(
9264
- (o) => o.collaterals.every((c) => c.lltv >= LLTV.from(parseUnits(minLltv.toString(), 16)))
9265
- ));
9266
- maxLltv && (offers = offers.filter(
9267
- (o) => o.collaterals.every((c) => c.lltv <= LLTV.from(parseUnits(maxLltv.toString(), 16)))
9268
- ));
9331
+ minLltv && (offers = offers.filter((o) => o.collaterals.every((c) => c.lltv >= minLltv)));
9332
+ maxLltv && (offers = offers.filter((o) => o.collaterals.every((c) => c.lltv <= maxLltv)));
9269
9333
  offers = offers.sort((a, b) => sort(sortBy, sortOrder, a, b));
9270
9334
  let nextCursor = null;
9271
9335
  if (offers.length > limit) {
@@ -9312,8 +9376,8 @@ function memory(parameters) {
9312
9376
  limit = 20
9313
9377
  } = params;
9314
9378
  const now = Time.now();
9315
- const buy = side !== "buy";
9316
- const sortOrder = buy ? "asc" : "desc";
9379
+ const isBuying = side === "buy";
9380
+ const sortOrder = isBuying ? "desc" : "asc";
9317
9381
  let offers = Array.from(map.values()).map((o) => ({
9318
9382
  ...o,
9319
9383
  consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
@@ -9326,22 +9390,27 @@ function memory(parameters) {
9326
9390
  offers = offers.filter(
9327
9391
  (o) => sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)
9328
9392
  );
9329
- offers = offers.filter((o) => o.hash !== cursor.hash);
9330
9393
  }
9331
- offers = offers.filter((o) => o.buy === buy);
9394
+ offers = offers.filter((o) => o.buy === !isBuying);
9332
9395
  offers = offers.filter((o) => o.chainId === BigInt(chainId));
9333
9396
  offers = offers.filter((o) => o.expiry >= now);
9334
- rate && (offers = offers.filter((o) => buy ? o.rate >= rate : o.rate <= rate));
9397
+ rate && (offers = offers.filter((o) => isBuying ? o.rate >= rate : o.rate <= rate));
9335
9398
  collaterals.length > 0 && (offers = offers.filter(
9336
- (o) => buy ? collaterals.every((c) => {
9337
- return o.collaterals.some(
9338
- (oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9339
- );
9340
- }) : o.collaterals.every((oc) => {
9341
- return collaterals.some(
9342
- (c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9343
- );
9344
- })
9399
+ (o) => isBuying ? (
9400
+ // when wanting to buy, sell offer collaterals ⊆ user buy collaterals
9401
+ o.collaterals.every((oc) => {
9402
+ return collaterals.some(
9403
+ (c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9404
+ );
9405
+ })
9406
+ ) : (
9407
+ // when wanting to sell, user sell collaterals ⊆ buy offer collaterals
9408
+ collaterals.every((c) => {
9409
+ return o.collaterals.some(
9410
+ (oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
9411
+ );
9412
+ })
9413
+ )
9345
9414
  ));
9346
9415
  maturity && (offers = offers.filter((o) => o.maturity === maturity));
9347
9416
  minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
@@ -9379,6 +9448,7 @@ function memory(parameters) {
9379
9448
  }
9380
9449
  offers = Array.from(byGroup.values());
9381
9450
  offers = offers.sort((a, b) => sort("rate", sortOrder, a, b));
9451
+ cursor && (offers = offers.filter((o) => o.hash !== cursor.hash));
9382
9452
  let nextCursor = null;
9383
9453
  if (offers.length > limit) {
9384
9454
  const last = offers[limit - 1];
@@ -9411,9 +9481,12 @@ function memory(parameters) {
9411
9481
  return deleted;
9412
9482
  },
9413
9483
  updateStatus: async (parameters2) => {
9414
- if (!map.has(parameters2.offerHash.toLowerCase())) return;
9415
- map.set(parameters2.offerHash.toLowerCase(), {
9416
- ...map.get(parameters2.offerHash.toLowerCase()),
9484
+ const key = parameters2.offerHash.toLowerCase();
9485
+ const existing = map.get(key);
9486
+ if (!existing) return;
9487
+ if (existing.status === parameters2.status) return;
9488
+ map.set(key, {
9489
+ ...existing,
9417
9490
  status: parameters2.status,
9418
9491
  metadata: parameters2.metadata
9419
9492
  });
@@ -9479,7 +9552,7 @@ async function serve(parameters) {
9479
9552
  return Mempool.error(err, c);
9480
9553
  }
9481
9554
  });
9482
- app.get("/v1/match-offers", async (c) => {
9555
+ app.get("/v1/offers/match", async (c) => {
9483
9556
  try {
9484
9557
  const params = parse("match_offers", c.req.query());
9485
9558
  const offers = await store.findMatchingOffers({