@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.
@@ -12,7 +12,7 @@ import "@opentelemetry/propagator-aws-xray";
12
12
  import "@opentelemetry/resources";
13
13
  import "@opentelemetry/sdk-trace-node";
14
14
  import "@opentelemetry/semantic-conventions";
15
- import { and, asc, eq, gt, gte, inArray, lte, ne, sql } from "drizzle-orm";
15
+ import { and, asc, desc, eq, gt, gte, inArray, lte, ne, sql } from "drizzle-orm";
16
16
  import { anvil, base, mainnet } from "viem/chains";
17
17
  import * as z$1 from "zod";
18
18
  import { StandardMerkleTree } from "@openzeppelin/merkle-tree";
@@ -566,7 +566,7 @@ var utils_exports = /* @__PURE__ */ __exportAll({
566
566
 
567
567
  //#endregion
568
568
  //#region src/indexer/collectors/Admin.ts
569
- function create$20(parameters) {
569
+ function create$21(parameters) {
570
570
  const collector = "admin";
571
571
  const { client, db, options: { maxBatchSize = 25, maxBlockNumber } = {} } = parameters;
572
572
  const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
@@ -806,8 +806,8 @@ const names = [
806
806
  "positions",
807
807
  "prices"
808
808
  ];
809
- function create$19({ name, collect, client, db, options }) {
810
- const admin = create$20({
809
+ function create$20({ name, collect, client, db, options }) {
810
+ const admin = create$21({
811
811
  client,
812
812
  db,
813
813
  options
@@ -973,18 +973,18 @@ const MorphoV2 = parseAbi([
973
973
  "function setFeeSetter(address newFeeSetter)",
974
974
  "function setObligationTradingFee(bytes32 id, uint256 index, uint256 newTradingFee)",
975
975
  "function setOwner(address newOwner)",
976
- "function setTradingFeeRecipient(address recipient)",
976
+ "function setTradingFeeRecipient(address feeRecipient)",
977
977
  "function sharesOf(bytes32 id, address user) view returns (uint256)",
978
978
  "function shuffleSession()",
979
979
  "function supplyCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf)",
980
- "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)",
980
+ "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)",
981
981
  "function totalShares(bytes32 id) view returns (uint256)",
982
982
  "function totalUnits(bytes32 id) view returns (uint256)",
983
983
  "function touchObligation((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation) returns (bytes32)",
984
984
  "function tradingFee(bytes32 id, uint256 timeToMaturity) view returns (uint256)",
985
985
  "function tradingFeeRecipient() view returns (address)",
986
- "function withdraw((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, uint256 obligationUnits, uint256 shares, address onBehalf) returns (uint256, uint256)",
987
- "function withdrawCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf)",
986
+ "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)",
987
+ "function withdrawCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf, address receiver)",
988
988
  "function withdrawable(bytes32 id) view returns (uint256)",
989
989
  "event Constructor(address indexed owner)",
990
990
  "event Consume(address indexed user, bytes32 indexed group, uint256 amount)",
@@ -996,12 +996,12 @@ const MorphoV2 = parseAbi([
996
996
  "event SetFeeSetter(address indexed feeSetter)",
997
997
  "event SetObligationTradingFee(bytes32 indexed id, uint256 indexed index, uint256 newTradingFee)",
998
998
  "event SetOwner(address indexed owner)",
999
- "event SetTradingFeeRecipient(address indexed recipient)",
999
+ "event SetTradingFeeRecipient(address indexed feeRecipient)",
1000
1000
  "event ShuffleSession(address indexed user, bytes32 session)",
1001
1001
  "event SupplyCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf)",
1002
- "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)",
1003
- "event Withdraw(address indexed caller, bytes32 indexed id, uint256 obligationUnits, uint256 shares, address indexed onBehalf)",
1004
- "event WithdrawCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf)"
1002
+ "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)",
1003
+ "event Withdraw(address caller, bytes32 indexed id, uint256 obligationUnits, uint256 shares, address indexed onBehalf, address indexed receiver)",
1004
+ "event WithdrawCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf, address receiver)"
1005
1005
  ]);
1006
1006
 
1007
1007
  //#endregion
@@ -1273,8 +1273,8 @@ const chains$2 = {
1273
1273
  name: "ethereum-virtual-testnet",
1274
1274
  custom: {
1275
1275
  morpho: {
1276
- address: "0x634b095371e4e45feed94c1a45c37798e173ea50",
1277
- blockCreated: 23226700
1276
+ address: "0xc9f3c65996fc46b9500608b2c9a9152c01c540f7",
1277
+ blockCreated: 23226871
1278
1278
  },
1279
1279
  morphoBlue: {
1280
1280
  address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -1420,13 +1420,13 @@ var MissingBlockNumberError = class extends BaseError {
1420
1420
 
1421
1421
  //#endregion
1422
1422
  //#region src/core/ChainRegistry.ts
1423
- var ChainRegistry_exports = /* @__PURE__ */ __exportAll({ create: () => create$18 });
1423
+ var ChainRegistry_exports = /* @__PURE__ */ __exportAll({ create: () => create$19 });
1424
1424
  /**
1425
1425
  * Creates a chain registry from a list of chains.
1426
1426
  * @param chains - Array of chain objects to register.
1427
1427
  * @returns A registry for looking up chains by ID. {@link ChainRegistry}
1428
1428
  */
1429
- function create$18(chains) {
1429
+ function create$19(chains) {
1430
1430
  const byId = /* @__PURE__ */ new Map();
1431
1431
  for (const chain of chains) byId.set(chain.id, chain);
1432
1432
  return {
@@ -2101,7 +2101,8 @@ const OfferSchema = () => {
2101
2101
  callback: z$1.object({
2102
2102
  address: z$1.string().transform(transformAddress),
2103
2103
  data: z$1.string().transform(transformHex)
2104
- })
2104
+ }),
2105
+ receiverIfMakerIsSeller: z$1.string().transform(transformAddress)
2105
2106
  }).refine((data) => data.start < data.expiry, {
2106
2107
  message: "start must be before expiry",
2107
2108
  path: ["start"]
@@ -2117,8 +2118,12 @@ const OfferSchema = () => {
2117
2118
  * @returns The created offer.
2118
2119
  */
2119
2120
  function from$14(input) {
2121
+ const normalizedInput = {
2122
+ ...input,
2123
+ receiverIfMakerIsSeller: input.receiverIfMakerIsSeller ?? input.maker
2124
+ };
2120
2125
  try {
2121
- return OfferSchema().parse(input);
2126
+ return OfferSchema().parse(normalizedInput);
2122
2127
  } catch (error) {
2123
2128
  throw new InvalidOfferError(error);
2124
2129
  }
@@ -2170,6 +2175,7 @@ const serialize = (offer) => ({
2170
2175
  address: offer.callback.address,
2171
2176
  data: offer.callback.data
2172
2177
  },
2178
+ receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller,
2173
2179
  hash: hash(offer)
2174
2180
  });
2175
2181
  /**
@@ -2212,8 +2218,9 @@ function random$1(config) {
2212
2218
  address: zeroAddress,
2213
2219
  data: "0x"
2214
2220
  };
2221
+ const maker = config?.maker ?? address();
2215
2222
  return from$14({
2216
- maker: config?.maker ?? address(),
2223
+ maker,
2217
2224
  assets: assetsScaled,
2218
2225
  obligationUnits: config?.obligationUnits ?? 0n,
2219
2226
  obligationShares: config?.obligationShares ?? 0n,
@@ -2230,7 +2237,8 @@ function random$1(config) {
2230
2237
  ...random$3(),
2231
2238
  lltv
2232
2239
  })).sort((a, b) => a.asset.localeCompare(b.asset)),
2233
- callback: config?.callback ?? emptyCallback
2240
+ callback: config?.callback ?? emptyCallback,
2241
+ receiverIfMakerIsSeller: config?.receiverIfMakerIsSeller ?? maker
2234
2242
  });
2235
2243
  }
2236
2244
  const weightedChoice = (pairs) => {
@@ -2316,6 +2324,10 @@ const types = {
2316
2324
  {
2317
2325
  name: "callback",
2318
2326
  type: "Callback"
2327
+ },
2328
+ {
2329
+ name: "receiverIfMakerIsSeller",
2330
+ type: "address"
2319
2331
  }
2320
2332
  ],
2321
2333
  Collateral: [
@@ -2361,7 +2373,8 @@ function hash(offer) {
2361
2373
  callback: {
2362
2374
  address: offer.callback.address.toLowerCase(),
2363
2375
  data: offer.callback.data
2364
- }
2376
+ },
2377
+ receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase()
2365
2378
  },
2366
2379
  primaryType: "Offer",
2367
2380
  types
@@ -2464,6 +2477,10 @@ const OfferAbi = [
2464
2477
  name: "data",
2465
2478
  type: "bytes"
2466
2479
  }]
2480
+ },
2481
+ {
2482
+ name: "receiverIfMakerIsSeller",
2483
+ type: "address"
2467
2484
  }
2468
2485
  ];
2469
2486
  function encode$1(offer) {
@@ -2482,7 +2499,8 @@ function encode$1(offer) {
2482
2499
  offer.loanToken,
2483
2500
  BigInt(offer.start),
2484
2501
  offer.collaterals,
2485
- offer.callback
2502
+ offer.callback,
2503
+ offer.receiverIfMakerIsSeller
2486
2504
  ]);
2487
2505
  }
2488
2506
  function decode$1(data) {
@@ -2516,7 +2534,8 @@ function decode$1(data) {
2516
2534
  callback: {
2517
2535
  address: decoded[14].address,
2518
2536
  data: decoded[14].data
2519
- }
2537
+ },
2538
+ receiverIfMakerIsSeller: decoded[15]
2520
2539
  });
2521
2540
  }
2522
2541
  /**
@@ -2592,6 +2611,12 @@ const takeEvent = {
2592
2611
  indexed: false,
2593
2612
  internalType: "bool"
2594
2613
  },
2614
+ {
2615
+ name: "sellerReceiver",
2616
+ type: "address",
2617
+ indexed: false,
2618
+ internalType: "address"
2619
+ },
2595
2620
  {
2596
2621
  name: "group",
2597
2622
  type: "bytes32",
@@ -2777,76 +2802,6 @@ function from$12(parameters) {
2777
2802
  };
2778
2803
  }
2779
2804
 
2780
- //#endregion
2781
- //#region src/core/Quote.ts
2782
- var Quote_exports = /* @__PURE__ */ __exportAll({
2783
- InvalidQuoteError: () => InvalidQuoteError,
2784
- QuoteSchema: () => QuoteSchema,
2785
- from: () => from$11,
2786
- fromSnakeCase: () => fromSnakeCase,
2787
- random: () => random
2788
- });
2789
- const QuoteSchema = z$1.object({
2790
- obligationId: z$1.string().transform(transformHex),
2791
- ask: z$1.object({ price: z$1.bigint({ coerce: true }).min(0n).max(maxUint256) }),
2792
- bid: z$1.object({ price: z$1.bigint({ coerce: true }).min(0n).max(maxUint256) })
2793
- });
2794
- /**
2795
- * Creates a quote for a given obligation.
2796
- * @constructor
2797
- * @param parameters - {@link from.Parameters}
2798
- * @returns The created quote. {@link Quote}
2799
- * @throws If the quote is invalid. {@link InvalidQuoteError}
2800
- *
2801
- * @example
2802
- * ```ts
2803
- * const quote = Quote.from({ obligationId: "0x123", ask: { price: 100n }, bid: { price: 100n } });
2804
- * ```
2805
- */
2806
- function from$11(parameters) {
2807
- try {
2808
- const parsedQuote = QuoteSchema.parse(parameters);
2809
- return {
2810
- obligationId: parsedQuote.obligationId,
2811
- ask: parsedQuote.ask,
2812
- bid: parsedQuote.bid
2813
- };
2814
- } catch (error) {
2815
- throw new InvalidQuoteError(error);
2816
- }
2817
- }
2818
- /**
2819
- * Creates a quote from a snake case object.
2820
- * @throws If the quote is invalid. {@link InvalidQuoteError}
2821
- * @param snake - {@link fromSnakeCase.Parameters}
2822
- * @returns The created quote. {@link fromSnakeCase.ReturnType}
2823
- */
2824
- function fromSnakeCase(snake) {
2825
- return from$11(fromSnakeCase$3(snake));
2826
- }
2827
- /**
2828
- * Generates a random quote.
2829
- * @returns A randomly generated quote. {@link random.ReturnType}
2830
- *
2831
- * @example
2832
- * ```ts
2833
- * const quote = Quote.random();
2834
- * ```
2835
- */
2836
- function random() {
2837
- return from$11({
2838
- obligationId: id(random$2()),
2839
- ask: { price: BigInt(int(1e6)) },
2840
- bid: { price: BigInt(int(1e6)) }
2841
- });
2842
- }
2843
- var InvalidQuoteError = class extends BaseError {
2844
- name = "Quote.InvalidQuoteError";
2845
- constructor(error) {
2846
- super("Invalid quote.", { cause: error });
2847
- }
2848
- };
2849
-
2850
2805
  //#endregion
2851
2806
  //#region src/core/Tick.ts
2852
2807
  var Tick_exports = /* @__PURE__ */ __exportAll({
@@ -2926,6 +2881,82 @@ var InvalidPriceError = class extends BaseError {
2926
2881
  }
2927
2882
  };
2928
2883
 
2884
+ //#endregion
2885
+ //#region src/core/Quote.ts
2886
+ var Quote_exports = /* @__PURE__ */ __exportAll({
2887
+ InvalidQuoteError: () => InvalidQuoteError,
2888
+ from: () => from$11,
2889
+ fromSnakeCase: () => fromSnakeCase,
2890
+ random: () => random
2891
+ });
2892
+ const SideInputSchema = z$1.object({ tick: z$1.number().int().min(0).max(TICK_RANGE).nullable() }).strict();
2893
+ const QuoteInputSchema = z$1.object({
2894
+ obligationId: z$1.string().transform(transformHex),
2895
+ ask: SideInputSchema,
2896
+ bid: SideInputSchema
2897
+ }).strict();
2898
+ /**
2899
+ * Creates a quote for a given obligation.
2900
+ * @constructor
2901
+ * @param parameters - {@link from.Parameters}
2902
+ * @returns The created quote. {@link Quote}
2903
+ * @throws If the quote is invalid. {@link InvalidQuoteError}
2904
+ *
2905
+ * @example
2906
+ * ```ts
2907
+ * const quote = Quote.from({ obligationId: "0x123", ask: { tick: 500 }, bid: { tick: 510 } });
2908
+ * ```
2909
+ */
2910
+ function from$11(parameters) {
2911
+ try {
2912
+ const parsedQuote = QuoteInputSchema.parse(parameters);
2913
+ return {
2914
+ obligationId: parsedQuote.obligationId,
2915
+ ask: sideFromTick(parsedQuote.ask),
2916
+ bid: sideFromTick(parsedQuote.bid)
2917
+ };
2918
+ } catch (error) {
2919
+ throw new InvalidQuoteError(error);
2920
+ }
2921
+ }
2922
+ /**
2923
+ * Creates a quote from a snake case object.
2924
+ * @throws If the quote is invalid. {@link InvalidQuoteError}
2925
+ * @param snake - {@link fromSnakeCase.Parameters}
2926
+ * @returns The created quote. {@link fromSnakeCase.ReturnType}
2927
+ */
2928
+ function fromSnakeCase(snake) {
2929
+ return from$11(fromSnakeCase$3(snake));
2930
+ }
2931
+ /**
2932
+ * Generates a random quote.
2933
+ * @returns A randomly generated quote. {@link random.ReturnType}
2934
+ *
2935
+ * @example
2936
+ * ```ts
2937
+ * const quote = Quote.random();
2938
+ * ```
2939
+ */
2940
+ function random() {
2941
+ return from$11({
2942
+ obligationId: id(random$2()),
2943
+ ask: { tick: int(TICK_RANGE + 1) },
2944
+ bid: { tick: int(TICK_RANGE + 1) }
2945
+ });
2946
+ }
2947
+ var InvalidQuoteError = class extends BaseError {
2948
+ name = "Quote.InvalidQuoteError";
2949
+ constructor(error) {
2950
+ super("Invalid quote.", { cause: error });
2951
+ }
2952
+ };
2953
+ function sideFromTick(side) {
2954
+ return {
2955
+ tick: side.tick,
2956
+ price: side.tick === null ? 0n : tickToPrice(side.tick)
2957
+ };
2958
+ }
2959
+
2929
2960
  //#endregion
2930
2961
  //#region src/core/TradingFee.ts
2931
2962
  var TradingFee_exports = /* @__PURE__ */ __exportAll({
@@ -3602,6 +3633,7 @@ const offers = s.table(EnumTableName.OFFERS, {
3602
3633
  buy: boolean("buy").notNull(),
3603
3634
  callbackAddress: varchar("callback_address", { length: 42 }).notNull(),
3604
3635
  callbackData: text("callback_data").notNull(),
3636
+ receiverIfMakerIsSeller: varchar("receiver_if_maker_is_seller", { length: 42 }),
3605
3637
  blockNumber: bigint("block_number", { mode: "number" }).notNull(),
3606
3638
  updatedAt: timestamp("updated_at").defaultNow().notNull()
3607
3639
  }, (table) => [
@@ -4983,7 +5015,7 @@ async function* collectPrices(parameters) {
4983
5015
  //#region src/indexer/collectors/CollectorBuilder.ts
4984
5016
  function createBuilder(parameters) {
4985
5017
  const { client, db, gatekeeper, options: { maxBlockNumber, blockWindow, interval } = {} } = parameters;
4986
- const createCollector = (name, collect) => create$19({
5018
+ const createCollector = (name, collect) => create$20({
4987
5019
  name,
4988
5020
  collect,
4989
5021
  client,
@@ -5075,7 +5107,7 @@ const from$7 = (parameters) => {
5075
5107
  //#endregion
5076
5108
  //#region src/indexer/Indexer.ts
5077
5109
  var Indexer_exports = /* @__PURE__ */ __exportAll({
5078
- create: () => create$17,
5110
+ create: () => create$18,
5079
5111
  from: () => from$6
5080
5112
  });
5081
5113
  function from$6(config) {
@@ -5091,7 +5123,7 @@ function from$6(config) {
5091
5123
  retryAttempts,
5092
5124
  retryDelayMs
5093
5125
  });
5094
- return create$17({
5126
+ return create$18({
5095
5127
  client,
5096
5128
  collectors: [
5097
5129
  offersCollector,
@@ -5101,7 +5133,7 @@ function from$6(config) {
5101
5133
  ]
5102
5134
  });
5103
5135
  }
5104
- function create$17(params) {
5136
+ function create$18(params) {
5105
5137
  const { collectors, client } = params;
5106
5138
  const indexerId = `${client.chain.id.toString()}.indexer`;
5107
5139
  const tracer = getTracer(`router.${indexerId}`);
@@ -5130,12 +5162,12 @@ function create$17(params) {
5130
5162
 
5131
5163
  //#endregion
5132
5164
  //#region src/api/Health.ts
5133
- var Health_exports = /* @__PURE__ */ __exportAll({ create: () => create$16 });
5165
+ var Health_exports = /* @__PURE__ */ __exportAll({ create: () => create$17 });
5134
5166
  const DEFAULT_MAX_ALLOWED_LAG = 5;
5135
5167
  /**
5136
5168
  * Create a health service that exposes collector and chain block numbers.
5137
5169
  */
5138
- function create$16(parameters) {
5170
+ function create$17(parameters) {
5139
5171
  const { db, maxAllowedLag = DEFAULT_MAX_ALLOWED_LAG, healthClients, chainRegistry } = parameters;
5140
5172
  const loadSnapshot = async () => {
5141
5173
  const [collectorRows, chainRows, remoteBlockByChainId] = await Promise.all([
@@ -5299,6 +5331,7 @@ var ObligationResponse_exports = /* @__PURE__ */ __exportAll({ from: () => from$
5299
5331
  * Creates an `ObligationResponse` from a `Obligation`.
5300
5332
  * @constructor
5301
5333
  * @param obligation - {@link Obligation}
5334
+ * @param quote - {@link Quote}
5302
5335
  * @returns The created `ObligationResponse`. {@link ObligationResponse}
5303
5336
  */
5304
5337
  function from$4(obligation, quote) {
@@ -5312,8 +5345,14 @@ function from$4(obligation, quote) {
5312
5345
  oracle: c.oracle
5313
5346
  })),
5314
5347
  maturity: obligation.maturity,
5315
- ask: { price: quote.ask.price.toString() },
5316
- bid: { price: quote.bid.price.toString() }
5348
+ ask: {
5349
+ tick: quote.ask.tick,
5350
+ price: quote.ask.price.toString()
5351
+ },
5352
+ bid: {
5353
+ tick: quote.bid.tick,
5354
+ price: quote.bid.price.toString()
5355
+ }
5317
5356
  };
5318
5357
  }
5319
5358
 
@@ -5361,7 +5400,8 @@ function from$3(input) {
5361
5400
  group: input.group,
5362
5401
  session: input.session,
5363
5402
  callback: input.callback.address,
5364
- callback_data: input.callback.data
5403
+ callback_data: input.callback.data,
5404
+ receiver_if_maker_is_seller: input.receiverIfMakerIsSeller
5365
5405
  },
5366
5406
  offer_hash: input.hash,
5367
5407
  obligation_id: id({
@@ -5428,7 +5468,7 @@ var InternalServerError = class extends APIError {
5428
5468
  super(STATUS_CODE.INTERNAL_SERVER_ERROR, message, "INTERNAL_SERVER_ERROR");
5429
5469
  }
5430
5470
  };
5431
- var BadRequestError = class extends APIError {
5471
+ var BadRequestError$1 = class extends APIError {
5432
5472
  constructor(message = "Invalid JSON format", details) {
5433
5473
  super(STATUS_CODE.BAD_REQUEST, message, "BAD_REQUEST", details);
5434
5474
  }
@@ -5450,7 +5490,7 @@ function success(args) {
5450
5490
  */
5451
5491
  function failure(err) {
5452
5492
  if (err instanceof APIError) return handleAPIError(err);
5453
- if (err instanceof SyntaxError) return handleAPIError(new BadRequestError(err.message));
5493
+ if (err instanceof SyntaxError) return handleAPIError(new BadRequestError$1(err.message));
5454
5494
  if (err instanceof z$1.ZodError) return handleAPIError(handleZodError(err));
5455
5495
  return handleAPIError(new InternalServerError());
5456
5496
  }
@@ -5500,7 +5540,7 @@ function __decorate(decorators, target, key, desc) {
5500
5540
  //#region src/api/Schema/openapi.ts
5501
5541
  const timestampExample = "2024-01-01T12:00:00.000Z";
5502
5542
  const offerCursorExample = "eyJvZmZzZXQiOjEwMH0";
5503
- const obligationCursorExample = "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc";
5543
+ const obligationCursorExample = "eyJzb3J0IjpbImlkIl0sImlkIjoiMHgxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAiLCJhc2siOiIwIiwiYmlkIjoiMCIsIm1hdHVyaXR5IjoxNzYxOTIyODAwfQ";
5504
5544
  const offerExample = {
5505
5545
  offer: {
5506
5546
  obligation: {
@@ -5523,7 +5563,8 @@ const offerExample = {
5523
5563
  group: "0x000000000000000000000000000000000000000000000000000000000008b8f4",
5524
5564
  session: "0x0000000000000000000000000000000000000000000000000000000000000000",
5525
5565
  callback: "0x0000000000000000000000000000000000000000",
5526
- callback_data: "0x"
5566
+ callback_data: "0x",
5567
+ receiver_if_maker_is_seller: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
5527
5568
  },
5528
5569
  offer_hash: "0xac4bd8318ec914f89f8af913f162230575b0ac0696a19256bc12138c5cfe1427",
5529
5570
  obligation_id: "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc",
@@ -5577,7 +5618,8 @@ const validateOfferExample = {
5577
5618
  callback: {
5578
5619
  address: "0x0000000000000000000000000000000000000000",
5579
5620
  data: "0x"
5580
- }
5621
+ },
5622
+ receiver_if_maker_is_seller: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
5581
5623
  };
5582
5624
  const routerStatusExample = {
5583
5625
  status: "live",
@@ -5648,11 +5690,23 @@ __decorate([ApiProperty({
5648
5690
  example: validateOfferExample.callback.data
5649
5691
  })], ValidateCallbackRequest.prototype, "data", void 0);
5650
5692
  var AskResponse = class {};
5693
+ __decorate([ApiProperty({
5694
+ type: "number",
5695
+ nullable: true,
5696
+ example: 500,
5697
+ description: "Best ask tick. Null when there is no active ask quote."
5698
+ })], AskResponse.prototype, "tick", void 0);
5651
5699
  __decorate([ApiProperty({
5652
5700
  type: "string",
5653
5701
  example: "1000000000000000000"
5654
5702
  })], AskResponse.prototype, "price", void 0);
5655
5703
  var BidResponse = class {};
5704
+ __decorate([ApiProperty({
5705
+ type: "number",
5706
+ nullable: true,
5707
+ example: 500,
5708
+ description: "Best bid tick. Null when there is no active bid quote."
5709
+ })], BidResponse.prototype, "tick", void 0);
5656
5710
  __decorate([ApiProperty({
5657
5711
  type: "string",
5658
5712
  example: "1000000000000000000"
@@ -5725,6 +5779,10 @@ __decorate([ApiProperty({
5725
5779
  type: "string",
5726
5780
  example: offerExample.offer.callback_data
5727
5781
  })], OfferDataResponse.prototype, "callback_data", void 0);
5782
+ __decorate([ApiProperty({
5783
+ type: "string",
5784
+ example: offerExample.offer.receiver_if_maker_is_seller
5785
+ })], OfferDataResponse.prototype, "receiver_if_maker_is_seller", void 0);
5728
5786
  var OfferListItemResponse = class {};
5729
5787
  __decorate([ApiProperty({
5730
5788
  type: () => OfferDataResponse,
@@ -5994,6 +6052,10 @@ __decorate([ApiProperty({
5994
6052
  type: () => ValidateCallbackRequest,
5995
6053
  example: validateOfferExample.callback
5996
6054
  })], ValidateOfferRequest.prototype, "callback", void 0);
6055
+ __decorate([ApiProperty({
6056
+ type: "string",
6057
+ example: validateOfferExample.receiver_if_maker_is_seller
6058
+ })], ValidateOfferRequest.prototype, "receiver_if_maker_is_seller", void 0);
5997
6059
  var ValidateOffersRequest = class {};
5998
6060
  __decorate([ApiProperty({
5999
6061
  type: () => [ValidateOfferRequest],
@@ -6546,13 +6608,13 @@ __decorate([
6546
6608
  methods: ["get"],
6547
6609
  path: "/v1/obligations",
6548
6610
  summary: "List all obligations",
6549
- description: "Returns a list of obligations with their current best ask and bid. Obligations are sorted by their id in ascending order by default."
6611
+ 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."
6550
6612
  }),
6551
6613
  ApiQuery({
6552
6614
  name: "cursor",
6553
6615
  type: "string",
6554
6616
  example: obligationCursorExample,
6555
- description: "Obligation id cursor for pagination."
6617
+ description: "Pagination cursor in base64url-encoded format."
6556
6618
  }),
6557
6619
  ApiQuery({
6558
6620
  name: "limit",
@@ -6596,6 +6658,15 @@ __decorate([
6596
6658
  style: "form",
6597
6659
  explode: false
6598
6660
  }),
6661
+ ApiQuery({
6662
+ name: "sort",
6663
+ type: "string",
6664
+ required: false,
6665
+ example: "-ask,bid,maturity",
6666
+ description: "Sort order as comma-separated fields (`id`, `ask`, `bid`, `maturity`). Prefix with `-` for descending order. Max 3 fields.",
6667
+ style: "form",
6668
+ explode: false
6669
+ }),
6599
6670
  ApiResponse({
6600
6671
  status: 200,
6601
6672
  description: "Success",
@@ -6731,11 +6802,13 @@ function from$2(position) {
6731
6802
  //#endregion
6732
6803
  //#region src/api/Schema/requests.ts
6733
6804
  const MAX_LIMIT = 100;
6734
- const DEFAULT_LIMIT$4 = 20;
6805
+ const DEFAULT_LIMIT$5 = 20;
6806
+ const MAX_OBLIGATION_SORT_FIELDS = 3;
6735
6807
  const CONFIG_RULES_MAX_LIMIT = 1e3;
6736
6808
  const CONFIG_RULES_DEFAULT_LIMIT = 100;
6737
6809
  const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
6738
6810
  const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
6811
+ const OBLIGATION_SORT_ENTRY_REGEX = /^-?(id|ask|bid|maturity)$/;
6739
6812
  /** Validate cursor is a valid base64url-encoded JSON object.
6740
6813
  * Domain layer handles semantic validation of cursor fields. */
6741
6814
  function isValidBase64urlJson(val) {
@@ -6767,8 +6840,8 @@ const PaginationQueryParams = z$1.object({
6767
6840
  description: "Pagination cursor in base64url-encoded format",
6768
6841
  example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
6769
6842
  }),
6770
- limit: z$1.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$1.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
6771
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
6843
+ limit: z$1.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$1.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$5).meta({
6844
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$5}`,
6772
6845
  example: 10
6773
6846
  })
6774
6847
  });
@@ -6865,9 +6938,12 @@ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend
6865
6938
  });
6866
6939
  const GetObligationsQueryParams = z$1.object({
6867
6940
  ...PaginationQueryParams.shape,
6868
- cursor: z$1.string().optional().meta({
6869
- description: "Obligation id cursor",
6870
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
6941
+ cursor: z$1.string().optional().refine((val) => {
6942
+ if (!val) return true;
6943
+ return isValidBase64urlJson(val);
6944
+ }, { message: "Invalid cursor format. Must be a valid base64url-encoded cursor object" }).meta({
6945
+ description: "Pagination cursor in base64url-encoded format.",
6946
+ example: "eyJzb3J0IjpbImlkIl0sImlkIjoiMHgxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAiLCJhc2siOiIwIiwiYmlkIjoiMCIsIm1hdHVyaXR5IjoxNzYxOTIyODAwfQ"
6871
6947
  }),
6872
6948
  chains: csvArray(z$1.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
6873
6949
  description: "Filter by chain IDs (comma-separated).",
@@ -6884,6 +6960,24 @@ const GetObligationsQueryParams = z$1.object({
6884
6960
  maturities: csvArray(z$1.string().regex(/^[1-9]\d*$/, { message: "Maturity must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
6885
6961
  description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
6886
6962
  example: "1761922800,1764524800"
6963
+ }),
6964
+ sort: csvArray(z$1.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) => {
6965
+ if (!entries) return;
6966
+ const seen = /* @__PURE__ */ new Set();
6967
+ for (const entry of entries) {
6968
+ const field = entry.startsWith("-") ? entry.slice(1) : entry;
6969
+ if (seen.has(field)) {
6970
+ ctx.addIssue({
6971
+ code: "custom",
6972
+ message: `Duplicate sort field: ${field}`
6973
+ });
6974
+ return;
6975
+ }
6976
+ seen.add(field);
6977
+ }
6978
+ }).meta({
6979
+ description: "Sort order as comma-separated fields. Prefix a field with '-' for descending order. Max 3 fields.",
6980
+ example: "-ask,bid,maturity"
6887
6981
  })
6888
6982
  });
6889
6983
  const GetObligationParams = z$1.object({ obligation_id: z$1.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({
@@ -6907,8 +7001,8 @@ const BookPaginationQueryParams = z$1.object({
6907
7001
  description: "Pagination cursor in base64url-encoded format for book levels",
6908
7002
  example: "eyJzaWRlIjoiYnV5IiwibGFzdFJhdGUiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwib2ZmZXJzQ3Vyc29yIjpudWxsfQ"
6909
7003
  }),
6910
- limit: z$1.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$1.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
6911
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
7004
+ limit: z$1.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$1.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$5).meta({
7005
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$5}`,
6912
7006
  example: 10
6913
7007
  })
6914
7008
  });
@@ -7085,7 +7179,7 @@ async function getConfigContracts(query, chainRegistry) {
7085
7179
  }
7086
7180
  function parseCursor$1(cursor) {
7087
7181
  const [chain, address] = cursor.split(":", 2);
7088
- if (!chain || !address) throw new BadRequestError("Cursor must be in the format chain_id:0x...");
7182
+ if (!chain || !address) throw new BadRequestError$1("Cursor must be in the format chain_id:0x...");
7089
7183
  return {
7090
7184
  chain_id: Number.parseInt(chain, 10),
7091
7185
  address: address.toLowerCase()
@@ -7211,19 +7305,19 @@ const oracles = {
7211
7305
  const configs = {
7212
7306
  ethereum: {
7213
7307
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7214
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7308
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7215
7309
  },
7216
7310
  base: {
7217
7311
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7218
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7312
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7219
7313
  },
7220
7314
  "ethereum-virtual-testnet": {
7221
7315
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7222
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7316
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7223
7317
  },
7224
7318
  anvil: {
7225
7319
  callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
7226
- maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
7320
+ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
7227
7321
  }
7228
7322
  };
7229
7323
 
@@ -7338,8 +7432,8 @@ async function getConfigRules(query, chains) {
7338
7432
  } catch (err) {
7339
7433
  return failure(err);
7340
7434
  }
7341
- if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError("Cursor type must match requested rule types"));
7342
- if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError("Cursor chain_id must match requested chains"));
7435
+ if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError$1("Cursor type must match requested rule types"));
7436
+ if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError$1("Cursor chain_id must match requested chains"));
7343
7437
  const startIndex = cursorRule ? findStartIndex(filteredRules, cursorRule) : 0;
7344
7438
  const page = filteredRules.slice(startIndex, startIndex + limit);
7345
7439
  const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor(page.at(-1)) : null;
@@ -7359,15 +7453,15 @@ function formatCursor(rule) {
7359
7453
  }
7360
7454
  function parseCursor(cursor) {
7361
7455
  const [type, chain, ...rest] = cursor.split(":");
7362
- if (!type || !chain || rest.length === 0) throw new BadRequestError("Cursor must be in the format type:chain_id:<value>");
7363
- if (!isConfigRuleType(type)) throw new BadRequestError("Cursor has an invalid rule type");
7456
+ if (!type || !chain || rest.length === 0) throw new BadRequestError$1("Cursor must be in the format type:chain_id:<value>");
7457
+ if (!isConfigRuleType(type)) throw new BadRequestError$1("Cursor has an invalid rule type");
7364
7458
  const chain_id = Number.parseInt(chain, 10);
7365
- if (!Number.isFinite(chain_id)) throw new BadRequestError("Cursor has an invalid chain_id");
7459
+ if (!Number.isFinite(chain_id)) throw new BadRequestError$1("Cursor has an invalid chain_id");
7366
7460
  if (type === "maturity") {
7367
7461
  const timestampValue = Number.parseInt(rest[0] ?? "", 10);
7368
7462
  const nameValue = rest.slice(1).join(":");
7369
- if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError("Cursor must be in the format maturity:chain_id:timestamp:name");
7370
- if (!isMaturityType(nameValue)) throw new BadRequestError("Cursor has an invalid maturity name");
7463
+ if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError$1("Cursor must be in the format maturity:chain_id:timestamp:name");
7464
+ if (!isMaturityType(nameValue)) throw new BadRequestError$1("Cursor has an invalid maturity name");
7371
7465
  return {
7372
7466
  type,
7373
7467
  chain_id,
@@ -7378,8 +7472,8 @@ function parseCursor(cursor) {
7378
7472
  if (type === "callback") {
7379
7473
  const callbackTypeValue = rest[0] ?? "";
7380
7474
  const addressValue = rest.slice(1).join(":");
7381
- if (!callbackTypeValue || !addressValue) throw new BadRequestError("Cursor must be in the format callback:chain_id:callback_type:address");
7382
- if (!isCallbackType(callbackTypeValue)) throw new BadRequestError("Cursor has an invalid callback type");
7475
+ if (!callbackTypeValue || !addressValue) throw new BadRequestError$1("Cursor must be in the format callback:chain_id:callback_type:address");
7476
+ if (!isCallbackType(callbackTypeValue)) throw new BadRequestError$1("Cursor has an invalid callback type");
7383
7477
  return {
7384
7478
  type,
7385
7479
  chain_id,
@@ -7389,14 +7483,14 @@ function parseCursor(cursor) {
7389
7483
  }
7390
7484
  if (type === "loan_token" || type === "collateral_token" || type === "oracle") {
7391
7485
  const addressValue = rest.join(":");
7392
- if (!addressValue) throw new BadRequestError(`Cursor must be in the format ${type}:chain_id:address`);
7486
+ if (!addressValue) throw new BadRequestError$1(`Cursor must be in the format ${type}:chain_id:address`);
7393
7487
  return {
7394
7488
  type,
7395
7489
  chain_id,
7396
7490
  address: parseAddress(addressValue, "Cursor address")
7397
7491
  };
7398
7492
  }
7399
- throw new BadRequestError("Cursor has an invalid rule type");
7493
+ throw new BadRequestError$1("Cursor has an invalid rule type");
7400
7494
  }
7401
7495
  function findStartIndex(rules, cursor) {
7402
7496
  let low = 0;
@@ -7410,7 +7504,7 @@ function findStartIndex(rules, cursor) {
7410
7504
  return low;
7411
7505
  }
7412
7506
  function parseAddress(address, label) {
7413
- if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError(`${label} must be a valid 20-byte address`);
7507
+ if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError$1(`${label} must be a valid 20-byte address`);
7414
7508
  return address.toLowerCase();
7415
7509
  }
7416
7510
  function isConfigRuleType(value) {
@@ -7423,7 +7517,7 @@ function parseMaturity(value) {
7423
7517
  try {
7424
7518
  return from$16(value);
7425
7519
  } catch (err) {
7426
- throw new BadRequestError(err instanceof Error ? err.message : "Invalid maturity timestamp");
7520
+ throw new BadRequestError$1(err instanceof Error ? err.message : "Invalid maturity timestamp");
7427
7521
  }
7428
7522
  }
7429
7523
  function isCallbackType(value) {
@@ -7519,7 +7613,7 @@ async function getHealth(query, db, chainRegistry) {
7519
7613
  try {
7520
7614
  const parsed = safeParse("get_health", query);
7521
7615
  if (!parsed.success) return failure(parsed.error);
7522
- const snapshot = await create$16({
7616
+ const snapshot = await create$17({
7523
7617
  db,
7524
7618
  chainRegistry
7525
7619
  }).getSnapshot();
@@ -7548,7 +7642,7 @@ async function getHealthChains(query, db, healthClients, chainRegistry) {
7548
7642
  try {
7549
7643
  const parsed = safeParse("get_health_chains", query);
7550
7644
  if (!parsed.success) return failure(parsed.error);
7551
- const snapshot = await create$16({
7645
+ const snapshot = await create$17({
7552
7646
  db,
7553
7647
  healthClients,
7554
7648
  chainRegistry
@@ -7580,7 +7674,7 @@ async function getHealthCollectors(query, db, chainRegistry) {
7580
7674
  try {
7581
7675
  const parsed = safeParse("get_health_collectors", query);
7582
7676
  if (!parsed.success) return failure(parsed.error);
7583
- const snapshot = await create$16({
7677
+ const snapshot = await create$17({
7584
7678
  db,
7585
7679
  chainRegistry
7586
7680
  }).getSnapshot();
@@ -7609,39 +7703,274 @@ async function getHealthCollectors(query, db, chainRegistry) {
7609
7703
  }
7610
7704
  }
7611
7705
 
7706
+ //#endregion
7707
+ //#region src/database/readers/ObligationsListing.ts
7708
+ const SORT_FIELDS = [
7709
+ "id",
7710
+ "ask",
7711
+ "bid",
7712
+ "maturity"
7713
+ ];
7714
+ const CURSOR_ID_REGEX = /^0x[a-f0-9]{64}$/i;
7715
+ const INT32_MIN = -2147483648;
7716
+ const INT32_MAX = 2147483647;
7717
+ const INT32_MAX_BIGINT = BigInt(INT32_MAX);
7718
+ const MAX_CURSOR_SORT_FIELDS = 4;
7719
+ const DEFAULT_LIMIT$4 = 20;
7720
+ var BadRequestError = class extends Error {
7721
+ constructor(message) {
7722
+ super(message);
7723
+ this.name = "ObligationsListingBadRequestError";
7724
+ }
7725
+ };
7726
+ /**
7727
+ * Creates the obligations listing reader facade.
7728
+ * @param parameters - Reader dependencies.
7729
+ * @returns Obligations listing reader.
7730
+ */
7731
+ function create$16(parameters) {
7732
+ const { db } = parameters;
7733
+ return { list: async (queryParameters) => {
7734
+ const { ids, chainId: chainIds, loanToken: loanTokens, collateralToken: collateralTokens, maturity: maturities, sort: sortTokens, cursor: encodedCursor, limit: requestedLimit } = queryParameters ?? {};
7735
+ const limit = requestedLimit ?? DEFAULT_LIMIT$4;
7736
+ if (!Number.isInteger(limit) || limit <= 0) throw new BadRequestError("Limit must be a positive integer");
7737
+ const cursorPayload = encodedCursor ? decodeCursorPayload(encodedCursor) : void 0;
7738
+ const requestedSort = normalizeSort(sortTokens);
7739
+ const cursorSort = cursorPayload ? normalizeSort(cursorPayload.sort) : void 0;
7740
+ if (cursorSort !== void 0 && sortTokens !== void 0 && !hasSameSort(requestedSort, cursorSort)) throw new BadRequestError("Cursor sort does not match requested sort");
7741
+ const sort = sortTokens !== void 0 ? requestedSort : cursorSort ?? requestedSort;
7742
+ const cursorValues = cursorPayload ? cursorValuesFromPayload(cursorPayload) : void 0;
7743
+ const now$3 = now();
7744
+ const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? sql`(${sql.join(loanTokens.map((token) => sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), sql` OR `)})` : void 0;
7745
+ const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? sql`EXISTS (
7746
+ SELECT 1 FROM ${obligationCollateralsV2} oc
7747
+ WHERE oc.obligation_id = ${obligations.obligationId}
7748
+ AND (${sql.join(collateralTokens.map((token) => sql`LOWER(oc.asset) = ${token.toLowerCase()}`), sql` OR `)})
7749
+ )` : void 0;
7750
+ const bestAskTick = db.select({ askTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId, obligations.obligationId), eq(offers.buy, false), gte(offers.expiry, now$3), gte(offers.maturity, now$3), lte(offers.start, now$3), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(desc(offers.tick)).limit(1).as("best_ask_tick");
7751
+ const bestBidTick = db.select({ bidTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId, obligations.obligationId), eq(offers.buy, true), gte(offers.expiry, now$3), gte(offers.maturity, now$3), lte(offers.start, now$3), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(asc(offers.tick)).limit(1).as("best_bid_tick");
7752
+ const obligationsWithQuotes = db.select({
7753
+ obligationId: obligations.obligationId,
7754
+ chainId: obligations.chainId,
7755
+ loanToken: obligations.loanToken,
7756
+ collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
7757
+ maturity: obligations.maturity,
7758
+ askTick: sql`MAX(${bestAskTick.askTick})`.as("ask_tick"),
7759
+ bidTick: sql`MAX(${bestBidTick.bidTick})`.as("bid_tick"),
7760
+ ask: sql`COALESCE(MAX(${bestAskTick.askTick}) + 1, 0)`.as("ask"),
7761
+ bid: sql`COALESCE(MAX(${bestBidTick.bidTick}) + 1, 0)`.as("bid")
7762
+ }).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
7763
+ AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).leftJoinLateral(bestAskTick, sql`true`).leftJoinLateral(bestBidTick, sql`true`).groupBy(obligations.obligationId).where(and(ids !== void 0 && ids.length > 0 ? inArray(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? inArray(obligations.maturity, maturities) : gte(obligations.maturity, now$3), collateralFilter)).as("obligations_with_quotes");
7764
+ const sortColumns = {
7765
+ id: obligationsWithQuotes.obligationId,
7766
+ ask: obligationsWithQuotes.ask,
7767
+ bid: obligationsWithQuotes.bid,
7768
+ maturity: obligationsWithQuotes.maturity
7769
+ };
7770
+ const rows = await db.select({
7771
+ obligationId: obligationsWithQuotes.obligationId,
7772
+ chainId: obligationsWithQuotes.chainId,
7773
+ loanToken: obligationsWithQuotes.loanToken,
7774
+ collaterals: obligationsWithQuotes.collaterals,
7775
+ maturity: obligationsWithQuotes.maturity,
7776
+ askTick: obligationsWithQuotes.askTick,
7777
+ bidTick: obligationsWithQuotes.bidTick,
7778
+ ask: obligationsWithQuotes.ask,
7779
+ bid: obligationsWithQuotes.bid
7780
+ }).from(obligationsWithQuotes).where(buildCursorFilter(sortColumns, sort, cursorValues)).orderBy(...buildOrderBy(sortColumns, sort)).limit(limit + 1);
7781
+ const hasMore = rows.length > limit;
7782
+ const listedRows = (hasMore ? rows.slice(0, limit) : rows).map((row) => {
7783
+ return {
7784
+ obligation: from$15({
7785
+ chainId: row.chainId,
7786
+ loanToken: row.loanToken,
7787
+ collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$17({
7788
+ asset: collateral.asset,
7789
+ oracle: collateral.oracle,
7790
+ lltv: from$18(BigInt(collateral.lltv))
7791
+ })),
7792
+ maturity: row.maturity
7793
+ }),
7794
+ quote: from$11({
7795
+ obligationId: row.obligationId,
7796
+ ask: { tick: row.askTick },
7797
+ bid: { tick: row.bidTick }
7798
+ }),
7799
+ cursorValues: {
7800
+ id: row.obligationId,
7801
+ ask: toBigInt(row.ask),
7802
+ bid: toBigInt(row.bid),
7803
+ maturity: row.maturity
7804
+ }
7805
+ };
7806
+ });
7807
+ const nextCursor = hasMore && listedRows.length > 0 ? encodeCursorPayload({
7808
+ sort: sortToTokens(sort),
7809
+ id: listedRows[listedRows.length - 1].cursorValues.id,
7810
+ ask: listedRows[listedRows.length - 1].cursorValues.ask.toString(),
7811
+ bid: listedRows[listedRows.length - 1].cursorValues.bid.toString(),
7812
+ maturity: listedRows[listedRows.length - 1].cursorValues.maturity
7813
+ }) : null;
7814
+ return {
7815
+ obligations: listedRows.map((row) => ({
7816
+ obligation: row.obligation,
7817
+ quote: row.quote
7818
+ })),
7819
+ nextCursor
7820
+ };
7821
+ } };
7822
+ }
7823
+ function isSortField(value) {
7824
+ return SORT_FIELDS.includes(value);
7825
+ }
7826
+ function parseSortToken(token) {
7827
+ const direction = token.startsWith("-") ? "desc" : "asc";
7828
+ const rawField = token.startsWith("-") ? token.slice(1) : token;
7829
+ if (!isSortField(rawField)) throw new BadRequestError(`Invalid sort field: ${rawField}`);
7830
+ return {
7831
+ field: rawField,
7832
+ direction
7833
+ };
7834
+ }
7835
+ function normalizeSort(sortTokens) {
7836
+ const parsed = sortTokens?.length ? sortTokens.map(parseSortToken) : [{
7837
+ field: "id",
7838
+ direction: "asc"
7839
+ }];
7840
+ return parsed.some((entry) => entry.field === "id") ? parsed : [...parsed, {
7841
+ field: "id",
7842
+ direction: "asc"
7843
+ }];
7844
+ }
7845
+ function sortToTokens(sortEntries) {
7846
+ return sortEntries.map((entry) => entry.direction === "desc" ? `-${entry.field}` : entry.field);
7847
+ }
7848
+ function hasSameSort(left, right) {
7849
+ if (left.length !== right.length) return false;
7850
+ return left.every((sortEntry, index) => sortEntry.field === right[index]?.field && sortEntry.direction === right[index]?.direction);
7851
+ }
7852
+ function decodeCursorPayload(cursor) {
7853
+ let decoded;
7854
+ try {
7855
+ decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
7856
+ } catch {
7857
+ throw new BadRequestError("Invalid cursor format");
7858
+ }
7859
+ if (decoded === null || typeof decoded !== "object") throw new BadRequestError("Invalid cursor payload");
7860
+ const payload = decoded;
7861
+ const sortTokens = parseCursorSortTokens(payload.sort);
7862
+ if (typeof payload.id !== "string" || !CURSOR_ID_REGEX.test(payload.id)) throw new BadRequestError("Invalid cursor obligation id");
7863
+ const ask = parseCursorNonNegativeInt32(payload.ask, "ask");
7864
+ const bid = parseCursorNonNegativeInt32(payload.bid, "bid");
7865
+ if (!isInt32(payload.maturity)) throw new BadRequestError("Invalid cursor maturity value");
7866
+ return {
7867
+ sort: sortTokens,
7868
+ id: payload.id,
7869
+ ask,
7870
+ bid,
7871
+ maturity: payload.maturity
7872
+ };
7873
+ }
7874
+ function parseCursorSortTokens(value) {
7875
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_CURSOR_SORT_FIELDS) throw new BadRequestError("Invalid cursor sort");
7876
+ const sortEntries = value.map((token) => {
7877
+ if (typeof token !== "string") throw new BadRequestError("Invalid cursor sort");
7878
+ try {
7879
+ return parseSortToken(token);
7880
+ } catch {
7881
+ throw new BadRequestError("Invalid cursor sort");
7882
+ }
7883
+ });
7884
+ if (new Set(sortEntries.map((entry) => entry.field)).size !== sortEntries.length) throw new BadRequestError("Invalid cursor sort");
7885
+ return sortToTokens(sortEntries);
7886
+ }
7887
+ function parseCursorNonNegativeInt32(value, field) {
7888
+ if (typeof value !== "string" || !/^\d+$/.test(value)) throw new BadRequestError(`Invalid cursor ${field} value`);
7889
+ if (BigInt(value) > INT32_MAX_BIGINT) throw new BadRequestError(`Invalid cursor ${field} value`);
7890
+ return value;
7891
+ }
7892
+ function isInt32(value) {
7893
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= INT32_MIN && value <= INT32_MAX;
7894
+ }
7895
+ function encodeCursorPayload(payload) {
7896
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
7897
+ }
7898
+ function cursorValuesFromPayload(payload) {
7899
+ return {
7900
+ id: payload.id,
7901
+ ask: BigInt(payload.ask),
7902
+ bid: BigInt(payload.bid),
7903
+ maturity: payload.maturity
7904
+ };
7905
+ }
7906
+ function cursorComparisonValue(cursorValues, field) {
7907
+ switch (field) {
7908
+ case "id": return cursorValues.id;
7909
+ case "maturity": return cursorValues.maturity;
7910
+ case "ask": return cursorValues.ask.toString();
7911
+ case "bid": return cursorValues.bid.toString();
7912
+ }
7913
+ }
7914
+ function buildCursorFilter(columns, sortEntries, cursorValues) {
7915
+ if (cursorValues === void 0) return void 0;
7916
+ const comparisons = sortEntries.map((sortEntry, index) => {
7917
+ const equals = sortEntries.slice(0, index).map((previous) => {
7918
+ return sql`${columns[previous.field]} = ${cursorComparisonValue(cursorValues, previous.field)}`;
7919
+ });
7920
+ const comparison = sortEntry.direction === "asc" ? sql`${columns[sortEntry.field]} > ${cursorComparisonValue(cursorValues, sortEntry.field)}` : sql`${columns[sortEntry.field]} < ${cursorComparisonValue(cursorValues, sortEntry.field)}`;
7921
+ return equals.length > 0 ? sql`(${sql.join([...equals, comparison], sql` AND `)})` : sql`(${comparison})`;
7922
+ });
7923
+ return comparisons.length > 0 ? sql`(${sql.join(comparisons, sql` OR `)})` : void 0;
7924
+ }
7925
+ function buildOrderBy(columns, sortEntries) {
7926
+ return sortEntries.map((sortEntry) => sortEntry.direction === "asc" ? asc(columns[sortEntry.field]) : desc(columns[sortEntry.field]));
7927
+ }
7928
+ function toBigInt(value) {
7929
+ if (typeof value === "bigint") return value;
7930
+ if (typeof value === "number") return BigInt(value);
7931
+ return BigInt(value.split(".")[0] ?? "0");
7932
+ }
7933
+
7612
7934
  //#endregion
7613
7935
  //#region src/api/Controllers/getObligation.ts
7936
+ function toPayloadError$1(err) {
7937
+ if (err instanceof BadRequestError) return new BadRequestError$1(err.message);
7938
+ return err;
7939
+ }
7614
7940
  async function getObligation(params, db) {
7615
7941
  const logger = getLogger();
7616
7942
  const result = safeParse("get_obligation", params, (issue) => issue.message);
7617
7943
  if (!result.success) return failure(result.error);
7618
7944
  const query = result.data;
7619
7945
  try {
7620
- const { obligations } = await db.offers.getObligations({ ids: [query.obligation_id] });
7621
- if (obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
7622
- const obligation = obligations[0];
7623
- const [quote] = await db.offers.getQuotes({ obligationIds: [id(obligation)] });
7946
+ const listing = await db.readers.obligations.list({
7947
+ ids: [query.obligation_id],
7948
+ limit: 1
7949
+ });
7950
+ if (listing.obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
7951
+ const obligation = listing.obligations[0];
7624
7952
  return success({
7625
- data: from$4(obligation, quote ?? {
7626
- obligationId: id(obligation),
7627
- ask: { price: 0n },
7628
- bid: { price: 0n }
7629
- }),
7953
+ data: from$4(obligation.obligation, obligation.quote),
7630
7954
  cursor: null
7631
7955
  });
7632
7956
  } catch (err) {
7957
+ const payloadError = toPayloadError$1(err);
7633
7958
  logger.error({
7634
- err,
7959
+ err: payloadError,
7635
7960
  msg: "Error get obligation",
7636
- errorMessage: err instanceof Error ? err.message : String(err),
7637
- errorStack: err instanceof Error ? err.stack : void 0
7961
+ errorMessage: payloadError instanceof Error ? payloadError.message : String(payloadError),
7962
+ errorStack: payloadError instanceof Error ? payloadError.stack : void 0
7638
7963
  });
7639
- return failure(err);
7964
+ return failure(payloadError);
7640
7965
  }
7641
7966
  }
7642
7967
 
7643
7968
  //#endregion
7644
7969
  //#region src/api/Controllers/getObligations.ts
7970
+ function toPayloadError(err) {
7971
+ if (err instanceof BadRequestError) return new BadRequestError$1(err.message);
7972
+ return err;
7973
+ }
7645
7974
  async function getObligations$1(queryParameters, db) {
7646
7975
  const logger = getLogger();
7647
7976
  const result = safeParse("get_obligations", queryParameters, (issue) => issue.message);
@@ -7652,31 +7981,28 @@ async function getObligations$1(queryParameters, db) {
7652
7981
  const loanTokens = query.loan_tokens?.length ? query.loan_tokens : void 0;
7653
7982
  const collateralTokens = query.collateral_tokens?.length ? query.collateral_tokens : void 0;
7654
7983
  const maturities = query.maturities?.length ? query.maturities : void 0;
7655
- const { obligations, nextCursor } = await db.offers.getObligations({
7656
- cursor: query.cursor,
7657
- limit: query.limit,
7984
+ const listing = await db.readers.obligations.list({
7658
7985
  chainId: chainIds,
7659
7986
  loanToken: loanTokens,
7660
7987
  collateralToken: collateralTokens,
7661
- maturity: maturities
7988
+ maturity: maturities,
7989
+ sort: query.sort,
7990
+ cursor: query.cursor,
7991
+ limit: query.limit
7662
7992
  });
7663
- const quotes = await db.offers.getQuotes({ obligationIds: obligations.map((o) => id(o)) });
7664
7993
  return success({
7665
- data: obligations.map((o) => from$4(o, quotes.find((q) => q.obligationId === id(o)) ?? {
7666
- obligationId: id(o),
7667
- ask: { price: 0n },
7668
- bid: { price: 0n }
7669
- })),
7670
- cursor: nextCursor ?? null
7994
+ data: listing.obligations.map((item) => from$4(item.obligation, item.quote)),
7995
+ cursor: listing.nextCursor
7671
7996
  });
7672
7997
  } catch (err) {
7998
+ const payloadError = toPayloadError(err);
7673
7999
  logger.error({
7674
- err,
8000
+ err: payloadError,
7675
8001
  msg: "Error get obligations",
7676
- errorMessage: err instanceof Error ? err.message : String(err),
7677
- errorStack: err instanceof Error ? err.stack : void 0
8002
+ errorMessage: payloadError instanceof Error ? payloadError.message : String(payloadError),
8003
+ errorStack: payloadError instanceof Error ? payloadError.stack : void 0
7678
8004
  });
7679
- return failure(err);
8005
+ return failure(payloadError);
7680
8006
  }
7681
8007
  }
7682
8008
 
@@ -7710,6 +8036,7 @@ function create$15(config) {
7710
8036
  groupMaker: offer.maker.toLowerCase(),
7711
8037
  callbackAddress: offer.callback.address.toLowerCase(),
7712
8038
  callbackData: offer.callback.data,
8039
+ receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase(),
7713
8040
  blockNumber
7714
8041
  })));
7715
8042
  if (offersRows.length === 0) return [];
@@ -7772,9 +8099,11 @@ function create$15(config) {
7772
8099
  loanToken: obligations.loanToken,
7773
8100
  callbackAddress: offers.callbackAddress,
7774
8101
  callbackData: offers.callbackData,
8102
+ receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
7775
8103
  collaterals: collateralsLateral.collaterals,
7776
8104
  blockNumber: offers.blockNumber
7777
8105
  }).from(offers).innerJoin(obligations, eq(offers.obligationId, obligations.obligationId)).innerJoinLateral(collateralsLateral, sql`true`).where(and(cursor !== null && cursor !== void 0 ? gt(offers.hash, cursor) : void 0, maker !== void 0 ? eq(offers.groupMaker, maker.toLowerCase()) : void 0)).orderBy(asc(offers.hash)).limit(limit)).map((row) => {
8106
+ const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
7778
8107
  return {
7779
8108
  hash: row.hash,
7780
8109
  maker: row.maker,
@@ -7799,6 +8128,7 @@ function create$15(config) {
7799
8128
  address: row.callbackAddress,
7800
8129
  data: row.callbackData
7801
8130
  },
8131
+ receiverIfMakerIsSeller,
7802
8132
  consumed: 0n,
7803
8133
  available: 0n,
7804
8134
  takeable: 0n,
@@ -7823,64 +8153,30 @@ function create$15(config) {
7823
8153
  }
7824
8154
  throw new Error("Invalid parameters");
7825
8155
  },
7826
- getObligations: async (parameters) => {
7827
- const { ids, chainId: chainIds, loanToken: loanTokens, collateralToken: collateralTokens, maturity: maturities, cursor, limit = DEFAULT_LIMIT$3 } = parameters ?? {};
7828
- const now$1 = now();
7829
- const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? sql`(${sql.join(loanTokens.map((token) => sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), sql` OR `)})` : void 0;
7830
- const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? sql`EXISTS (
7831
- SELECT 1 FROM ${obligationCollateralsV2} oc
7832
- WHERE oc.obligation_id = ${obligations.obligationId}
7833
- AND (${sql.join(collateralTokens.map((token) => sql`LOWER(oc.asset) = ${token.toLowerCase()}`), sql` OR `)})
7834
- )` : void 0;
7835
- const result = await db.select({
7836
- obligationId: obligations.obligationId,
7837
- chainId: obligations.chainId,
7838
- loanToken: obligations.loanToken,
7839
- collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
7840
- maturity: obligations.maturity
7841
- }).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
7842
- AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).groupBy(obligations.obligationId).where(and(cursor !== null && cursor !== void 0 ? gt(obligations.obligationId, cursor) : sql`true`, ids !== void 0 && ids.length > 0 ? inArray(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? inArray(obligations.maturity, maturities) : gte(obligations.maturity, now$1), collateralFilter)).orderBy(asc(obligations.obligationId)).limit(limit);
7843
- const items = [];
7844
- for (const row of result) items.push(from$15({
7845
- chainId: row.chainId,
7846
- loanToken: row.loanToken,
7847
- collaterals: row.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)).map((c) => from$17({
7848
- asset: c.asset,
7849
- oracle: c.oracle,
7850
- lltv: from$18(BigInt(c.lltv))
7851
- })),
7852
- maturity: row.maturity
7853
- }));
7854
- const returnedItems = Array.from(items.values());
7855
- return {
7856
- obligations: returnedItems,
7857
- nextCursor: returnedItems.length === limit && returnedItems.length > 0 ? result[result.length - 1].obligationId : null
7858
- };
7859
- },
7860
8156
  getQuotes: async (parameters) => {
7861
8157
  const { obligationIds } = parameters;
7862
8158
  if (obligationIds.length === 0) return [];
7863
8159
  const now$2 = now();
7864
8160
  const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
7865
8161
  obligationId: offers.obligationId,
7866
- price: offers.tick
8162
+ tick: offers.tick
7867
8163
  }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(inArray(offers.obligationId, obligationIds), eq(offers.buy, side === "buy"), gte(offers.expiry, now$2), gte(offers.maturity, now$2), lte(offers.start, now$2), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(offers.obligationId, side === "buy" ? sql`${offers.tick} ASC` : sql`${offers.tick} DESC`);
7868
8164
  const [bestBuys, bestSells] = await Promise.all([query({ side: "buy" }), query({ side: "sell" })]);
7869
8165
  const quotes = /* @__PURE__ */ new Map();
7870
8166
  for (const row of bestSells) quotes.set(row.obligationId, {
7871
- ask: { price: BigInt(row.price) },
7872
- bid: { price: 0n }
8167
+ ask: { tick: row.tick },
8168
+ bid: { tick: null }
7873
8169
  });
7874
8170
  for (const row of bestBuys) {
7875
8171
  const quote = quotes.get(row.obligationId);
7876
8172
  if (!quote) {
7877
8173
  quotes.set(row.obligationId, {
7878
- ask: { price: 0n },
7879
- bid: { price: BigInt(row.price) }
8174
+ ask: { tick: null },
8175
+ bid: { tick: row.tick }
7880
8176
  });
7881
8177
  continue;
7882
8178
  }
7883
- quote.bid = { price: BigInt(row.price) };
8179
+ quote.bid = { tick: row.tick };
7884
8180
  }
7885
8181
  return Array.from(quotes.entries()).map(([id, quote]) => {
7886
8182
  return from$11({
@@ -7991,6 +8287,7 @@ async function getOffersQuery(db, parameters) {
7991
8287
  loanToken: obligations.loanToken,
7992
8288
  callbackAddress: offers.callbackAddress,
7993
8289
  callbackData: offers.callbackData,
8290
+ receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
7994
8291
  collaterals: collateralsLateral.collaterals,
7995
8292
  blockNumber: offers.blockNumber,
7996
8293
  available: sql`${availableExpr}::numeric`.as("available"),
@@ -8012,6 +8309,7 @@ async function getOffersQuery(db, parameters) {
8012
8309
  )
8013
8310
  END
8014
8311
  ) > 0` : void 0)).orderBy(asc(offers.hash)).limit(limit)).map((row) => {
8312
+ const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
8015
8313
  return {
8016
8314
  hash: row.hash,
8017
8315
  maker: row.maker,
@@ -8036,6 +8334,7 @@ async function getOffersQuery(db, parameters) {
8036
8334
  address: row.callbackAddress,
8037
8335
  data: row.callbackData
8038
8336
  },
8337
+ receiverIfMakerIsSeller,
8039
8338
  consumed: BigInt(row.consumed),
8040
8339
  available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
8041
8340
  takeable: BigInt(String(row.takeable ?? "0").split(".")[0] ?? "0"),
@@ -8126,7 +8425,7 @@ async function getUserPositions(queryParameters, db) {
8126
8425
  async function validateOffers(body, gatekeeper) {
8127
8426
  const logger = getLogger();
8128
8427
  const result = safeParse("validate_offers", body, (issue) => issue.message);
8129
- if (!result.success) return failure(new BadRequestError(result.error.issues[0]?.message ?? "Invalid request body"));
8428
+ if (!result.success) return failure(new BadRequestError$1(result.error.issues[0]?.message ?? "Invalid request body"));
8130
8429
  const { offers: rawOffers } = result.data;
8131
8430
  const parsedOffers = [];
8132
8431
  const offerIndexByHash = /* @__PURE__ */ new Map();
@@ -8142,7 +8441,7 @@ async function validateOffers(body, gatekeeper) {
8142
8441
  } catch (err) {
8143
8442
  let message = err instanceof Error ? err.message : String(err);
8144
8443
  if (err instanceof InvalidOfferError) message = err.formattedMessage;
8145
- return failure(new BadRequestError(`Offer at index ${i} failed to parse: ${message}`));
8444
+ return failure(new BadRequestError$1(`Offer at index ${i} failed to parse: ${message}`));
8146
8445
  }
8147
8446
  }
8148
8447
  try {
@@ -8434,7 +8733,8 @@ async function getOffers(apiClient, parameters) {
8434
8733
  callback: {
8435
8734
  address: offerData.callback,
8436
8735
  data: offerData.callback_data
8437
- }
8736
+ },
8737
+ receiver_if_maker_is_seller: offerData.receiver_if_maker_is_seller
8438
8738
  }),
8439
8739
  hash: item.offer_hash,
8440
8740
  consumed: BigInt(item.consumed),
@@ -8451,13 +8751,15 @@ async function getOffers(apiClient, parameters) {
8451
8751
  };
8452
8752
  }
8453
8753
  async function getObligations(apiClient, parameters) {
8754
+ const sort = parameters?.sort?.length ? parameters.sort.join(",") : void 0;
8454
8755
  const { data, error, response } = await apiClient.GET("/v1/obligations", { params: { query: {
8455
8756
  cursor: parameters?.cursor,
8456
8757
  limit: parameters?.limit,
8457
8758
  chains: parameters?.chainIds,
8458
8759
  loan_tokens: parameters?.loanTokens,
8459
8760
  collateral_tokens: parameters?.collateralTokens,
8460
- maturities: parameters?.maturities
8761
+ maturities: parameters?.maturities,
8762
+ sort
8461
8763
  } } });
8462
8764
  if (error !== void 0) {
8463
8765
  switch (response.status) {
@@ -8481,10 +8783,10 @@ async function getObligations(apiClient, parameters) {
8481
8783
  const { obligationId: _, ...returned } = {
8482
8784
  id: () => id(obligation),
8483
8785
  ...obligation,
8484
- ...fromSnakeCase({
8485
- obligation_id: item.id,
8486
- ask: item.ask,
8487
- bid: item.bid
8786
+ ...from$11({
8787
+ obligationId: item.id,
8788
+ ask: { tick: item.ask.tick },
8789
+ bid: { tick: item.bid.tick }
8488
8790
  })
8489
8791
  };
8490
8792
  return returned;
@@ -8940,6 +9242,7 @@ async function _getOffers(db, params) {
8940
9242
  p.buy,
8941
9243
  p.callback_address,
8942
9244
  p.callback_data,
9245
+ p.receiver_if_maker_is_seller,
8943
9246
  p.block_number,
8944
9247
  p.group_chain_id,
8945
9248
  p.group_maker,
@@ -9017,6 +9320,7 @@ async function _getOffers(db, params) {
9017
9320
  buy,
9018
9321
  callback_address,
9019
9322
  callback_data,
9323
+ receiver_if_maker_is_seller,
9020
9324
  block_number,
9021
9325
  group_chain_id,
9022
9326
  group_maker,
@@ -9034,8 +9338,8 @@ async function _getOffers(db, params) {
9034
9338
  ORDER BY clc.hash, clc.position_chain_id, clc.position_contract, clc.position_user, clc.contribution_in_loan DESC
9035
9339
  ) deduped
9036
9340
  GROUP BY hash, obligation_id, assets, tick, obligation_units, obligation_shares, maturity, expiry, start, group_group, buy,
9037
- callback_address, callback_data, block_number, group_chain_id, group_maker,
9038
- consumed, chain_id, loan_token, session
9341
+ callback_address, callback_data, block_number, group_chain_id, group_maker,
9342
+ consumed, chain_id, loan_token, session, receiver_if_maker_is_seller
9039
9343
  UNION ALL
9040
9344
  -- Sell offers without callbacks: collateral positions not indexed, takeable = assets - consumed
9041
9345
  SELECT
@@ -9043,6 +9347,7 @@ async function _getOffers(db, params) {
9043
9347
  p.obligation_units, p.obligation_shares,
9044
9348
  p.maturity, p.expiry, p.start, p.group_group,
9045
9349
  p.buy, p.callback_address, p.callback_data,
9350
+ p.receiver_if_maker_is_seller,
9046
9351
  p.block_number, p.group_chain_id, p.group_maker,
9047
9352
  p.consumed, p.chain_id, p.loan_token, p.session,
9048
9353
  0 AS total_available
@@ -9071,6 +9376,7 @@ async function _getOffers(db, params) {
9071
9376
  oc.loan_token,
9072
9377
  oc.callback_address,
9073
9378
  oc.callback_data,
9379
+ oc.receiver_if_maker_is_seller,
9074
9380
  oc.block_number,
9075
9381
  oc.session,
9076
9382
  COALESCE(oc.total_available, 0) AS available,
@@ -9100,6 +9406,7 @@ async function _getOffers(db, params) {
9100
9406
  `);
9101
9407
  return {
9102
9408
  rows: raw.rows.map((row) => {
9409
+ const receiverIfMakerIsSeller = (row.receiver_if_maker_is_seller ?? row.group_maker).toLowerCase();
9103
9410
  return {
9104
9411
  hash: row.hash,
9105
9412
  maker: row.group_maker,
@@ -9124,6 +9431,7 @@ async function _getOffers(db, params) {
9124
9431
  address: row.callback_address,
9125
9432
  data: row.callback_data
9126
9433
  },
9434
+ receiverIfMakerIsSeller,
9127
9435
  blockNumber: row.block_number,
9128
9436
  consumed: BigInt(row.consumed ?? 0),
9129
9437
  available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
@@ -9373,38 +9681,60 @@ function create$8(db) {
9373
9681
  * @returns Obligations domain. {@link ObligationsDomain}
9374
9682
  */
9375
9683
  function create$7(db) {
9376
- return { create: async (obligations$1) => {
9377
- if (obligations$1.length === 0) return;
9378
- const obligationsById = /* @__PURE__ */ new Map();
9379
- for (const obligation of obligations$1) {
9380
- const id$1 = id(obligation).toLowerCase();
9381
- if (!obligationsById.get(id$1)) obligationsById.set(id$1, obligation);
9382
- }
9383
- try {
9384
- await db.transaction(async (dbTx) => {
9385
- const obligationRows = obligations$1.map((obligation) => ({
9386
- obligationId: id(obligation),
9387
- chainId: obligation.chainId,
9388
- loanToken: obligation.loanToken.toLowerCase(),
9389
- maturity: obligation.maturity
9390
- }));
9391
- for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
9392
- const collateralRows = obligations$1.flatMap((obligation) => {
9393
- return obligation.collaterals.map((collateral) => ({
9684
+ return {
9685
+ get: async (parameters) => {
9686
+ const chainIds = parameters?.chainId;
9687
+ const now$1 = now();
9688
+ return (await db.select({
9689
+ chainId: obligations.chainId,
9690
+ loanToken: obligations.loanToken,
9691
+ collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
9692
+ maturity: obligations.maturity
9693
+ }).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
9694
+ AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).groupBy(obligations.obligationId).where(and(chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, gte(obligations.maturity, now$1))).orderBy(asc(obligations.obligationId))).map((row) => from$15({
9695
+ chainId: row.chainId,
9696
+ loanToken: row.loanToken,
9697
+ collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$17({
9698
+ asset: collateral.asset,
9699
+ oracle: collateral.oracle,
9700
+ lltv: from$18(BigInt(collateral.lltv))
9701
+ })),
9702
+ maturity: row.maturity
9703
+ }));
9704
+ },
9705
+ create: async (obligations$1) => {
9706
+ if (obligations$1.length === 0) return;
9707
+ const obligationsById = /* @__PURE__ */ new Map();
9708
+ for (const obligation of obligations$1) {
9709
+ const id$1 = id(obligation).toLowerCase();
9710
+ if (!obligationsById.get(id$1)) obligationsById.set(id$1, obligation);
9711
+ }
9712
+ try {
9713
+ await db.transaction(async (dbTx) => {
9714
+ const obligationRows = obligations$1.map((obligation) => ({
9394
9715
  obligationId: id(obligation),
9395
- asset: collateral.asset.toLowerCase(),
9396
- oracleChainId: obligation.chainId,
9397
- oracleAddress: collateral.oracle.toLowerCase(),
9398
- lltv: collateral.lltv
9716
+ chainId: obligation.chainId,
9717
+ loanToken: obligation.loanToken.toLowerCase(),
9718
+ maturity: obligation.maturity
9399
9719
  }));
9720
+ for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
9721
+ const collateralRows = obligations$1.flatMap((obligation) => {
9722
+ return obligation.collaterals.map((collateral) => ({
9723
+ obligationId: id(obligation),
9724
+ asset: collateral.asset.toLowerCase(),
9725
+ oracleChainId: obligation.chainId,
9726
+ oracleAddress: collateral.oracle.toLowerCase(),
9727
+ lltv: collateral.lltv
9728
+ }));
9729
+ });
9730
+ for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
9400
9731
  });
9401
- for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
9402
- });
9403
- } catch (err) {
9404
- const error = err instanceof Error ? err : new Error(String(err));
9405
- throw new Error("Obligations.create failed. Ensure oracles exist before inserting obligations.", { cause: error });
9732
+ } catch (err) {
9733
+ const error = err instanceof Error ? err : new Error(String(err));
9734
+ throw new Error("Obligations.create failed. Ensure oracles exist before inserting obligations.", { cause: error });
9735
+ }
9406
9736
  }
9407
- } };
9737
+ };
9408
9738
  }
9409
9739
 
9410
9740
  //#endregion
@@ -9989,6 +10319,9 @@ function createDomains(core, chainRegistry) {
9989
10319
  transfers: create$3(core)
9990
10320
  };
9991
10321
  }
10322
+ function createReaders(core) {
10323
+ return { obligations: create$16({ db: core }) };
10324
+ }
9992
10325
  const AUGMENT_CACHE = /* @__PURE__ */ new WeakMap();
9993
10326
  function augmentWithDomains(base, chainRegistry) {
9994
10327
  const cached = AUGMENT_CACHE.get(base)?.get(chainRegistry);
@@ -10000,6 +10333,7 @@ function augmentWithDomains(base, chainRegistry) {
10000
10333
  });
10001
10334
  };
10002
10335
  const dms = createDomains(wrapped, chainRegistry);
10336
+ const readers = createReaders(wrapped);
10003
10337
  Object.defineProperties(wrapped, {
10004
10338
  book: {
10005
10339
  value: dms.book,
@@ -10056,6 +10390,10 @@ function augmentWithDomains(base, chainRegistry) {
10056
10390
  transfers: {
10057
10391
  value: dms.transfers,
10058
10392
  enumerable: true
10393
+ },
10394
+ readers: {
10395
+ value: readers,
10396
+ enumerable: true
10059
10397
  }
10060
10398
  });
10061
10399
  const chainRegistryMap = AUGMENT_CACHE.get(base);
@@ -10695,7 +11033,7 @@ const chains = ({ chains }) => single("chain_ids", `Validates that offer chain i
10695
11033
  });
10696
11034
  const maturity = ({ maturities }) => single("maturity", `Validates that offer maturity is one of: [${maturities.join(", ")}]`, (offer) => {
10697
11035
  const allowedMaturities = maturities.map((m) => from$16(m));
10698
- 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}` };
11036
+ if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be one of (${allowedMaturities.join(", ")}). Got: ${offer.maturity}` };
10699
11037
  });
10700
11038
  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) => {
10701
11039
  if (!isEmptyCallback(offer)) return { message: "Non-empty callbacks are not supported." };
@@ -10775,7 +11113,7 @@ const morphoRules = (chains$3) => {
10775
11113
  sameMaker(),
10776
11114
  amountMutualExclusivity(),
10777
11115
  chains({ chains: chains$3 }),
10778
- maturity({ maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth] }),
11116
+ maturity({ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek] }),
10779
11117
  callback({
10780
11118
  callbacks: [Type$1.BuyWithEmptyCallback, Type$1.SellWithEmptyCallback],
10781
11119
  allowedAddresses: []