@morpho-dev/router 0.8.0 → 0.9.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.
@@ -608,7 +608,7 @@ var utils_exports = /* @__PURE__ */ __exportAll({
608
608
 
609
609
  //#endregion
610
610
  //#region src/indexer/collectors/Admin.ts
611
- function create$20(parameters) {
611
+ function create$21(parameters) {
612
612
  const collector = "admin";
613
613
  const { client, db, options: { maxBatchSize = 25, maxBlockNumber } = {} } = parameters;
614
614
  const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
@@ -848,8 +848,8 @@ const names = [
848
848
  "positions",
849
849
  "prices"
850
850
  ];
851
- function create$19({ name, collect, client, db, options }) {
852
- const admin = create$20({
851
+ function create$20({ name, collect, client, db, options }) {
852
+ const admin = create$21({
853
853
  client,
854
854
  db,
855
855
  options
@@ -1015,18 +1015,18 @@ const MorphoV2 = (0, viem.parseAbi)([
1015
1015
  "function setFeeSetter(address newFeeSetter)",
1016
1016
  "function setObligationTradingFee(bytes32 id, uint256 index, uint256 newTradingFee)",
1017
1017
  "function setOwner(address newOwner)",
1018
- "function setTradingFeeRecipient(address recipient)",
1018
+ "function setTradingFeeRecipient(address feeRecipient)",
1019
1019
  "function sharesOf(bytes32 id, address user) view returns (uint256)",
1020
1020
  "function shuffleSession()",
1021
1021
  "function supplyCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf)",
1022
- "function take(uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, address taker, ((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, bool buy, address maker, uint256 assets, uint256 obligationUnits, uint256 obligationShares, uint256 start, uint256 expiry, uint256 tick, bytes32 group, bytes32 session, address callback, bytes callbackData) offer, (uint8 v, bytes32 r, bytes32 s) sig, bytes32 root, bytes32[] proof, address takerCallback, bytes takerCallbackData) returns (uint256, uint256, uint256, uint256)",
1022
+ "function take(uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, address taker, address takerCallback, bytes takerCallbackData, address receiverIfTakerIsSeller, ((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, bool buy, address maker, uint256 assets, uint256 obligationUnits, uint256 obligationShares, uint256 start, uint256 expiry, uint256 tick, bytes32 group, bytes32 session, address callback, bytes callbackData, address receiverIfMakerIsSeller) offer, (uint8 v, bytes32 r, bytes32 s) sig, bytes32 root, bytes32[] proof) returns (uint256, uint256, uint256, uint256)",
1023
1023
  "function totalShares(bytes32 id) view returns (uint256)",
1024
1024
  "function totalUnits(bytes32 id) view returns (uint256)",
1025
1025
  "function touchObligation((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation) returns (bytes32)",
1026
1026
  "function tradingFee(bytes32 id, uint256 timeToMaturity) view returns (uint256)",
1027
1027
  "function tradingFeeRecipient() view returns (address)",
1028
- "function withdraw((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, uint256 obligationUnits, uint256 shares, address onBehalf) returns (uint256, uint256)",
1029
- "function withdrawCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf)",
1028
+ "function withdraw((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, uint256 obligationUnits, uint256 shares, address onBehalf, address receiver) returns (uint256, uint256)",
1029
+ "function withdrawCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf, address receiver)",
1030
1030
  "function withdrawable(bytes32 id) view returns (uint256)",
1031
1031
  "event Constructor(address indexed owner)",
1032
1032
  "event Consume(address indexed user, bytes32 indexed group, uint256 amount)",
@@ -1038,12 +1038,12 @@ const MorphoV2 = (0, viem.parseAbi)([
1038
1038
  "event SetFeeSetter(address indexed feeSetter)",
1039
1039
  "event SetObligationTradingFee(bytes32 indexed id, uint256 indexed index, uint256 newTradingFee)",
1040
1040
  "event SetOwner(address indexed owner)",
1041
- "event SetTradingFeeRecipient(address indexed recipient)",
1041
+ "event SetTradingFeeRecipient(address indexed feeRecipient)",
1042
1042
  "event ShuffleSession(address indexed user, bytes32 session)",
1043
1043
  "event SupplyCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf)",
1044
- "event Take(address caller, bytes32 indexed id, address indexed maker, address indexed taker, bool offerIsBuy, uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, bool buyerIsLender, bool sellerIsBorrower, bytes32 group, uint256 consumed)",
1045
- "event Withdraw(address indexed caller, bytes32 indexed id, uint256 obligationUnits, uint256 shares, address indexed onBehalf)",
1046
- "event WithdrawCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf)"
1044
+ "event Take(address caller, bytes32 indexed id, address indexed maker, address indexed taker, bool offerIsBuy, uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, bool buyerIsLender, bool sellerIsBorrower, address sellerReceiver, bytes32 group, uint256 consumed)",
1045
+ "event Withdraw(address caller, bytes32 indexed id, uint256 obligationUnits, uint256 shares, address indexed onBehalf, address indexed receiver)",
1046
+ "event WithdrawCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf, address receiver)"
1047
1047
  ]);
1048
1048
 
1049
1049
  //#endregion
@@ -1315,8 +1315,8 @@ const chains$2 = {
1315
1315
  name: "ethereum-virtual-testnet",
1316
1316
  custom: {
1317
1317
  morpho: {
1318
- address: "0x634b095371e4e45feed94c1a45c37798e173ea50",
1319
- blockCreated: 23226700
1318
+ address: "0xc9f3c65996fc46b9500608b2c9a9152c01c540f7",
1319
+ blockCreated: 23226871
1320
1320
  },
1321
1321
  morphoBlue: {
1322
1322
  address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -1462,13 +1462,13 @@ var MissingBlockNumberError = class extends BaseError {
1462
1462
 
1463
1463
  //#endregion
1464
1464
  //#region src/core/ChainRegistry.ts
1465
- var ChainRegistry_exports = /* @__PURE__ */ __exportAll({ create: () => create$18 });
1465
+ var ChainRegistry_exports = /* @__PURE__ */ __exportAll({ create: () => create$19 });
1466
1466
  /**
1467
1467
  * Creates a chain registry from a list of chains.
1468
1468
  * @param chains - Array of chain objects to register.
1469
1469
  * @returns A registry for looking up chains by ID. {@link ChainRegistry}
1470
1470
  */
1471
- function create$18(chains) {
1471
+ function create$19(chains) {
1472
1472
  const byId = /* @__PURE__ */ new Map();
1473
1473
  for (const chain of chains) byId.set(chain.id, chain);
1474
1474
  return {
@@ -2143,7 +2143,8 @@ const OfferSchema = () => {
2143
2143
  callback: zod.object({
2144
2144
  address: zod.string().transform(transformAddress),
2145
2145
  data: zod.string().transform(transformHex)
2146
- })
2146
+ }),
2147
+ receiverIfMakerIsSeller: zod.string().transform(transformAddress)
2147
2148
  }).refine((data) => data.start < data.expiry, {
2148
2149
  message: "start must be before expiry",
2149
2150
  path: ["start"]
@@ -2159,8 +2160,12 @@ const OfferSchema = () => {
2159
2160
  * @returns The created offer.
2160
2161
  */
2161
2162
  function from$14(input) {
2163
+ const normalizedInput = {
2164
+ ...input,
2165
+ receiverIfMakerIsSeller: input.receiverIfMakerIsSeller ?? input.maker
2166
+ };
2162
2167
  try {
2163
- return OfferSchema().parse(input);
2168
+ return OfferSchema().parse(normalizedInput);
2164
2169
  } catch (error) {
2165
2170
  throw new InvalidOfferError(error);
2166
2171
  }
@@ -2212,6 +2217,7 @@ const serialize = (offer) => ({
2212
2217
  address: offer.callback.address,
2213
2218
  data: offer.callback.data
2214
2219
  },
2220
+ receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller,
2215
2221
  hash: hash(offer)
2216
2222
  });
2217
2223
  /**
@@ -2254,8 +2260,9 @@ function random$1(config) {
2254
2260
  address: viem.zeroAddress,
2255
2261
  data: "0x"
2256
2262
  };
2263
+ const maker = config?.maker ?? address();
2257
2264
  return from$14({
2258
- maker: config?.maker ?? address(),
2265
+ maker,
2259
2266
  assets: assetsScaled,
2260
2267
  obligationUnits: config?.obligationUnits ?? 0n,
2261
2268
  obligationShares: config?.obligationShares ?? 0n,
@@ -2272,7 +2279,8 @@ function random$1(config) {
2272
2279
  ...random$3(),
2273
2280
  lltv
2274
2281
  })).sort((a, b) => a.asset.localeCompare(b.asset)),
2275
- callback: config?.callback ?? emptyCallback
2282
+ callback: config?.callback ?? emptyCallback,
2283
+ receiverIfMakerIsSeller: config?.receiverIfMakerIsSeller ?? maker
2276
2284
  });
2277
2285
  }
2278
2286
  const weightedChoice = (pairs) => {
@@ -2358,6 +2366,10 @@ const types = {
2358
2366
  {
2359
2367
  name: "callback",
2360
2368
  type: "Callback"
2369
+ },
2370
+ {
2371
+ name: "receiverIfMakerIsSeller",
2372
+ type: "address"
2361
2373
  }
2362
2374
  ],
2363
2375
  Collateral: [
@@ -2403,7 +2415,8 @@ function hash(offer) {
2403
2415
  callback: {
2404
2416
  address: offer.callback.address.toLowerCase(),
2405
2417
  data: offer.callback.data
2406
- }
2418
+ },
2419
+ receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase()
2407
2420
  },
2408
2421
  primaryType: "Offer",
2409
2422
  types
@@ -2506,6 +2519,10 @@ const OfferAbi = [
2506
2519
  name: "data",
2507
2520
  type: "bytes"
2508
2521
  }]
2522
+ },
2523
+ {
2524
+ name: "receiverIfMakerIsSeller",
2525
+ type: "address"
2509
2526
  }
2510
2527
  ];
2511
2528
  function encode$1(offer) {
@@ -2524,7 +2541,8 @@ function encode$1(offer) {
2524
2541
  offer.loanToken,
2525
2542
  BigInt(offer.start),
2526
2543
  offer.collaterals,
2527
- offer.callback
2544
+ offer.callback,
2545
+ offer.receiverIfMakerIsSeller
2528
2546
  ]);
2529
2547
  }
2530
2548
  function decode$1(data) {
@@ -2558,7 +2576,8 @@ function decode$1(data) {
2558
2576
  callback: {
2559
2577
  address: decoded[14].address,
2560
2578
  data: decoded[14].data
2561
- }
2579
+ },
2580
+ receiverIfMakerIsSeller: decoded[15]
2562
2581
  });
2563
2582
  }
2564
2583
  /**
@@ -2634,6 +2653,12 @@ const takeEvent = {
2634
2653
  indexed: false,
2635
2654
  internalType: "bool"
2636
2655
  },
2656
+ {
2657
+ name: "sellerReceiver",
2658
+ type: "address",
2659
+ indexed: false,
2660
+ internalType: "address"
2661
+ },
2637
2662
  {
2638
2663
  name: "group",
2639
2664
  type: "bytes32",
@@ -2819,76 +2844,6 @@ function from$12(parameters) {
2819
2844
  };
2820
2845
  }
2821
2846
 
2822
- //#endregion
2823
- //#region src/core/Quote.ts
2824
- var Quote_exports = /* @__PURE__ */ __exportAll({
2825
- InvalidQuoteError: () => InvalidQuoteError,
2826
- QuoteSchema: () => QuoteSchema,
2827
- from: () => from$11,
2828
- fromSnakeCase: () => fromSnakeCase,
2829
- random: () => random
2830
- });
2831
- const QuoteSchema = zod.object({
2832
- obligationId: zod.string().transform(transformHex),
2833
- ask: zod.object({ price: zod.bigint({ coerce: true }).min(0n).max(viem.maxUint256) }),
2834
- bid: zod.object({ price: zod.bigint({ coerce: true }).min(0n).max(viem.maxUint256) })
2835
- });
2836
- /**
2837
- * Creates a quote for a given obligation.
2838
- * @constructor
2839
- * @param parameters - {@link from.Parameters}
2840
- * @returns The created quote. {@link Quote}
2841
- * @throws If the quote is invalid. {@link InvalidQuoteError}
2842
- *
2843
- * @example
2844
- * ```ts
2845
- * const quote = Quote.from({ obligationId: "0x123", ask: { price: 100n }, bid: { price: 100n } });
2846
- * ```
2847
- */
2848
- function from$11(parameters) {
2849
- try {
2850
- const parsedQuote = QuoteSchema.parse(parameters);
2851
- return {
2852
- obligationId: parsedQuote.obligationId,
2853
- ask: parsedQuote.ask,
2854
- bid: parsedQuote.bid
2855
- };
2856
- } catch (error) {
2857
- throw new InvalidQuoteError(error);
2858
- }
2859
- }
2860
- /**
2861
- * Creates a quote from a snake case object.
2862
- * @throws If the quote is invalid. {@link InvalidQuoteError}
2863
- * @param snake - {@link fromSnakeCase.Parameters}
2864
- * @returns The created quote. {@link fromSnakeCase.ReturnType}
2865
- */
2866
- function fromSnakeCase(snake) {
2867
- return from$11(fromSnakeCase$3(snake));
2868
- }
2869
- /**
2870
- * Generates a random quote.
2871
- * @returns A randomly generated quote. {@link random.ReturnType}
2872
- *
2873
- * @example
2874
- * ```ts
2875
- * const quote = Quote.random();
2876
- * ```
2877
- */
2878
- function random() {
2879
- return from$11({
2880
- obligationId: id(random$2()),
2881
- ask: { price: BigInt(int(1e6)) },
2882
- bid: { price: BigInt(int(1e6)) }
2883
- });
2884
- }
2885
- var InvalidQuoteError = class extends BaseError {
2886
- name = "Quote.InvalidQuoteError";
2887
- constructor(error) {
2888
- super("Invalid quote.", { cause: error });
2889
- }
2890
- };
2891
-
2892
2847
  //#endregion
2893
2848
  //#region src/core/Tick.ts
2894
2849
  var Tick_exports = /* @__PURE__ */ __exportAll({
@@ -2968,6 +2923,82 @@ var InvalidPriceError = class extends BaseError {
2968
2923
  }
2969
2924
  };
2970
2925
 
2926
+ //#endregion
2927
+ //#region src/core/Quote.ts
2928
+ var Quote_exports = /* @__PURE__ */ __exportAll({
2929
+ InvalidQuoteError: () => InvalidQuoteError,
2930
+ from: () => from$11,
2931
+ fromSnakeCase: () => fromSnakeCase,
2932
+ random: () => random
2933
+ });
2934
+ const SideInputSchema = zod.object({ tick: zod.number().int().min(0).max(TICK_RANGE).nullable() }).strict();
2935
+ const QuoteInputSchema = zod.object({
2936
+ obligationId: zod.string().transform(transformHex),
2937
+ ask: SideInputSchema,
2938
+ bid: SideInputSchema
2939
+ }).strict();
2940
+ /**
2941
+ * Creates a quote for a given obligation.
2942
+ * @constructor
2943
+ * @param parameters - {@link from.Parameters}
2944
+ * @returns The created quote. {@link Quote}
2945
+ * @throws If the quote is invalid. {@link InvalidQuoteError}
2946
+ *
2947
+ * @example
2948
+ * ```ts
2949
+ * const quote = Quote.from({ obligationId: "0x123", ask: { tick: 500 }, bid: { tick: 510 } });
2950
+ * ```
2951
+ */
2952
+ function from$11(parameters) {
2953
+ try {
2954
+ const parsedQuote = QuoteInputSchema.parse(parameters);
2955
+ return {
2956
+ obligationId: parsedQuote.obligationId,
2957
+ ask: sideFromTick(parsedQuote.ask),
2958
+ bid: sideFromTick(parsedQuote.bid)
2959
+ };
2960
+ } catch (error) {
2961
+ throw new InvalidQuoteError(error);
2962
+ }
2963
+ }
2964
+ /**
2965
+ * Creates a quote from a snake case object.
2966
+ * @throws If the quote is invalid. {@link InvalidQuoteError}
2967
+ * @param snake - {@link fromSnakeCase.Parameters}
2968
+ * @returns The created quote. {@link fromSnakeCase.ReturnType}
2969
+ */
2970
+ function fromSnakeCase(snake) {
2971
+ return from$11(fromSnakeCase$3(snake));
2972
+ }
2973
+ /**
2974
+ * Generates a random quote.
2975
+ * @returns A randomly generated quote. {@link random.ReturnType}
2976
+ *
2977
+ * @example
2978
+ * ```ts
2979
+ * const quote = Quote.random();
2980
+ * ```
2981
+ */
2982
+ function random() {
2983
+ return from$11({
2984
+ obligationId: id(random$2()),
2985
+ ask: { tick: int(TICK_RANGE + 1) },
2986
+ bid: { tick: int(TICK_RANGE + 1) }
2987
+ });
2988
+ }
2989
+ var InvalidQuoteError = class extends BaseError {
2990
+ name = "Quote.InvalidQuoteError";
2991
+ constructor(error) {
2992
+ super("Invalid quote.", { cause: error });
2993
+ }
2994
+ };
2995
+ function sideFromTick(side) {
2996
+ return {
2997
+ tick: side.tick,
2998
+ price: side.tick === null ? 0n : tickToPrice(side.tick)
2999
+ };
3000
+ }
3001
+
2971
3002
  //#endregion
2972
3003
  //#region src/core/TradingFee.ts
2973
3004
  var TradingFee_exports = /* @__PURE__ */ __exportAll({
@@ -3644,6 +3675,7 @@ const offers = s.table(EnumTableName.OFFERS, {
3644
3675
  buy: (0, drizzle_orm_pg_core.boolean)("buy").notNull(),
3645
3676
  callbackAddress: (0, drizzle_orm_pg_core.varchar)("callback_address", { length: 42 }).notNull(),
3646
3677
  callbackData: (0, drizzle_orm_pg_core.text)("callback_data").notNull(),
3678
+ receiverIfMakerIsSeller: (0, drizzle_orm_pg_core.varchar)("receiver_if_maker_is_seller", { length: 42 }),
3647
3679
  blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
3648
3680
  updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
3649
3681
  }, (table) => [
@@ -5025,7 +5057,7 @@ async function* collectPrices(parameters) {
5025
5057
  //#region src/indexer/collectors/CollectorBuilder.ts
5026
5058
  function createBuilder(parameters) {
5027
5059
  const { client, db, gatekeeper, options: { maxBlockNumber, blockWindow, interval } = {} } = parameters;
5028
- const createCollector = (name, collect) => create$19({
5060
+ const createCollector = (name, collect) => create$20({
5029
5061
  name,
5030
5062
  collect,
5031
5063
  client,
@@ -5117,7 +5149,7 @@ const from$7 = (parameters) => {
5117
5149
  //#endregion
5118
5150
  //#region src/indexer/Indexer.ts
5119
5151
  var Indexer_exports = /* @__PURE__ */ __exportAll({
5120
- create: () => create$17,
5152
+ create: () => create$18,
5121
5153
  from: () => from$6
5122
5154
  });
5123
5155
  function from$6(config) {
@@ -5133,7 +5165,7 @@ function from$6(config) {
5133
5165
  retryAttempts,
5134
5166
  retryDelayMs
5135
5167
  });
5136
- return create$17({
5168
+ return create$18({
5137
5169
  client,
5138
5170
  collectors: [
5139
5171
  offersCollector,
@@ -5143,7 +5175,7 @@ function from$6(config) {
5143
5175
  ]
5144
5176
  });
5145
5177
  }
5146
- function create$17(params) {
5178
+ function create$18(params) {
5147
5179
  const { collectors, client } = params;
5148
5180
  const indexerId = `${client.chain.id.toString()}.indexer`;
5149
5181
  const tracer = getTracer(`router.${indexerId}`);
@@ -5172,12 +5204,12 @@ function create$17(params) {
5172
5204
 
5173
5205
  //#endregion
5174
5206
  //#region src/api/Health.ts
5175
- var Health_exports = /* @__PURE__ */ __exportAll({ create: () => create$16 });
5207
+ var Health_exports = /* @__PURE__ */ __exportAll({ create: () => create$17 });
5176
5208
  const DEFAULT_MAX_ALLOWED_LAG = 5;
5177
5209
  /**
5178
5210
  * Create a health service that exposes collector and chain block numbers.
5179
5211
  */
5180
- function create$16(parameters) {
5212
+ function create$17(parameters) {
5181
5213
  const { db, maxAllowedLag = DEFAULT_MAX_ALLOWED_LAG, healthClients, chainRegistry } = parameters;
5182
5214
  const loadSnapshot = async () => {
5183
5215
  const [collectorRows, chainRows, remoteBlockByChainId] = await Promise.all([
@@ -5341,6 +5373,7 @@ var ObligationResponse_exports = /* @__PURE__ */ __exportAll({ from: () => from$
5341
5373
  * Creates an `ObligationResponse` from a `Obligation`.
5342
5374
  * @constructor
5343
5375
  * @param obligation - {@link Obligation}
5376
+ * @param quote - {@link Quote}
5344
5377
  * @returns The created `ObligationResponse`. {@link ObligationResponse}
5345
5378
  */
5346
5379
  function from$4(obligation, quote) {
@@ -5354,8 +5387,14 @@ function from$4(obligation, quote) {
5354
5387
  oracle: c.oracle
5355
5388
  })),
5356
5389
  maturity: obligation.maturity,
5357
- ask: { price: quote.ask.price.toString() },
5358
- bid: { price: quote.bid.price.toString() }
5390
+ ask: {
5391
+ tick: quote.ask.tick,
5392
+ price: quote.ask.price.toString()
5393
+ },
5394
+ bid: {
5395
+ tick: quote.bid.tick,
5396
+ price: quote.bid.price.toString()
5397
+ }
5359
5398
  };
5360
5399
  }
5361
5400
 
@@ -5403,7 +5442,8 @@ function from$3(input) {
5403
5442
  group: input.group,
5404
5443
  session: input.session,
5405
5444
  callback: input.callback.address,
5406
- callback_data: input.callback.data
5445
+ callback_data: input.callback.data,
5446
+ receiver_if_maker_is_seller: input.receiverIfMakerIsSeller
5407
5447
  },
5408
5448
  offer_hash: input.hash,
5409
5449
  obligation_id: id({
@@ -5470,7 +5510,7 @@ var InternalServerError = class extends APIError {
5470
5510
  super(STATUS_CODE.INTERNAL_SERVER_ERROR, message, "INTERNAL_SERVER_ERROR");
5471
5511
  }
5472
5512
  };
5473
- var BadRequestError = class extends APIError {
5513
+ var BadRequestError$1 = class extends APIError {
5474
5514
  constructor(message = "Invalid JSON format", details) {
5475
5515
  super(STATUS_CODE.BAD_REQUEST, message, "BAD_REQUEST", details);
5476
5516
  }
@@ -5492,7 +5532,7 @@ function success(args) {
5492
5532
  */
5493
5533
  function failure(err) {
5494
5534
  if (err instanceof APIError) return handleAPIError(err);
5495
- if (err instanceof SyntaxError) return handleAPIError(new BadRequestError(err.message));
5535
+ if (err instanceof SyntaxError) return handleAPIError(new BadRequestError$1(err.message));
5496
5536
  if (err instanceof zod.ZodError) return handleAPIError(handleZodError(err));
5497
5537
  return handleAPIError(new InternalServerError());
5498
5538
  }
@@ -5542,7 +5582,7 @@ function __decorate(decorators, target, key, desc) {
5542
5582
  //#region src/api/Schema/openapi.ts
5543
5583
  const timestampExample = "2024-01-01T12:00:00.000Z";
5544
5584
  const offerCursorExample = "eyJvZmZzZXQiOjEwMH0";
5545
- const obligationCursorExample = "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc";
5585
+ const obligationCursorExample = "eyJzb3J0IjpbImlkIl0sImlkIjoiMHgxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAiLCJhc2siOiIwIiwiYmlkIjoiMCIsIm1hdHVyaXR5IjoxNzYxOTIyODAwfQ";
5546
5586
  const offerExample = {
5547
5587
  offer: {
5548
5588
  obligation: {
@@ -5565,7 +5605,8 @@ const offerExample = {
5565
5605
  group: "0x000000000000000000000000000000000000000000000000000000000008b8f4",
5566
5606
  session: "0x0000000000000000000000000000000000000000000000000000000000000000",
5567
5607
  callback: "0x0000000000000000000000000000000000000000",
5568
- callback_data: "0x"
5608
+ callback_data: "0x",
5609
+ receiver_if_maker_is_seller: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
5569
5610
  },
5570
5611
  offer_hash: "0xac4bd8318ec914f89f8af913f162230575b0ac0696a19256bc12138c5cfe1427",
5571
5612
  obligation_id: "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc",
@@ -5619,7 +5660,8 @@ const validateOfferExample = {
5619
5660
  callback: {
5620
5661
  address: "0x0000000000000000000000000000000000000000",
5621
5662
  data: "0x"
5622
- }
5663
+ },
5664
+ receiver_if_maker_is_seller: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
5623
5665
  };
5624
5666
  const routerStatusExample = {
5625
5667
  status: "live",
@@ -5690,11 +5732,23 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5690
5732
  example: validateOfferExample.callback.data
5691
5733
  })], ValidateCallbackRequest.prototype, "data", void 0);
5692
5734
  var AskResponse = class {};
5735
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5736
+ type: "number",
5737
+ nullable: true,
5738
+ example: 500,
5739
+ description: "Best ask tick. Null when there is no active ask quote."
5740
+ })], AskResponse.prototype, "tick", void 0);
5693
5741
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5694
5742
  type: "string",
5695
5743
  example: "1000000000000000000"
5696
5744
  })], AskResponse.prototype, "price", void 0);
5697
5745
  var BidResponse = class {};
5746
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5747
+ type: "number",
5748
+ nullable: true,
5749
+ example: 500,
5750
+ description: "Best bid tick. Null when there is no active bid quote."
5751
+ })], BidResponse.prototype, "tick", void 0);
5698
5752
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5699
5753
  type: "string",
5700
5754
  example: "1000000000000000000"
@@ -5767,6 +5821,10 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5767
5821
  type: "string",
5768
5822
  example: offerExample.offer.callback_data
5769
5823
  })], OfferDataResponse.prototype, "callback_data", void 0);
5824
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5825
+ type: "string",
5826
+ example: offerExample.offer.receiver_if_maker_is_seller
5827
+ })], OfferDataResponse.prototype, "receiver_if_maker_is_seller", void 0);
5770
5828
  var OfferListItemResponse = class {};
5771
5829
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
5772
5830
  type: () => OfferDataResponse,
@@ -6036,6 +6094,10 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
6036
6094
  type: () => ValidateCallbackRequest,
6037
6095
  example: validateOfferExample.callback
6038
6096
  })], ValidateOfferRequest.prototype, "callback", void 0);
6097
+ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
6098
+ type: "string",
6099
+ example: validateOfferExample.receiver_if_maker_is_seller
6100
+ })], ValidateOfferRequest.prototype, "receiver_if_maker_is_seller", void 0);
6039
6101
  var ValidateOffersRequest = class {};
6040
6102
  __decorate([(0, openapi_metadata_decorators.ApiProperty)({
6041
6103
  type: () => [ValidateOfferRequest],
@@ -6588,13 +6650,13 @@ __decorate([
6588
6650
  methods: ["get"],
6589
6651
  path: "/v1/obligations",
6590
6652
  summary: "List all obligations",
6591
- description: "Returns a list of obligations with their current best ask and bid. Obligations are sorted by their id in ascending order by default."
6653
+ description: "Returns a list of obligations with their current best ask and bid. Sorting is customizable with the sort parameter and defaults to id ascending."
6592
6654
  }),
6593
6655
  (0, openapi_metadata_decorators.ApiQuery)({
6594
6656
  name: "cursor",
6595
6657
  type: "string",
6596
6658
  example: obligationCursorExample,
6597
- description: "Obligation id cursor for pagination."
6659
+ description: "Pagination cursor in base64url-encoded format."
6598
6660
  }),
6599
6661
  (0, openapi_metadata_decorators.ApiQuery)({
6600
6662
  name: "limit",
@@ -6638,6 +6700,15 @@ __decorate([
6638
6700
  style: "form",
6639
6701
  explode: false
6640
6702
  }),
6703
+ (0, openapi_metadata_decorators.ApiQuery)({
6704
+ name: "sort",
6705
+ type: "string",
6706
+ required: false,
6707
+ example: "-ask,bid,maturity",
6708
+ description: "Sort order as comma-separated fields (`id`, `ask`, `bid`, `maturity`). Prefix with `-` for descending order. Max 3 fields.",
6709
+ style: "form",
6710
+ explode: false
6711
+ }),
6641
6712
  (0, openapi_metadata_decorators.ApiResponse)({
6642
6713
  status: 200,
6643
6714
  description: "Success",
@@ -6773,11 +6844,13 @@ function from$2(position) {
6773
6844
  //#endregion
6774
6845
  //#region src/api/Schema/requests.ts
6775
6846
  const MAX_LIMIT = 100;
6776
- const DEFAULT_LIMIT$4 = 20;
6847
+ const DEFAULT_LIMIT$5 = 20;
6848
+ const MAX_OBLIGATION_SORT_FIELDS = 3;
6777
6849
  const CONFIG_RULES_MAX_LIMIT = 1e3;
6778
6850
  const CONFIG_RULES_DEFAULT_LIMIT = 100;
6779
6851
  const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
6780
6852
  const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
6853
+ const OBLIGATION_SORT_ENTRY_REGEX = /^-?(id|ask|bid|maturity)$/;
6781
6854
  /** Validate cursor is a valid base64url-encoded JSON object.
6782
6855
  * Domain layer handles semantic validation of cursor fields. */
6783
6856
  function isValidBase64urlJson(val) {
@@ -6809,8 +6882,8 @@ const PaginationQueryParams = zod.object({
6809
6882
  description: "Pagination cursor in base64url-encoded format",
6810
6883
  example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
6811
6884
  }),
6812
- limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
6813
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
6885
+ limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$5).meta({
6886
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$5}`,
6814
6887
  example: 10
6815
6888
  })
6816
6889
  });
@@ -6907,9 +6980,12 @@ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend
6907
6980
  });
6908
6981
  const GetObligationsQueryParams = zod.object({
6909
6982
  ...PaginationQueryParams.shape,
6910
- cursor: zod.string().optional().meta({
6911
- description: "Obligation id cursor",
6912
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
6983
+ cursor: zod.string().optional().refine((val) => {
6984
+ if (!val) return true;
6985
+ return isValidBase64urlJson(val);
6986
+ }, { message: "Invalid cursor format. Must be a valid base64url-encoded cursor object" }).meta({
6987
+ description: "Pagination cursor in base64url-encoded format.",
6988
+ example: "eyJzb3J0IjpbImlkIl0sImlkIjoiMHgxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAiLCJhc2siOiIwIiwiYmlkIjoiMCIsIm1hdHVyaXR5IjoxNzYxOTIyODAwfQ"
6913
6989
  }),
6914
6990
  chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
6915
6991
  description: "Filter by chain IDs (comma-separated).",
@@ -6926,6 +7002,24 @@ const GetObligationsQueryParams = zod.object({
6926
7002
  maturities: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Maturity must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
6927
7003
  description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
6928
7004
  example: "1761922800,1764524800"
7005
+ }),
7006
+ sort: csvArray(zod.string().regex(OBLIGATION_SORT_ENTRY_REGEX, { message: "Sort entries must be one of: id, ask, bid, maturity (optionally prefixed with '-')" })).refine((entries) => entries === void 0 || entries.length <= MAX_OBLIGATION_SORT_FIELDS, { message: `Sort cannot include more than ${MAX_OBLIGATION_SORT_FIELDS} fields` }).superRefine((entries, ctx) => {
7007
+ if (!entries) return;
7008
+ const seen = /* @__PURE__ */ new Set();
7009
+ for (const entry of entries) {
7010
+ const field = entry.startsWith("-") ? entry.slice(1) : entry;
7011
+ if (seen.has(field)) {
7012
+ ctx.addIssue({
7013
+ code: "custom",
7014
+ message: `Duplicate sort field: ${field}`
7015
+ });
7016
+ return;
7017
+ }
7018
+ seen.add(field);
7019
+ }
7020
+ }).meta({
7021
+ description: "Sort order as comma-separated fields. Prefix a field with '-' for descending order. Max 3 fields.",
7022
+ example: "-ask,bid,maturity"
6929
7023
  })
6930
7024
  });
6931
7025
  const GetObligationParams = zod.object({ obligation_id: zod.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
@@ -6949,8 +7043,8 @@ const BookPaginationQueryParams = zod.object({
6949
7043
  description: "Pagination cursor in base64url-encoded format for book levels",
6950
7044
  example: "eyJzaWRlIjoiYnV5IiwibGFzdFJhdGUiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwib2ZmZXJzQ3Vyc29yIjpudWxsfQ"
6951
7045
  }),
6952
- limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
6953
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
7046
+ limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$5).meta({
7047
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$5}`,
6954
7048
  example: 10
6955
7049
  })
6956
7050
  });
@@ -7127,7 +7221,7 @@ async function getConfigContracts(query, chainRegistry) {
7127
7221
  }
7128
7222
  function parseCursor$1(cursor) {
7129
7223
  const [chain, address] = cursor.split(":", 2);
7130
- if (!chain || !address) throw new BadRequestError("Cursor must be in the format chain_id:0x...");
7224
+ if (!chain || !address) throw new BadRequestError$1("Cursor must be in the format chain_id:0x...");
7131
7225
  return {
7132
7226
  chain_id: Number.parseInt(chain, 10),
7133
7227
  address: address.toLowerCase()
@@ -7253,19 +7347,19 @@ const oracles = {
7253
7347
  const configs = {
7254
7348
  ethereum: {
7255
7349
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7256
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7350
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7257
7351
  },
7258
7352
  base: {
7259
7353
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7260
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7354
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7261
7355
  },
7262
7356
  "ethereum-virtual-testnet": {
7263
7357
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7264
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7358
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7265
7359
  },
7266
7360
  anvil: {
7267
7361
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7268
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7362
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7269
7363
  }
7270
7364
  };
7271
7365
 
@@ -7380,8 +7474,8 @@ async function getConfigRules(query, chains) {
7380
7474
  } catch (err) {
7381
7475
  return failure(err);
7382
7476
  }
7383
- if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError("Cursor type must match requested rule types"));
7384
- if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError("Cursor chain_id must match requested chains"));
7477
+ if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError$1("Cursor type must match requested rule types"));
7478
+ if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError$1("Cursor chain_id must match requested chains"));
7385
7479
  const startIndex = cursorRule ? findStartIndex(filteredRules, cursorRule) : 0;
7386
7480
  const page = filteredRules.slice(startIndex, startIndex + limit);
7387
7481
  const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor(page.at(-1)) : null;
@@ -7401,15 +7495,15 @@ function formatCursor(rule) {
7401
7495
  }
7402
7496
  function parseCursor(cursor) {
7403
7497
  const [type, chain, ...rest] = cursor.split(":");
7404
- if (!type || !chain || rest.length === 0) throw new BadRequestError("Cursor must be in the format type:chain_id:<value>");
7405
- if (!isConfigRuleType(type)) throw new BadRequestError("Cursor has an invalid rule type");
7498
+ if (!type || !chain || rest.length === 0) throw new BadRequestError$1("Cursor must be in the format type:chain_id:<value>");
7499
+ if (!isConfigRuleType(type)) throw new BadRequestError$1("Cursor has an invalid rule type");
7406
7500
  const chain_id = Number.parseInt(chain, 10);
7407
- if (!Number.isFinite(chain_id)) throw new BadRequestError("Cursor has an invalid chain_id");
7501
+ if (!Number.isFinite(chain_id)) throw new BadRequestError$1("Cursor has an invalid chain_id");
7408
7502
  if (type === "maturity") {
7409
7503
  const timestampValue = Number.parseInt(rest[0] ?? "", 10);
7410
7504
  const nameValue = rest.slice(1).join(":");
7411
- if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError("Cursor must be in the format maturity:chain_id:timestamp:name");
7412
- if (!isMaturityType(nameValue)) throw new BadRequestError("Cursor has an invalid maturity name");
7505
+ if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError$1("Cursor must be in the format maturity:chain_id:timestamp:name");
7506
+ if (!isMaturityType(nameValue)) throw new BadRequestError$1("Cursor has an invalid maturity name");
7413
7507
  return {
7414
7508
  type,
7415
7509
  chain_id,
@@ -7420,8 +7514,8 @@ function parseCursor(cursor) {
7420
7514
  if (type === "callback") {
7421
7515
  const callbackTypeValue = rest[0] ?? "";
7422
7516
  const addressValue = rest.slice(1).join(":");
7423
- if (!callbackTypeValue || !addressValue) throw new BadRequestError("Cursor must be in the format callback:chain_id:callback_type:address");
7424
- if (!isCallbackType(callbackTypeValue)) throw new BadRequestError("Cursor has an invalid callback type");
7517
+ if (!callbackTypeValue || !addressValue) throw new BadRequestError$1("Cursor must be in the format callback:chain_id:callback_type:address");
7518
+ if (!isCallbackType(callbackTypeValue)) throw new BadRequestError$1("Cursor has an invalid callback type");
7425
7519
  return {
7426
7520
  type,
7427
7521
  chain_id,
@@ -7431,14 +7525,14 @@ function parseCursor(cursor) {
7431
7525
  }
7432
7526
  if (type === "loan_token" || type === "collateral_token" || type === "oracle") {
7433
7527
  const addressValue = rest.join(":");
7434
- if (!addressValue) throw new BadRequestError(`Cursor must be in the format ${type}:chain_id:address`);
7528
+ if (!addressValue) throw new BadRequestError$1(`Cursor must be in the format ${type}:chain_id:address`);
7435
7529
  return {
7436
7530
  type,
7437
7531
  chain_id,
7438
7532
  address: parseAddress(addressValue, "Cursor address")
7439
7533
  };
7440
7534
  }
7441
- throw new BadRequestError("Cursor has an invalid rule type");
7535
+ throw new BadRequestError$1("Cursor has an invalid rule type");
7442
7536
  }
7443
7537
  function findStartIndex(rules, cursor) {
7444
7538
  let low = 0;
@@ -7452,7 +7546,7 @@ function findStartIndex(rules, cursor) {
7452
7546
  return low;
7453
7547
  }
7454
7548
  function parseAddress(address, label) {
7455
- if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError(`${label} must be a valid 20-byte address`);
7549
+ if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError$1(`${label} must be a valid 20-byte address`);
7456
7550
  return address.toLowerCase();
7457
7551
  }
7458
7552
  function isConfigRuleType(value) {
@@ -7465,7 +7559,7 @@ function parseMaturity(value) {
7465
7559
  try {
7466
7560
  return from$16(value);
7467
7561
  } catch (err) {
7468
- throw new BadRequestError(err instanceof Error ? err.message : "Invalid maturity timestamp");
7562
+ throw new BadRequestError$1(err instanceof Error ? err.message : "Invalid maturity timestamp");
7469
7563
  }
7470
7564
  }
7471
7565
  function isCallbackType(value) {
@@ -7561,7 +7655,7 @@ async function getHealth(query, db, chainRegistry) {
7561
7655
  try {
7562
7656
  const parsed = safeParse("get_health", query);
7563
7657
  if (!parsed.success) return failure(parsed.error);
7564
- const snapshot = await create$16({
7658
+ const snapshot = await create$17({
7565
7659
  db,
7566
7660
  chainRegistry
7567
7661
  }).getSnapshot();
@@ -7590,7 +7684,7 @@ async function getHealthChains(query, db, healthClients, chainRegistry) {
7590
7684
  try {
7591
7685
  const parsed = safeParse("get_health_chains", query);
7592
7686
  if (!parsed.success) return failure(parsed.error);
7593
- const snapshot = await create$16({
7687
+ const snapshot = await create$17({
7594
7688
  db,
7595
7689
  healthClients,
7596
7690
  chainRegistry
@@ -7622,7 +7716,7 @@ async function getHealthCollectors(query, db, chainRegistry) {
7622
7716
  try {
7623
7717
  const parsed = safeParse("get_health_collectors", query);
7624
7718
  if (!parsed.success) return failure(parsed.error);
7625
- const snapshot = await create$16({
7719
+ const snapshot = await create$17({
7626
7720
  db,
7627
7721
  chainRegistry
7628
7722
  }).getSnapshot();
@@ -7651,39 +7745,274 @@ async function getHealthCollectors(query, db, chainRegistry) {
7651
7745
  }
7652
7746
  }
7653
7747
 
7748
+ //#endregion
7749
+ //#region src/database/readers/ObligationsListing.ts
7750
+ const SORT_FIELDS = [
7751
+ "id",
7752
+ "ask",
7753
+ "bid",
7754
+ "maturity"
7755
+ ];
7756
+ const CURSOR_ID_REGEX = /^0x[a-f0-9]{64}$/i;
7757
+ const INT32_MIN = -2147483648;
7758
+ const INT32_MAX = 2147483647;
7759
+ const INT32_MAX_BIGINT = BigInt(INT32_MAX);
7760
+ const MAX_CURSOR_SORT_FIELDS = 4;
7761
+ const DEFAULT_LIMIT$4 = 20;
7762
+ var BadRequestError = class extends Error {
7763
+ constructor(message) {
7764
+ super(message);
7765
+ this.name = "ObligationsListingBadRequestError";
7766
+ }
7767
+ };
7768
+ /**
7769
+ * Creates the obligations listing reader facade.
7770
+ * @param parameters - Reader dependencies.
7771
+ * @returns Obligations listing reader.
7772
+ */
7773
+ function create$16(parameters) {
7774
+ const { db } = parameters;
7775
+ return { list: async (queryParameters) => {
7776
+ const { ids, chainId: chainIds, loanToken: loanTokens, collateralToken: collateralTokens, maturity: maturities, sort: sortTokens, cursor: encodedCursor, limit: requestedLimit } = queryParameters ?? {};
7777
+ const limit = requestedLimit ?? DEFAULT_LIMIT$4;
7778
+ if (!Number.isInteger(limit) || limit <= 0) throw new BadRequestError("Limit must be a positive integer");
7779
+ const cursorPayload = encodedCursor ? decodeCursorPayload(encodedCursor) : void 0;
7780
+ const requestedSort = normalizeSort(sortTokens);
7781
+ const cursorSort = cursorPayload ? normalizeSort(cursorPayload.sort) : void 0;
7782
+ if (cursorSort !== void 0 && sortTokens !== void 0 && !hasSameSort(requestedSort, cursorSort)) throw new BadRequestError("Cursor sort does not match requested sort");
7783
+ const sort = sortTokens !== void 0 ? requestedSort : cursorSort ?? requestedSort;
7784
+ const cursorValues = cursorPayload ? cursorValuesFromPayload(cursorPayload) : void 0;
7785
+ const now$3 = now();
7786
+ const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? drizzle_orm.sql`(${drizzle_orm.sql.join(loanTokens.map((token) => drizzle_orm.sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), drizzle_orm.sql` OR `)})` : void 0;
7787
+ const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? drizzle_orm.sql`EXISTS (
7788
+ SELECT 1 FROM ${obligationCollateralsV2} oc
7789
+ WHERE oc.obligation_id = ${obligations.obligationId}
7790
+ AND (${drizzle_orm.sql.join(collateralTokens.map((token) => drizzle_orm.sql`LOWER(oc.asset) = ${token.toLowerCase()}`), drizzle_orm.sql` OR `)})
7791
+ )` : void 0;
7792
+ const bestAskTick = db.select({ askTick: offers.tick }).from(offers).innerJoin(groups, (0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.groupChainId, groups.chainId), (0, drizzle_orm.eq)(offers.groupMaker, groups.maker), (0, drizzle_orm.eq)(offers.group, groups.group))).leftJoin(validations, (0, drizzle_orm.eq)(offers.hash, validations.offerHash)).leftJoin(status, (0, drizzle_orm.eq)(validations.statusId, status.id)).where((0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.obligationId, obligations.obligationId), (0, drizzle_orm.eq)(offers.buy, false), (0, drizzle_orm.gte)(offers.expiry, now$3), (0, drizzle_orm.gte)(offers.maturity, now$3), (0, drizzle_orm.lte)(offers.start, now$3), drizzle_orm.sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy((0, drizzle_orm.desc)(offers.tick)).limit(1).as("best_ask_tick");
7793
+ const bestBidTick = db.select({ bidTick: offers.tick }).from(offers).innerJoin(groups, (0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.groupChainId, groups.chainId), (0, drizzle_orm.eq)(offers.groupMaker, groups.maker), (0, drizzle_orm.eq)(offers.group, groups.group))).leftJoin(validations, (0, drizzle_orm.eq)(offers.hash, validations.offerHash)).leftJoin(status, (0, drizzle_orm.eq)(validations.statusId, status.id)).where((0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.obligationId, obligations.obligationId), (0, drizzle_orm.eq)(offers.buy, true), (0, drizzle_orm.gte)(offers.expiry, now$3), (0, drizzle_orm.gte)(offers.maturity, now$3), (0, drizzle_orm.lte)(offers.start, now$3), drizzle_orm.sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy((0, drizzle_orm.asc)(offers.tick)).limit(1).as("best_bid_tick");
7794
+ const obligationsWithQuotes = db.select({
7795
+ obligationId: obligations.obligationId,
7796
+ chainId: obligations.chainId,
7797
+ loanToken: obligations.loanToken,
7798
+ collaterals: drizzle_orm.sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
7799
+ maturity: obligations.maturity,
7800
+ askTick: drizzle_orm.sql`MAX(${bestAskTick.askTick})`.as("ask_tick"),
7801
+ bidTick: drizzle_orm.sql`MAX(${bestBidTick.bidTick})`.as("bid_tick"),
7802
+ ask: drizzle_orm.sql`COALESCE(MAX(${bestAskTick.askTick}) + 1, 0)`.as("ask"),
7803
+ bid: drizzle_orm.sql`COALESCE(MAX(${bestBidTick.bidTick}) + 1, 0)`.as("bid")
7804
+ }).from(obligations).innerJoin(obligationCollateralsV2, (0, drizzle_orm.eq)(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
7805
+ AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).leftJoinLateral(bestAskTick, drizzle_orm.sql`true`).leftJoinLateral(bestBidTick, drizzle_orm.sql`true`).groupBy(obligations.obligationId).where((0, drizzle_orm.and)(ids !== void 0 && ids.length > 0 ? (0, drizzle_orm.inArray)(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? (0, drizzle_orm.inArray)(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? (0, drizzle_orm.inArray)(obligations.maturity, maturities) : (0, drizzle_orm.gte)(obligations.maturity, now$3), collateralFilter)).as("obligations_with_quotes");
7806
+ const sortColumns = {
7807
+ id: obligationsWithQuotes.obligationId,
7808
+ ask: obligationsWithQuotes.ask,
7809
+ bid: obligationsWithQuotes.bid,
7810
+ maturity: obligationsWithQuotes.maturity
7811
+ };
7812
+ const rows = await db.select({
7813
+ obligationId: obligationsWithQuotes.obligationId,
7814
+ chainId: obligationsWithQuotes.chainId,
7815
+ loanToken: obligationsWithQuotes.loanToken,
7816
+ collaterals: obligationsWithQuotes.collaterals,
7817
+ maturity: obligationsWithQuotes.maturity,
7818
+ askTick: obligationsWithQuotes.askTick,
7819
+ bidTick: obligationsWithQuotes.bidTick,
7820
+ ask: obligationsWithQuotes.ask,
7821
+ bid: obligationsWithQuotes.bid
7822
+ }).from(obligationsWithQuotes).where(buildCursorFilter(sortColumns, sort, cursorValues)).orderBy(...buildOrderBy(sortColumns, sort)).limit(limit + 1);
7823
+ const hasMore = rows.length > limit;
7824
+ const listedRows = (hasMore ? rows.slice(0, limit) : rows).map((row) => {
7825
+ return {
7826
+ obligation: from$15({
7827
+ chainId: row.chainId,
7828
+ loanToken: row.loanToken,
7829
+ collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$17({
7830
+ asset: collateral.asset,
7831
+ oracle: collateral.oracle,
7832
+ lltv: from$18(BigInt(collateral.lltv))
7833
+ })),
7834
+ maturity: row.maturity
7835
+ }),
7836
+ quote: from$11({
7837
+ obligationId: row.obligationId,
7838
+ ask: { tick: row.askTick },
7839
+ bid: { tick: row.bidTick }
7840
+ }),
7841
+ cursorValues: {
7842
+ id: row.obligationId,
7843
+ ask: toBigInt(row.ask),
7844
+ bid: toBigInt(row.bid),
7845
+ maturity: row.maturity
7846
+ }
7847
+ };
7848
+ });
7849
+ const nextCursor = hasMore && listedRows.length > 0 ? encodeCursorPayload({
7850
+ sort: sortToTokens(sort),
7851
+ id: listedRows[listedRows.length - 1].cursorValues.id,
7852
+ ask: listedRows[listedRows.length - 1].cursorValues.ask.toString(),
7853
+ bid: listedRows[listedRows.length - 1].cursorValues.bid.toString(),
7854
+ maturity: listedRows[listedRows.length - 1].cursorValues.maturity
7855
+ }) : null;
7856
+ return {
7857
+ obligations: listedRows.map((row) => ({
7858
+ obligation: row.obligation,
7859
+ quote: row.quote
7860
+ })),
7861
+ nextCursor
7862
+ };
7863
+ } };
7864
+ }
7865
+ function isSortField(value) {
7866
+ return SORT_FIELDS.includes(value);
7867
+ }
7868
+ function parseSortToken(token) {
7869
+ const direction = token.startsWith("-") ? "desc" : "asc";
7870
+ const rawField = token.startsWith("-") ? token.slice(1) : token;
7871
+ if (!isSortField(rawField)) throw new BadRequestError(`Invalid sort field: ${rawField}`);
7872
+ return {
7873
+ field: rawField,
7874
+ direction
7875
+ };
7876
+ }
7877
+ function normalizeSort(sortTokens) {
7878
+ const parsed = sortTokens?.length ? sortTokens.map(parseSortToken) : [{
7879
+ field: "id",
7880
+ direction: "asc"
7881
+ }];
7882
+ return parsed.some((entry) => entry.field === "id") ? parsed : [...parsed, {
7883
+ field: "id",
7884
+ direction: "asc"
7885
+ }];
7886
+ }
7887
+ function sortToTokens(sortEntries) {
7888
+ return sortEntries.map((entry) => entry.direction === "desc" ? `-${entry.field}` : entry.field);
7889
+ }
7890
+ function hasSameSort(left, right) {
7891
+ if (left.length !== right.length) return false;
7892
+ return left.every((sortEntry, index) => sortEntry.field === right[index]?.field && sortEntry.direction === right[index]?.direction);
7893
+ }
7894
+ function decodeCursorPayload(cursor) {
7895
+ let decoded;
7896
+ try {
7897
+ decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
7898
+ } catch {
7899
+ throw new BadRequestError("Invalid cursor format");
7900
+ }
7901
+ if (decoded === null || typeof decoded !== "object") throw new BadRequestError("Invalid cursor payload");
7902
+ const payload = decoded;
7903
+ const sortTokens = parseCursorSortTokens(payload.sort);
7904
+ if (typeof payload.id !== "string" || !CURSOR_ID_REGEX.test(payload.id)) throw new BadRequestError("Invalid cursor obligation id");
7905
+ const ask = parseCursorNonNegativeInt32(payload.ask, "ask");
7906
+ const bid = parseCursorNonNegativeInt32(payload.bid, "bid");
7907
+ if (!isInt32(payload.maturity)) throw new BadRequestError("Invalid cursor maturity value");
7908
+ return {
7909
+ sort: sortTokens,
7910
+ id: payload.id,
7911
+ ask,
7912
+ bid,
7913
+ maturity: payload.maturity
7914
+ };
7915
+ }
7916
+ function parseCursorSortTokens(value) {
7917
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_CURSOR_SORT_FIELDS) throw new BadRequestError("Invalid cursor sort");
7918
+ const sortEntries = value.map((token) => {
7919
+ if (typeof token !== "string") throw new BadRequestError("Invalid cursor sort");
7920
+ try {
7921
+ return parseSortToken(token);
7922
+ } catch {
7923
+ throw new BadRequestError("Invalid cursor sort");
7924
+ }
7925
+ });
7926
+ if (new Set(sortEntries.map((entry) => entry.field)).size !== sortEntries.length) throw new BadRequestError("Invalid cursor sort");
7927
+ return sortToTokens(sortEntries);
7928
+ }
7929
+ function parseCursorNonNegativeInt32(value, field) {
7930
+ if (typeof value !== "string" || !/^\d+$/.test(value)) throw new BadRequestError(`Invalid cursor ${field} value`);
7931
+ if (BigInt(value) > INT32_MAX_BIGINT) throw new BadRequestError(`Invalid cursor ${field} value`);
7932
+ return value;
7933
+ }
7934
+ function isInt32(value) {
7935
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= INT32_MIN && value <= INT32_MAX;
7936
+ }
7937
+ function encodeCursorPayload(payload) {
7938
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
7939
+ }
7940
+ function cursorValuesFromPayload(payload) {
7941
+ return {
7942
+ id: payload.id,
7943
+ ask: BigInt(payload.ask),
7944
+ bid: BigInt(payload.bid),
7945
+ maturity: payload.maturity
7946
+ };
7947
+ }
7948
+ function cursorComparisonValue(cursorValues, field) {
7949
+ switch (field) {
7950
+ case "id": return cursorValues.id;
7951
+ case "maturity": return cursorValues.maturity;
7952
+ case "ask": return cursorValues.ask.toString();
7953
+ case "bid": return cursorValues.bid.toString();
7954
+ }
7955
+ }
7956
+ function buildCursorFilter(columns, sortEntries, cursorValues) {
7957
+ if (cursorValues === void 0) return void 0;
7958
+ const comparisons = sortEntries.map((sortEntry, index) => {
7959
+ const equals = sortEntries.slice(0, index).map((previous) => {
7960
+ return drizzle_orm.sql`${columns[previous.field]} = ${cursorComparisonValue(cursorValues, previous.field)}`;
7961
+ });
7962
+ const comparison = sortEntry.direction === "asc" ? drizzle_orm.sql`${columns[sortEntry.field]} > ${cursorComparisonValue(cursorValues, sortEntry.field)}` : drizzle_orm.sql`${columns[sortEntry.field]} < ${cursorComparisonValue(cursorValues, sortEntry.field)}`;
7963
+ return equals.length > 0 ? drizzle_orm.sql`(${drizzle_orm.sql.join([...equals, comparison], drizzle_orm.sql` AND `)})` : drizzle_orm.sql`(${comparison})`;
7964
+ });
7965
+ return comparisons.length > 0 ? drizzle_orm.sql`(${drizzle_orm.sql.join(comparisons, drizzle_orm.sql` OR `)})` : void 0;
7966
+ }
7967
+ function buildOrderBy(columns, sortEntries) {
7968
+ return sortEntries.map((sortEntry) => sortEntry.direction === "asc" ? (0, drizzle_orm.asc)(columns[sortEntry.field]) : (0, drizzle_orm.desc)(columns[sortEntry.field]));
7969
+ }
7970
+ function toBigInt(value) {
7971
+ if (typeof value === "bigint") return value;
7972
+ if (typeof value === "number") return BigInt(value);
7973
+ return BigInt(value.split(".")[0] ?? "0");
7974
+ }
7975
+
7654
7976
  //#endregion
7655
7977
  //#region src/api/Controllers/getObligation.ts
7978
+ function toPayloadError$1(err) {
7979
+ if (err instanceof BadRequestError) return new BadRequestError$1(err.message);
7980
+ return err;
7981
+ }
7656
7982
  async function getObligation(params, db) {
7657
7983
  const logger = getLogger();
7658
7984
  const result = safeParse("get_obligation", params, (issue) => issue.message);
7659
7985
  if (!result.success) return failure(result.error);
7660
7986
  const query = result.data;
7661
7987
  try {
7662
- const { obligations } = await db.offers.getObligations({ ids: [query.obligation_id] });
7663
- if (obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
7664
- const obligation = obligations[0];
7665
- const [quote] = await db.offers.getQuotes({ obligationIds: [id(obligation)] });
7988
+ const listing = await db.readers.obligations.list({
7989
+ ids: [query.obligation_id],
7990
+ limit: 1
7991
+ });
7992
+ if (listing.obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
7993
+ const obligation = listing.obligations[0];
7666
7994
  return success({
7667
- data: from$4(obligation, quote ?? {
7668
- obligationId: id(obligation),
7669
- ask: { price: 0n },
7670
- bid: { price: 0n }
7671
- }),
7995
+ data: from$4(obligation.obligation, obligation.quote),
7672
7996
  cursor: null
7673
7997
  });
7674
7998
  } catch (err) {
7999
+ const payloadError = toPayloadError$1(err);
7675
8000
  logger.error({
7676
- err,
8001
+ err: payloadError,
7677
8002
  msg: "Error get obligation",
7678
- errorMessage: err instanceof Error ? err.message : String(err),
7679
- errorStack: err instanceof Error ? err.stack : void 0
8003
+ errorMessage: payloadError instanceof Error ? payloadError.message : String(payloadError),
8004
+ errorStack: payloadError instanceof Error ? payloadError.stack : void 0
7680
8005
  });
7681
- return failure(err);
8006
+ return failure(payloadError);
7682
8007
  }
7683
8008
  }
7684
8009
 
7685
8010
  //#endregion
7686
8011
  //#region src/api/Controllers/getObligations.ts
8012
+ function toPayloadError(err) {
8013
+ if (err instanceof BadRequestError) return new BadRequestError$1(err.message);
8014
+ return err;
8015
+ }
7687
8016
  async function getObligations$1(queryParameters, db) {
7688
8017
  const logger = getLogger();
7689
8018
  const result = safeParse("get_obligations", queryParameters, (issue) => issue.message);
@@ -7694,31 +8023,28 @@ async function getObligations$1(queryParameters, db) {
7694
8023
  const loanTokens = query.loan_tokens?.length ? query.loan_tokens : void 0;
7695
8024
  const collateralTokens = query.collateral_tokens?.length ? query.collateral_tokens : void 0;
7696
8025
  const maturities = query.maturities?.length ? query.maturities : void 0;
7697
- const { obligations, nextCursor } = await db.offers.getObligations({
7698
- cursor: query.cursor,
7699
- limit: query.limit,
8026
+ const listing = await db.readers.obligations.list({
7700
8027
  chainId: chainIds,
7701
8028
  loanToken: loanTokens,
7702
8029
  collateralToken: collateralTokens,
7703
- maturity: maturities
8030
+ maturity: maturities,
8031
+ sort: query.sort,
8032
+ cursor: query.cursor,
8033
+ limit: query.limit
7704
8034
  });
7705
- const quotes = await db.offers.getQuotes({ obligationIds: obligations.map((o) => id(o)) });
7706
8035
  return success({
7707
- data: obligations.map((o) => from$4(o, quotes.find((q) => q.obligationId === id(o)) ?? {
7708
- obligationId: id(o),
7709
- ask: { price: 0n },
7710
- bid: { price: 0n }
7711
- })),
7712
- cursor: nextCursor ?? null
8036
+ data: listing.obligations.map((item) => from$4(item.obligation, item.quote)),
8037
+ cursor: listing.nextCursor
7713
8038
  });
7714
8039
  } catch (err) {
8040
+ const payloadError = toPayloadError(err);
7715
8041
  logger.error({
7716
- err,
8042
+ err: payloadError,
7717
8043
  msg: "Error get obligations",
7718
- errorMessage: err instanceof Error ? err.message : String(err),
7719
- errorStack: err instanceof Error ? err.stack : void 0
8044
+ errorMessage: payloadError instanceof Error ? payloadError.message : String(payloadError),
8045
+ errorStack: payloadError instanceof Error ? payloadError.stack : void 0
7720
8046
  });
7721
- return failure(err);
8047
+ return failure(payloadError);
7722
8048
  }
7723
8049
  }
7724
8050
 
@@ -7752,6 +8078,7 @@ function create$15(config) {
7752
8078
  groupMaker: offer.maker.toLowerCase(),
7753
8079
  callbackAddress: offer.callback.address.toLowerCase(),
7754
8080
  callbackData: offer.callback.data,
8081
+ receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase(),
7755
8082
  blockNumber
7756
8083
  })));
7757
8084
  if (offersRows.length === 0) return [];
@@ -7814,9 +8141,11 @@ function create$15(config) {
7814
8141
  loanToken: obligations.loanToken,
7815
8142
  callbackAddress: offers.callbackAddress,
7816
8143
  callbackData: offers.callbackData,
8144
+ receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
7817
8145
  collaterals: collateralsLateral.collaterals,
7818
8146
  blockNumber: offers.blockNumber
7819
8147
  }).from(offers).innerJoin(obligations, (0, drizzle_orm.eq)(offers.obligationId, obligations.obligationId)).innerJoinLateral(collateralsLateral, drizzle_orm.sql`true`).where((0, drizzle_orm.and)(cursor !== null && cursor !== void 0 ? (0, drizzle_orm.gt)(offers.hash, cursor) : void 0, maker !== void 0 ? (0, drizzle_orm.eq)(offers.groupMaker, maker.toLowerCase()) : void 0)).orderBy((0, drizzle_orm.asc)(offers.hash)).limit(limit)).map((row) => {
8148
+ const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
7820
8149
  return {
7821
8150
  hash: row.hash,
7822
8151
  maker: row.maker,
@@ -7841,6 +8170,7 @@ function create$15(config) {
7841
8170
  address: row.callbackAddress,
7842
8171
  data: row.callbackData
7843
8172
  },
8173
+ receiverIfMakerIsSeller,
7844
8174
  consumed: 0n,
7845
8175
  available: 0n,
7846
8176
  takeable: 0n,
@@ -7865,64 +8195,30 @@ function create$15(config) {
7865
8195
  }
7866
8196
  throw new Error("Invalid parameters");
7867
8197
  },
7868
- getObligations: async (parameters) => {
7869
- const { ids, chainId: chainIds, loanToken: loanTokens, collateralToken: collateralTokens, maturity: maturities, cursor, limit = DEFAULT_LIMIT$3 } = parameters ?? {};
7870
- const now$1 = now();
7871
- const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? drizzle_orm.sql`(${drizzle_orm.sql.join(loanTokens.map((token) => drizzle_orm.sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), drizzle_orm.sql` OR `)})` : void 0;
7872
- const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? drizzle_orm.sql`EXISTS (
7873
- SELECT 1 FROM ${obligationCollateralsV2} oc
7874
- WHERE oc.obligation_id = ${obligations.obligationId}
7875
- AND (${drizzle_orm.sql.join(collateralTokens.map((token) => drizzle_orm.sql`LOWER(oc.asset) = ${token.toLowerCase()}`), drizzle_orm.sql` OR `)})
7876
- )` : void 0;
7877
- const result = await db.select({
7878
- obligationId: obligations.obligationId,
7879
- chainId: obligations.chainId,
7880
- loanToken: obligations.loanToken,
7881
- collaterals: drizzle_orm.sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
7882
- maturity: obligations.maturity
7883
- }).from(obligations).innerJoin(obligationCollateralsV2, (0, drizzle_orm.eq)(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
7884
- AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).groupBy(obligations.obligationId).where((0, drizzle_orm.and)(cursor !== null && cursor !== void 0 ? (0, drizzle_orm.gt)(obligations.obligationId, cursor) : drizzle_orm.sql`true`, ids !== void 0 && ids.length > 0 ? (0, drizzle_orm.inArray)(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? (0, drizzle_orm.inArray)(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? (0, drizzle_orm.inArray)(obligations.maturity, maturities) : (0, drizzle_orm.gte)(obligations.maturity, now$1), collateralFilter)).orderBy((0, drizzle_orm.asc)(obligations.obligationId)).limit(limit);
7885
- const items = [];
7886
- for (const row of result) items.push(from$15({
7887
- chainId: row.chainId,
7888
- loanToken: row.loanToken,
7889
- collaterals: row.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)).map((c) => from$17({
7890
- asset: c.asset,
7891
- oracle: c.oracle,
7892
- lltv: from$18(BigInt(c.lltv))
7893
- })),
7894
- maturity: row.maturity
7895
- }));
7896
- const returnedItems = Array.from(items.values());
7897
- return {
7898
- obligations: returnedItems,
7899
- nextCursor: returnedItems.length === limit && returnedItems.length > 0 ? result[result.length - 1].obligationId : null
7900
- };
7901
- },
7902
8198
  getQuotes: async (parameters) => {
7903
8199
  const { obligationIds } = parameters;
7904
8200
  if (obligationIds.length === 0) return [];
7905
8201
  const now$2 = now();
7906
8202
  const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
7907
8203
  obligationId: offers.obligationId,
7908
- price: offers.tick
8204
+ tick: offers.tick
7909
8205
  }).from(offers).innerJoin(groups, (0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.groupChainId, groups.chainId), (0, drizzle_orm.eq)(offers.groupMaker, groups.maker), (0, drizzle_orm.eq)(offers.group, groups.group))).leftJoin(validations, (0, drizzle_orm.eq)(offers.hash, validations.offerHash)).leftJoin(status, (0, drizzle_orm.eq)(validations.statusId, status.id)).where((0, drizzle_orm.and)((0, drizzle_orm.inArray)(offers.obligationId, obligationIds), (0, drizzle_orm.eq)(offers.buy, side === "buy"), (0, drizzle_orm.gte)(offers.expiry, now$2), (0, drizzle_orm.gte)(offers.maturity, now$2), (0, drizzle_orm.lte)(offers.start, now$2), drizzle_orm.sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(offers.obligationId, side === "buy" ? drizzle_orm.sql`${offers.tick} ASC` : drizzle_orm.sql`${offers.tick} DESC`);
7910
8206
  const [bestBuys, bestSells] = await Promise.all([query({ side: "buy" }), query({ side: "sell" })]);
7911
8207
  const quotes = /* @__PURE__ */ new Map();
7912
8208
  for (const row of bestSells) quotes.set(row.obligationId, {
7913
- ask: { price: BigInt(row.price) },
7914
- bid: { price: 0n }
8209
+ ask: { tick: row.tick },
8210
+ bid: { tick: null }
7915
8211
  });
7916
8212
  for (const row of bestBuys) {
7917
8213
  const quote = quotes.get(row.obligationId);
7918
8214
  if (!quote) {
7919
8215
  quotes.set(row.obligationId, {
7920
- ask: { price: 0n },
7921
- bid: { price: BigInt(row.price) }
8216
+ ask: { tick: null },
8217
+ bid: { tick: row.tick }
7922
8218
  });
7923
8219
  continue;
7924
8220
  }
7925
- quote.bid = { price: BigInt(row.price) };
8221
+ quote.bid = { tick: row.tick };
7926
8222
  }
7927
8223
  return Array.from(quotes.entries()).map(([id, quote]) => {
7928
8224
  return from$11({
@@ -8033,6 +8329,7 @@ async function getOffersQuery(db, parameters) {
8033
8329
  loanToken: obligations.loanToken,
8034
8330
  callbackAddress: offers.callbackAddress,
8035
8331
  callbackData: offers.callbackData,
8332
+ receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
8036
8333
  collaterals: collateralsLateral.collaterals,
8037
8334
  blockNumber: offers.blockNumber,
8038
8335
  available: drizzle_orm.sql`${availableExpr}::numeric`.as("available"),
@@ -8054,6 +8351,7 @@ async function getOffersQuery(db, parameters) {
8054
8351
  )
8055
8352
  END
8056
8353
  ) > 0` : void 0)).orderBy((0, drizzle_orm.asc)(offers.hash)).limit(limit)).map((row) => {
8354
+ const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
8057
8355
  return {
8058
8356
  hash: row.hash,
8059
8357
  maker: row.maker,
@@ -8078,6 +8376,7 @@ async function getOffersQuery(db, parameters) {
8078
8376
  address: row.callbackAddress,
8079
8377
  data: row.callbackData
8080
8378
  },
8379
+ receiverIfMakerIsSeller,
8081
8380
  consumed: BigInt(row.consumed),
8082
8381
  available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
8083
8382
  takeable: BigInt(String(row.takeable ?? "0").split(".")[0] ?? "0"),
@@ -8168,7 +8467,7 @@ async function getUserPositions(queryParameters, db) {
8168
8467
  async function validateOffers(body, gatekeeper) {
8169
8468
  const logger = getLogger();
8170
8469
  const result = safeParse("validate_offers", body, (issue) => issue.message);
8171
- if (!result.success) return failure(new BadRequestError(result.error.issues[0]?.message ?? "Invalid request body"));
8470
+ if (!result.success) return failure(new BadRequestError$1(result.error.issues[0]?.message ?? "Invalid request body"));
8172
8471
  const { offers: rawOffers } = result.data;
8173
8472
  const parsedOffers = [];
8174
8473
  const offerIndexByHash = /* @__PURE__ */ new Map();
@@ -8184,7 +8483,7 @@ async function validateOffers(body, gatekeeper) {
8184
8483
  } catch (err) {
8185
8484
  let message = err instanceof Error ? err.message : String(err);
8186
8485
  if (err instanceof InvalidOfferError) message = err.formattedMessage;
8187
- return failure(new BadRequestError(`Offer at index ${i} failed to parse: ${message}`));
8486
+ return failure(new BadRequestError$1(`Offer at index ${i} failed to parse: ${message}`));
8188
8487
  }
8189
8488
  }
8190
8489
  try {
@@ -8476,7 +8775,8 @@ async function getOffers(apiClient, parameters) {
8476
8775
  callback: {
8477
8776
  address: offerData.callback,
8478
8777
  data: offerData.callback_data
8479
- }
8778
+ },
8779
+ receiver_if_maker_is_seller: offerData.receiver_if_maker_is_seller
8480
8780
  }),
8481
8781
  hash: item.offer_hash,
8482
8782
  consumed: BigInt(item.consumed),
@@ -8493,13 +8793,15 @@ async function getOffers(apiClient, parameters) {
8493
8793
  };
8494
8794
  }
8495
8795
  async function getObligations(apiClient, parameters) {
8796
+ const sort = parameters?.sort?.length ? parameters.sort.join(",") : void 0;
8496
8797
  const { data, error, response } = await apiClient.GET("/v1/obligations", { params: { query: {
8497
8798
  cursor: parameters?.cursor,
8498
8799
  limit: parameters?.limit,
8499
8800
  chains: parameters?.chainIds,
8500
8801
  loan_tokens: parameters?.loanTokens,
8501
8802
  collateral_tokens: parameters?.collateralTokens,
8502
- maturities: parameters?.maturities
8803
+ maturities: parameters?.maturities,
8804
+ sort
8503
8805
  } } });
8504
8806
  if (error !== void 0) {
8505
8807
  switch (response.status) {
@@ -8523,10 +8825,10 @@ async function getObligations(apiClient, parameters) {
8523
8825
  const { obligationId: _, ...returned } = {
8524
8826
  id: () => id(obligation),
8525
8827
  ...obligation,
8526
- ...fromSnakeCase({
8527
- obligation_id: item.id,
8528
- ask: item.ask,
8529
- bid: item.bid
8828
+ ...from$11({
8829
+ obligationId: item.id,
8830
+ ask: { tick: item.ask.tick },
8831
+ bid: { tick: item.bid.tick }
8530
8832
  })
8531
8833
  };
8532
8834
  return returned;
@@ -8982,6 +9284,7 @@ async function _getOffers(db, params) {
8982
9284
  p.buy,
8983
9285
  p.callback_address,
8984
9286
  p.callback_data,
9287
+ p.receiver_if_maker_is_seller,
8985
9288
  p.block_number,
8986
9289
  p.group_chain_id,
8987
9290
  p.group_maker,
@@ -9059,6 +9362,7 @@ async function _getOffers(db, params) {
9059
9362
  buy,
9060
9363
  callback_address,
9061
9364
  callback_data,
9365
+ receiver_if_maker_is_seller,
9062
9366
  block_number,
9063
9367
  group_chain_id,
9064
9368
  group_maker,
@@ -9076,8 +9380,8 @@ async function _getOffers(db, params) {
9076
9380
  ORDER BY clc.hash, clc.position_chain_id, clc.position_contract, clc.position_user, clc.contribution_in_loan DESC
9077
9381
  ) deduped
9078
9382
  GROUP BY hash, obligation_id, assets, tick, obligation_units, obligation_shares, maturity, expiry, start, group_group, buy,
9079
- callback_address, callback_data, block_number, group_chain_id, group_maker,
9080
- consumed, chain_id, loan_token, session
9383
+ callback_address, callback_data, block_number, group_chain_id, group_maker,
9384
+ consumed, chain_id, loan_token, session, receiver_if_maker_is_seller
9081
9385
  UNION ALL
9082
9386
  -- Sell offers without callbacks: collateral positions not indexed, takeable = assets - consumed
9083
9387
  SELECT
@@ -9085,6 +9389,7 @@ async function _getOffers(db, params) {
9085
9389
  p.obligation_units, p.obligation_shares,
9086
9390
  p.maturity, p.expiry, p.start, p.group_group,
9087
9391
  p.buy, p.callback_address, p.callback_data,
9392
+ p.receiver_if_maker_is_seller,
9088
9393
  p.block_number, p.group_chain_id, p.group_maker,
9089
9394
  p.consumed, p.chain_id, p.loan_token, p.session,
9090
9395
  0 AS total_available
@@ -9113,6 +9418,7 @@ async function _getOffers(db, params) {
9113
9418
  oc.loan_token,
9114
9419
  oc.callback_address,
9115
9420
  oc.callback_data,
9421
+ oc.receiver_if_maker_is_seller,
9116
9422
  oc.block_number,
9117
9423
  oc.session,
9118
9424
  COALESCE(oc.total_available, 0) AS available,
@@ -9142,6 +9448,7 @@ async function _getOffers(db, params) {
9142
9448
  `);
9143
9449
  return {
9144
9450
  rows: raw.rows.map((row) => {
9451
+ const receiverIfMakerIsSeller = (row.receiver_if_maker_is_seller ?? row.group_maker).toLowerCase();
9145
9452
  return {
9146
9453
  hash: row.hash,
9147
9454
  maker: row.group_maker,
@@ -9166,6 +9473,7 @@ async function _getOffers(db, params) {
9166
9473
  address: row.callback_address,
9167
9474
  data: row.callback_data
9168
9475
  },
9476
+ receiverIfMakerIsSeller,
9169
9477
  blockNumber: row.block_number,
9170
9478
  consumed: BigInt(row.consumed ?? 0),
9171
9479
  available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
@@ -9415,38 +9723,60 @@ function create$8(db) {
9415
9723
  * @returns Obligations domain. {@link ObligationsDomain}
9416
9724
  */
9417
9725
  function create$7(db) {
9418
- return { create: async (obligations$1) => {
9419
- if (obligations$1.length === 0) return;
9420
- const obligationsById = /* @__PURE__ */ new Map();
9421
- for (const obligation of obligations$1) {
9422
- const id$1 = id(obligation).toLowerCase();
9423
- if (!obligationsById.get(id$1)) obligationsById.set(id$1, obligation);
9424
- }
9425
- try {
9426
- await db.transaction(async (dbTx) => {
9427
- const obligationRows = obligations$1.map((obligation) => ({
9428
- obligationId: id(obligation),
9429
- chainId: obligation.chainId,
9430
- loanToken: obligation.loanToken.toLowerCase(),
9431
- maturity: obligation.maturity
9432
- }));
9433
- for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
9434
- const collateralRows = obligations$1.flatMap((obligation) => {
9435
- return obligation.collaterals.map((collateral) => ({
9726
+ return {
9727
+ get: async (parameters) => {
9728
+ const chainIds = parameters?.chainId;
9729
+ const now$1 = now();
9730
+ return (await db.select({
9731
+ chainId: obligations.chainId,
9732
+ loanToken: obligations.loanToken,
9733
+ collaterals: drizzle_orm.sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
9734
+ maturity: obligations.maturity
9735
+ }).from(obligations).innerJoin(obligationCollateralsV2, (0, drizzle_orm.eq)(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
9736
+ AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).groupBy(obligations.obligationId).where((0, drizzle_orm.and)(chainIds !== void 0 && chainIds.length > 0 ? (0, drizzle_orm.inArray)(obligations.chainId, chainIds) : void 0, (0, drizzle_orm.gte)(obligations.maturity, now$1))).orderBy((0, drizzle_orm.asc)(obligations.obligationId))).map((row) => from$15({
9737
+ chainId: row.chainId,
9738
+ loanToken: row.loanToken,
9739
+ collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$17({
9740
+ asset: collateral.asset,
9741
+ oracle: collateral.oracle,
9742
+ lltv: from$18(BigInt(collateral.lltv))
9743
+ })),
9744
+ maturity: row.maturity
9745
+ }));
9746
+ },
9747
+ create: async (obligations$1) => {
9748
+ if (obligations$1.length === 0) return;
9749
+ const obligationsById = /* @__PURE__ */ new Map();
9750
+ for (const obligation of obligations$1) {
9751
+ const id$1 = id(obligation).toLowerCase();
9752
+ if (!obligationsById.get(id$1)) obligationsById.set(id$1, obligation);
9753
+ }
9754
+ try {
9755
+ await db.transaction(async (dbTx) => {
9756
+ const obligationRows = obligations$1.map((obligation) => ({
9436
9757
  obligationId: id(obligation),
9437
- asset: collateral.asset.toLowerCase(),
9438
- oracleChainId: obligation.chainId,
9439
- oracleAddress: collateral.oracle.toLowerCase(),
9440
- lltv: collateral.lltv
9758
+ chainId: obligation.chainId,
9759
+ loanToken: obligation.loanToken.toLowerCase(),
9760
+ maturity: obligation.maturity
9441
9761
  }));
9762
+ for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
9763
+ const collateralRows = obligations$1.flatMap((obligation) => {
9764
+ return obligation.collaterals.map((collateral) => ({
9765
+ obligationId: id(obligation),
9766
+ asset: collateral.asset.toLowerCase(),
9767
+ oracleChainId: obligation.chainId,
9768
+ oracleAddress: collateral.oracle.toLowerCase(),
9769
+ lltv: collateral.lltv
9770
+ }));
9771
+ });
9772
+ for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
9442
9773
  });
9443
- for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
9444
- });
9445
- } catch (err) {
9446
- const error = err instanceof Error ? err : new Error(String(err));
9447
- throw new Error("Obligations.create failed. Ensure oracles exist before inserting obligations.", { cause: error });
9774
+ } catch (err) {
9775
+ const error = err instanceof Error ? err : new Error(String(err));
9776
+ throw new Error("Obligations.create failed. Ensure oracles exist before inserting obligations.", { cause: error });
9777
+ }
9448
9778
  }
9449
- } };
9779
+ };
9450
9780
  }
9451
9781
 
9452
9782
  //#endregion
@@ -10031,6 +10361,9 @@ function createDomains(core, chainRegistry) {
10031
10361
  transfers: create$3(core)
10032
10362
  };
10033
10363
  }
10364
+ function createReaders(core) {
10365
+ return { obligations: create$16({ db: core }) };
10366
+ }
10034
10367
  const AUGMENT_CACHE = /* @__PURE__ */ new WeakMap();
10035
10368
  function augmentWithDomains(base, chainRegistry) {
10036
10369
  const cached = AUGMENT_CACHE.get(base)?.get(chainRegistry);
@@ -10042,6 +10375,7 @@ function augmentWithDomains(base, chainRegistry) {
10042
10375
  });
10043
10376
  };
10044
10377
  const dms = createDomains(wrapped, chainRegistry);
10378
+ const readers = createReaders(wrapped);
10045
10379
  Object.defineProperties(wrapped, {
10046
10380
  book: {
10047
10381
  value: dms.book,
@@ -10098,6 +10432,10 @@ function augmentWithDomains(base, chainRegistry) {
10098
10432
  transfers: {
10099
10433
  value: dms.transfers,
10100
10434
  enumerable: true
10435
+ },
10436
+ readers: {
10437
+ value: readers,
10438
+ enumerable: true
10101
10439
  }
10102
10440
  });
10103
10441
  const chainRegistryMap = AUGMENT_CACHE.get(base);
@@ -10737,7 +11075,7 @@ const chains = ({ chains }) => single("chain_ids", `Validates that offer chain i
10737
11075
  });
10738
11076
  const maturity = ({ maturities }) => single("maturity", `Validates that offer maturity is one of: [${maturities.join(", ")}]`, (offer) => {
10739
11077
  const allowedMaturities = maturities.map((m) => from$16(m));
10740
- if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}` };
11078
+ if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be one of (${allowedMaturities.join(", ")}). Got: ${offer.maturity}` };
10741
11079
  });
10742
11080
  const callback = ({ callbacks }) => single("callback", `Validates callbacks: buy empty callback is ${callbacks.includes(Type$1.BuyWithEmptyCallback) ? "allowed" : "not allowed"}; sell empty callback is ${callbacks.includes(Type$1.SellWithEmptyCallback) ? "allowed" : "not allowed"}; non-empty callbacks are rejected`, (offer) => {
10743
11081
  if (!isEmptyCallback(offer)) return { message: "Non-empty callbacks are not supported." };
@@ -10817,7 +11155,7 @@ const morphoRules = (chains$3) => {
10817
11155
  sameMaker(),
10818
11156
  amountMutualExclusivity(),
10819
11157
  chains({ chains: chains$3 }),
10820
- maturity({ maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth] }),
11158
+ maturity({ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek] }),
10821
11159
  callback({
10822
11160
  callbacks: [Type$1.BuyWithEmptyCallback, Type$1.SellWithEmptyCallback],
10823
11161
  allowedAddresses: []