@morpho-dev/router 0.6.0 → 0.7.0

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.
@@ -5256,7 +5256,8 @@ var OfferListResponse = class extends SuccessResponse {};
5256
5256
  __decorate([ApiProperty({
5257
5257
  type: "string",
5258
5258
  nullable: true,
5259
- example: offerCursorExample
5259
+ example: offerCursorExample,
5260
+ description: "Pagination cursor. Offer hash (0x...) for maker queries; base64url-encoded cursor for obligation queries."
5260
5261
  })], OfferListResponse.prototype, "cursor", void 0);
5261
5262
  __decorate([ApiProperty({
5262
5263
  type: () => [OfferListItemResponse],
@@ -5809,11 +5810,17 @@ const configRulesLoanTokenExample = {
5809
5810
  chain_id: 1,
5810
5811
  address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
5811
5812
  };
5813
+ const configRulesOracleExample = {
5814
+ type: "oracle",
5815
+ chain_id: 1,
5816
+ address: "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83"
5817
+ };
5812
5818
  const configRulesChecksumExample = "f1d2d2f924e986ac86fdf7b36c94bcdf";
5813
5819
  const configRulesPayloadExample = [
5814
5820
  configRulesMaturityExample,
5815
5821
  configRulesCallbackExample,
5816
- configRulesLoanTokenExample
5822
+ configRulesLoanTokenExample,
5823
+ configRulesOracleExample
5817
5824
  ];
5818
5825
  const configContractNames = [
5819
5826
  "mempool",
@@ -5964,7 +5971,7 @@ __decorate([
5964
5971
  name: "types",
5965
5972
  type: ["string"],
5966
5973
  required: false,
5967
- example: "maturity,loan_token",
5974
+ example: "maturity,loan_token,oracle",
5968
5975
  description: "Filter by rule types (comma-separated).",
5969
5976
  style: "form",
5970
5977
  explode: false
@@ -6195,6 +6202,9 @@ function isValidBase64urlJson(val) {
6195
6202
  return false;
6196
6203
  }
6197
6204
  }
6205
+ function isValidOfferHashCursor(val) {
6206
+ return /^0x[a-f0-9]{64}$/i.test(val);
6207
+ }
6198
6208
  const csvArray = (schema) => z$1.preprocess((value) => {
6199
6209
  if (value === void 0) return void 0;
6200
6210
  if (Array.isArray(value)) {
@@ -6220,10 +6230,11 @@ const PaginationQueryParams = z$1.object({
6220
6230
  const ConfigRuleTypes = z$1.enum([
6221
6231
  "maturity",
6222
6232
  "callback",
6223
- "loan_token"
6233
+ "loan_token",
6234
+ "oracle"
6224
6235
  ]);
6225
6236
  const GetConfigRulesQueryParams = z$1.object({
6226
- cursor: z$1.string().regex(/^(maturity|callback|loan_token):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
6237
+ cursor: z$1.string().regex(/^(maturity|callback|loan_token|oracle):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
6227
6238
  description: "Pagination cursor in type:chain_id:<value> format",
6228
6239
  example: "maturity:1:1730415600:end_of_next_month"
6229
6240
  }),
@@ -6233,7 +6244,7 @@ const GetConfigRulesQueryParams = z$1.object({
6233
6244
  }),
6234
6245
  types: csvArray(ConfigRuleTypes).meta({
6235
6246
  description: "Filter by rule types (comma-separated).",
6236
- example: "maturity,loan_token"
6247
+ example: "maturity,loan_token,oracle"
6237
6248
  }),
6238
6249
  chains: csvArray(z$1.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
6239
6250
  description: "Filter by chain IDs (comma-separated).",
@@ -6254,8 +6265,11 @@ const GetConfigContractsQueryParams = z$1.object({
6254
6265
  example: "1,8453"
6255
6266
  })
6256
6267
  });
6257
- const GetOffersQueryParams = z$1.object({
6258
- ...PaginationQueryParams.shape,
6268
+ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend({
6269
+ cursor: z$1.string().optional().meta({
6270
+ description: "Pagination cursor. Use offer hash (0x...) for maker queries, base64url for obligation queries.",
6271
+ example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
6272
+ }),
6259
6273
  side: z$1.enum(["buy", "sell"]).optional().meta({
6260
6274
  description: "Side of the offer. Required when using obligation_id.",
6261
6275
  example: "buy"
@@ -6279,11 +6293,29 @@ const GetOffersQueryParams = z$1.object({
6279
6293
  });
6280
6294
  return;
6281
6295
  }
6282
- if (hasMaker) return;
6296
+ if (hasMaker) {
6297
+ if (val.cursor !== void 0 && !isValidOfferHashCursor(val.cursor)) ctx.addIssue({
6298
+ code: "custom",
6299
+ path: ["cursor"],
6300
+ message: "Cursor must be a 32-byte hex offer hash when filtering by maker"
6301
+ });
6302
+ return;
6303
+ }
6283
6304
  if (!hasObligation || !hasSide) ctx.addIssue({
6284
6305
  code: "custom",
6285
6306
  message: "Must provide either maker or both obligation_id and side"
6286
6307
  });
6308
+ if (val.cursor !== void 0 && !isValidBase64urlJson(val.cursor)) ctx.addIssue({
6309
+ code: "custom",
6310
+ path: ["cursor"],
6311
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
6312
+ });
6313
+ }).transform((val) => {
6314
+ if (val.maker && val.cursor) return {
6315
+ ...val,
6316
+ cursor: val.cursor.toLowerCase()
6317
+ };
6318
+ return val;
6287
6319
  });
6288
6320
  const GetObligationsQueryParams = z$1.object({
6289
6321
  ...PaginationQueryParams.shape,
@@ -6559,13 +6591,18 @@ const assets = {
6559
6591
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
6560
6592
  "0x6B175474E89094C44Da98b954EedeAC495271d0F",
6561
6593
  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
6562
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
6594
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
6595
+ "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
6596
+ "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
6563
6597
  ],
6564
6598
  [ChainId.BASE.toString()]: [
6565
6599
  "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
6566
6600
  "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
6567
6601
  "0x4200000000000000000000000000000000000006",
6568
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
6602
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
6603
+ "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
6604
+ "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
6605
+ "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
6569
6606
  ],
6570
6607
  [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
6571
6608
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
@@ -6581,6 +6618,43 @@ const assets = {
6581
6618
  "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
6582
6619
  ]
6583
6620
  };
6621
+ const oracles$1 = {
6622
+ [ChainId.ETHEREUM.toString()]: [
6623
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
6624
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
6625
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
6626
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
6627
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
6628
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
6629
+ ],
6630
+ [ChainId.BASE.toString()]: [
6631
+ "0xD09048c8B568Dbf5f189302beA26c9edABFC4858",
6632
+ "0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4",
6633
+ "0x05D2618404668D725B66c0f32B39e4EC15B393dC",
6634
+ "0xE1bb8E5b4930eC9FeC7f7943FCF6227649F14B37",
6635
+ "0x663BECd10daE6C4A3Dcd89F1d76c1174199639B9",
6636
+ "0x10b95702a0ce895972C91e432C4f7E19811D320E",
6637
+ "0x8C87DbD7A0c647A4291592Bc2994dbF95880fE2F",
6638
+ "0x4A11590e5326138B514E08A9B52202D42077Ca65",
6639
+ "0xa54122f0E0766258377Ffe732e454A3248f454F4"
6640
+ ],
6641
+ [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
6642
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
6643
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
6644
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
6645
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
6646
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
6647
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
6648
+ ],
6649
+ [ChainId.ANVIL.toString()]: [
6650
+ "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
6651
+ "0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
6652
+ "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
6653
+ "0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
6654
+ "0xbD60A6770b27E084E8617335ddE769241B0e71D8",
6655
+ "0xAe12416c1F21B0698c27fe042D9309C83baC6597"
6656
+ ]
6657
+ };
6584
6658
  const configs = {
6585
6659
  ethereum: {
6586
6660
  callbacks: [
@@ -6647,7 +6721,7 @@ const configs = {
6647
6721
  //#endregion
6648
6722
  //#region src/gatekeeper/ConfigRules.ts
6649
6723
  /**
6650
- * Build the configured rules (maturities + callback addresses + loan tokens) for the provided chains.
6724
+ * Build the configured rules (maturities + callback addresses + loan tokens + oracles) for the provided chains.
6651
6725
  * @param chains - Chains to include in the configured rules.
6652
6726
  * @returns Sorted list of config rules.
6653
6727
  */
@@ -6679,6 +6753,12 @@ function buildConfigRules(chains) {
6679
6753
  chain_id: chain.id,
6680
6754
  address: normalizeAddress(address)
6681
6755
  });
6756
+ const oracles = oracles$1[chain.id.toString()] ?? [];
6757
+ for (const address of oracles) rules.push({
6758
+ type: "oracle",
6759
+ chain_id: chain.id,
6760
+ address: normalizeAddress(address)
6761
+ });
6682
6762
  }
6683
6763
  rules.sort(compareConfigRules);
6684
6764
  return rules;
@@ -6700,6 +6780,10 @@ function buildConfigRulesChecksum(rules) {
6700
6780
  hash.update(`callback:${rule.chain_id}:${rule.callback_type}:${rule.address}\n`);
6701
6781
  continue;
6702
6782
  }
6783
+ if (rule.type === "oracle") {
6784
+ hash.update(`oracle:${rule.chain_id}:${rule.address}\n`);
6785
+ continue;
6786
+ }
6703
6787
  hash.update(`loan_token:${rule.chain_id}:${rule.address}\n`);
6704
6788
  }
6705
6789
  return hash.digest("hex");
@@ -6716,6 +6800,7 @@ function compareConfigRules(left, right) {
6716
6800
  return left.address.localeCompare(right.address);
6717
6801
  }
6718
6802
  if (left.type === "loan_token" && right.type === "loan_token") return left.address.localeCompare(right.address);
6803
+ if (left.type === "oracle" && right.type === "oracle") return left.address.localeCompare(right.address);
6719
6804
  return 0;
6720
6805
  }
6721
6806
 
@@ -6760,6 +6845,7 @@ async function getConfigRules(query, chains) {
6760
6845
  function formatCursor(rule) {
6761
6846
  if (rule.type === "maturity") return `maturity:${rule.chain_id}:${rule.timestamp}:${rule.name}`;
6762
6847
  if (rule.type === "callback") return `callback:${rule.chain_id}:${rule.callback_type}:${rule.address.toLowerCase()}`;
6848
+ if (rule.type === "oracle") return `oracle:${rule.chain_id}:${rule.address.toLowerCase()}`;
6763
6849
  return `loan_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
6764
6850
  }
6765
6851
  function parseCursor(cursor) {
@@ -6792,13 +6878,16 @@ function parseCursor(cursor) {
6792
6878
  address: parseAddress(addressValue, "Cursor address")
6793
6879
  };
6794
6880
  }
6795
- const addressValue = rest.join(":");
6796
- if (!addressValue) throw new BadRequestError("Cursor must be in the format loan_token:chain_id:address");
6797
- return {
6798
- type,
6799
- chain_id,
6800
- address: parseAddress(addressValue, "Cursor address")
6801
- };
6881
+ if (type === "loan_token" || type === "oracle") {
6882
+ const addressValue = rest.join(":");
6883
+ if (!addressValue) throw new BadRequestError(`Cursor must be in the format ${type}:chain_id:address`);
6884
+ return {
6885
+ type,
6886
+ chain_id,
6887
+ address: parseAddress(addressValue, "Cursor address")
6888
+ };
6889
+ }
6890
+ throw new BadRequestError("Cursor has an invalid rule type");
6802
6891
  }
6803
6892
  function findStartIndex(rules, cursor) {
6804
6893
  let low = 0;
@@ -6816,7 +6905,7 @@ function parseAddress(address, label) {
6816
6905
  return address.toLowerCase();
6817
6906
  }
6818
6907
  function isConfigRuleType(value) {
6819
- return value === "maturity" || value === "callback" || value === "loan_token";
6908
+ return value === "maturity" || value === "callback" || value === "loan_token" || value === "oracle";
6820
6909
  }
6821
6910
  function isMaturityType(value) {
6822
6911
  return Object.values(MaturityType).includes(value);
@@ -9280,9 +9369,9 @@ function create$5(db) {
9280
9369
  blockNumber: r.blockNumber
9281
9370
  }));
9282
9371
  },
9283
- upsert: async (oracles$1) => {
9284
- if (oracles$1.length === 0) return;
9285
- const rows = oracles$1.map((o) => ({
9372
+ upsert: async (oracles$2) => {
9373
+ if (oracles$2.length === 0) return;
9374
+ const rows = oracles$2.map((o) => ({
9286
9375
  chainId: o.chainId,
9287
9376
  address: o.address.toLowerCase(),
9288
9377
  price: o.price !== null ? o.price.toString() : null,
@@ -10478,6 +10567,7 @@ var Rules_exports = /* @__PURE__ */ __exportAll({
10478
10567
  callback: () => callback,
10479
10568
  chains: () => chains,
10480
10569
  maturity: () => maturity,
10570
+ oracle: () => oracle,
10481
10571
  sameMaker: () => sameMaker,
10482
10572
  token: () => token,
10483
10573
  validity: () => validity
@@ -10616,6 +10706,16 @@ const token = ({ assetsByChainId }) => single("token", "Validates that offer loa
10616
10706
  if (offer.collaterals.some((collateral) => !allowedAssets.includes(collateral.asset.toLowerCase()))) return { message: "Collateral is not allowed" };
10617
10707
  });
10618
10708
  /**
10709
+ * A validation rule that checks if the offer's oracle addresses are allowed for its chain.
10710
+ * @param oraclesByChainId - Allowed oracles indexed by chain id.
10711
+ * @returns The issue that was found. If the offer is valid, this will be undefined.
10712
+ */
10713
+ const oracle = ({ oraclesByChainId }) => single("oracle", "Validates that offer collateral oracles are in the allowed oracle list for the offer chain", (offer) => {
10714
+ const allowedOracles = oraclesByChainId[offer.chainId]?.map((oracle) => oracle.toLowerCase());
10715
+ if (!allowedOracles || allowedOracles.length === 0) return { message: `No allowed oracles for chain ${offer.chainId}` };
10716
+ if (offer.collaterals.some((collateral) => !allowedOracles.includes(collateral.oracle.toLowerCase()))) return { message: "Oracle is not allowed" };
10717
+ });
10718
+ /**
10619
10719
  * A batch validation rule that ensures all offers in a tree have the same maker address.
10620
10720
  * Returns an issue only for the first non-conforming offer.
10621
10721
  * This rule is signing-agnostic; signer verification is handled at the collector level.
@@ -10646,7 +10746,11 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
10646
10746
  //#region src/gatekeeper/morphoRules.ts
10647
10747
  const morphoRules = (chains$3) => {
10648
10748
  const assetsByChainId = {};
10649
- for (const chain of chains$3) assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
10749
+ const oraclesByChainId = {};
10750
+ for (const chain of chains$3) {
10751
+ assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
10752
+ oraclesByChainId[chain.id] = oracles$1[chain.id.toString()] ?? [];
10753
+ }
10650
10754
  return [
10651
10755
  sameMaker(),
10652
10756
  amountMutualExclusivity(),
@@ -10660,7 +10764,8 @@ const morphoRules = (chains$3) => {
10660
10764
  ],
10661
10765
  allowedAddresses: chains$3.flatMap((c) => getCallbackAddresses(c.name))
10662
10766
  }),
10663
- token({ assetsByChainId })
10767
+ token({ assetsByChainId }),
10768
+ oracle({ oraclesByChainId })
10664
10769
  ];
10665
10770
  };
10666
10771