@morpho-dev/router 0.12.0 → 0.12.1

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.
package/dist/cli.js CHANGED
@@ -211,7 +211,7 @@ function startActiveSpan(tracer, name, fn) {
211
211
  //#endregion
212
212
  //#region package.json
213
213
  var name = "@morpho-dev/router";
214
- var version = "0.12.0";
214
+ var version = "0.12.1";
215
215
  var description = "Router package for Morpho protocol";
216
216
 
217
217
  //#endregion
@@ -341,8 +341,8 @@ const chains$1 = {
341
341
  name: "base",
342
342
  custom: {
343
343
  morpho: {
344
- address: "0xee437005ba0a3d0e9bc6510775c41f23ed2909e1",
345
- blockCreated: 43515847
344
+ address: "0x26378861d9c740fe86e7472752aef5b1a783c60b",
345
+ blockCreated: 43606117
346
346
  },
347
347
  morphoBlue: {
348
348
  address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -371,8 +371,8 @@ const chains$1 = {
371
371
  name: "ethereum-virtual-testnet",
372
372
  custom: {
373
373
  morpho: {
374
- address: "0xc1c6e6fa308eeef18e96be420d8ccb218215a26c",
375
- blockCreated: 23244321
374
+ address: "0x94c576ee728ee290116ee65c0d1e9fed4a1b5041",
375
+ blockCreated: 23244323
376
376
  },
377
377
  morphoBlue: {
378
378
  address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -807,7 +807,7 @@ async function startDetached(config, logPath) {
807
807
  }
808
808
  async function startInProcess(config, logPath) {
809
809
  fs.closeSync(fs.openSync(logPath, "w"));
810
- const { startServer: startProolServer } = await import("./server-D4xxddev.js");
810
+ const { startServer: startProolServer } = await import("./server-DNFuP89-.js");
811
811
  const stop = await startProolServer(config);
812
812
  return {
813
813
  process: {
@@ -929,6 +929,7 @@ const transformAddress = (val, ctx) => {
929
929
  });
930
930
  return z$2.NEVER;
931
931
  };
932
+ const AddressSchema = z$2.string().transform(transformAddress);
932
933
 
933
934
  //#endregion
934
935
  //#region src/chain/state.ts
@@ -1767,14 +1768,8 @@ const isEmptyCallback = (offer) => offer.callback.data === "0x";
1767
1768
 
1768
1769
  //#endregion
1769
1770
  //#region src/core/Maturity.ts
1770
- const MaturitySchema = z$2.number().int().refine((maturity) => {
1771
- try {
1772
- from$15(maturity);
1773
- return true;
1774
- } catch (_e) {
1775
- return false;
1776
- }
1777
- }, { error: (issue) => {
1771
+ const MAX_TIMESTAMP_SECONDS = 999999999999;
1772
+ const MaturitySchema = z$2.number().int().max(MAX_TIMESTAMP_SECONDS).refine(isAt15UTC, { error: (issue) => {
1778
1773
  try {
1779
1774
  return `The maturity is set to ${/* @__PURE__ */ new Date(issue.input * 1e3)}. It must be at 15:00:00 UTC.`;
1780
1775
  } catch (_) {
@@ -1809,7 +1804,7 @@ function from$15(ts) {
1809
1804
  if (ts in MaturityOptions) return MaturityOptions[ts]();
1810
1805
  throw new InvalidOptionError$1(ts);
1811
1806
  }
1812
- if (typeof ts === "number" && ts > 0xe8d4a51000) throw new InvalidFormatError();
1807
+ if (typeof ts === "number" && ts > MAX_TIMESTAMP_SECONDS) throw new InvalidFormatError();
1813
1808
  if (!isAt15UTC(ts)) throw new InvalidDateError(ts);
1814
1809
  return ts;
1815
1810
  }
@@ -1903,7 +1898,6 @@ const assets = {
1903
1898
  "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
1904
1899
  "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
1905
1900
  "0x4200000000000000000000000000000000000006",
1906
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
1907
1901
  "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
1908
1902
  "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
1909
1903
  "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
@@ -2055,7 +2049,7 @@ const MorphoV2 = parseAbi([
2055
2049
  "function obligationCreated(bytes32 id) view returns (bool)",
2056
2050
  "function obligationState(bytes32 id) view returns (uint128 totalUnits, uint256 withdrawable, uint128 lossIndex, bool created)",
2057
2051
  "function owner() view returns (address)",
2058
- "function repay((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, uint256 obligationUnits, address onBehalf)",
2052
+ "function repay((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, uint256 units, address onBehalf)",
2059
2053
  "function session(address user) view returns (bytes32)",
2060
2054
  "function setConsumed(bytes32 group, uint256 amount, address onBehalf)",
2061
2055
  "function setDefaultTradingFee(address loanToken, uint256 index, uint256 newTradingFee)",
@@ -2070,7 +2064,7 @@ const MorphoV2 = parseAbi([
2070
2064
  "function shuffleSession(address onBehalf)",
2071
2065
  "function slash(bytes32 id, address user)",
2072
2066
  "function supplyCollateral((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, uint256 collateralIndex, uint256 assets, address onBehalf)",
2073
- "function take(uint256 obligationUnits, address taker, address takerCallback, bytes takerCallbackData, address receiverIfTakerIsSeller, ((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, bool buy, address maker, uint256 obligationUnits, uint256 start, uint256 expiry, uint256 tick, bytes32 group, bytes32 session, address callback, bytes callbackData, address receiverIfMakerIsSeller, bool exitOnly) offer, (uint8 v, bytes32 r, bytes32 s) sig, bytes32 root, bytes32[] proof) returns (uint256, uint256, uint256)",
2067
+ "function take(uint256 units, address taker, address takerCallback, bytes takerCallbackData, address receiverIfTakerIsSeller, ((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, bool buy, address maker, uint256 maxUnits, uint256 start, uint256 expiry, uint256 tick, bytes32 group, bytes32 session, address callback, bytes callbackData, address receiverIfMakerIsSeller, bool exitOnly) offer, (uint8 v, bytes32 r, bytes32 s) sig, bytes32 root, bytes32[] proof) returns (uint256, uint256, uint256)",
2074
2068
  "function toId((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation) view returns (bytes32)",
2075
2069
  "function toObligation(bytes32 id) view returns ((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold))",
2076
2070
  "function totalUnits(bytes32 id) view returns (uint256)",
@@ -2078,14 +2072,14 @@ const MorphoV2 = parseAbi([
2078
2072
  "function tradingFee(bytes32 id, uint256 timeToMaturity) view returns (uint256)",
2079
2073
  "function tradingFeeRecipient() view returns (address)",
2080
2074
  "function userLossIndex(bytes32 id, address user) view returns (uint128)",
2081
- "function withdraw((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, uint256 obligationUnits, address onBehalf, address receiver)",
2075
+ "function withdraw((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, uint256 units, address onBehalf, address receiver)",
2082
2076
  "function withdrawCollateral((address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation, uint256 collateralIndex, uint256 assets, address onBehalf, address receiver)",
2083
2077
  "function withdrawable(bytes32 id) view returns (uint256)",
2084
2078
  "event Constructor(address indexed owner)",
2085
2079
  "event FlashLoan(address indexed caller, address indexed token, uint256 assets)",
2086
2080
  "event Liquidate(address indexed caller, bytes32 indexed id_, uint256 collateralIndex, uint256 seizedAssets, uint256 repaidUnits, address indexed borrower, uint256 badDebt, uint256 latestLossIndex)",
2087
2081
  "event ObligationCreated(bytes32 indexed id_, (address loanToken, (address token, uint256 lltv, uint256 maxLif, address oracle)[] collaterals, uint256 maturity, uint256 rcfThreshold) obligation)",
2088
- "event Repay(address indexed caller, bytes32 indexed id_, uint256 obligationUnits, address indexed onBehalf)",
2082
+ "event Repay(address indexed caller, bytes32 indexed id_, uint256 units, address indexed onBehalf)",
2089
2083
  "event SetConsumed(address indexed caller, address indexed onBehalf, bytes32 indexed group, uint256 amount)",
2090
2084
  "event SetDefaultTradingFee(address indexed loanToken, uint256 indexed index, uint256 newTradingFee)",
2091
2085
  "event SetFeeSetter(address indexed feeSetter)",
@@ -2099,8 +2093,8 @@ const MorphoV2 = parseAbi([
2099
2093
  "event ShuffleSession(address indexed caller, address indexed onBehalf, bytes32 session)",
2100
2094
  "event Slash(address caller, bytes32 indexed id_, address indexed user, uint256 credit, uint256 latestLossIndex)",
2101
2095
  "event SupplyCollateral(address caller, bytes32 indexed id_, address indexed collateral, uint256 assets, address indexed onBehalf)",
2102
- "event Take(address caller, bytes32 indexed id_, address indexed maker, address indexed taker, bool offerIsBuy, uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, address sellerReceiver, bytes32 group, uint256 consumed, uint256 totalUnits)",
2103
- "event Withdraw(address caller, bytes32 indexed id_, uint256 obligationUnits, address indexed onBehalf, address indexed receiver)",
2096
+ "event Take(address caller, bytes32 indexed id_, address indexed maker, address indexed taker, bool offerIsBuy, uint256 buyerAssets, uint256 sellerAssets, uint256 units, address sellerReceiver, bytes32 group, uint256 consumed, uint256 totalUnits)",
2097
+ "event Withdraw(address caller, bytes32 indexed id_, uint256 units, address indexed onBehalf, address indexed receiver)",
2104
2098
  "event WithdrawCollateral(address caller, bytes32 indexed id_, address indexed collateral, uint256 assets, address indexed onBehalf, address receiver)"
2105
2099
  ]);
2106
2100
 
@@ -2276,10 +2270,10 @@ const abi$1 = [
2276
2270
  }
2277
2271
  ];
2278
2272
  const CollateralSchema = z$2.object({
2279
- asset: z$2.string().transform(transformAddress),
2280
- oracle: z$2.string().transform(transformAddress),
2273
+ asset: AddressSchema,
2274
+ oracle: AddressSchema,
2281
2275
  lltv: LLTVSchema,
2282
- maxLif: z$2.bigint({ coerce: true }).min(0n).optional().default(0n)
2276
+ maxLif: z$2.bigint({ coerce: true }).min(0n)
2283
2277
  });
2284
2278
  const CollateralsSchema = z$2.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine((collaterals) => {
2285
2279
  for (let i = 1; i < collaterals.length; i++) if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) return false;
@@ -2297,7 +2291,7 @@ const from$13 = (parameters) => {
2297
2291
  return {
2298
2292
  asset: parameters.asset.toLowerCase(),
2299
2293
  lltv: from$14(parameters.lltv),
2300
- maxLif: parameters.maxLif ?? 0n,
2294
+ maxLif: parameters.maxLif,
2301
2295
  oracle: parameters.oracle.toLowerCase()
2302
2296
  };
2303
2297
  };
@@ -2388,7 +2382,7 @@ function stringifyBigint(value) {
2388
2382
  //#endregion
2389
2383
  //#region src/core/Obligation.ts
2390
2384
  const ObligationSchema = z$2.object({
2391
- loanToken: z$2.string().transform(transformAddress),
2385
+ loanToken: AddressSchema,
2392
2386
  collaterals: CollateralsSchema,
2393
2387
  maturity: MaturitySchema,
2394
2388
  rcfThreshold: z$2.bigint({ coerce: true }).min(0n)
@@ -2431,7 +2425,8 @@ const tupleAbi = [{
2431
2425
  * Collateral.from({
2432
2426
  * asset: privateKeyToAccount(generatePrivateKey()).address,
2433
2427
  * oracle: privateKeyToAccount(generatePrivateKey()).address,
2434
- * lltv: 0.965
2428
+ * lltv: 0.965,
2429
+ * maxLif: 0n,
2435
2430
  * }),
2436
2431
  * ],
2437
2432
  * maturity: Maturity.from("end_of_next_quarter"),
@@ -2481,7 +2476,7 @@ function key(parameters) {
2481
2476
  parameters.collaterals.map((c) => ({
2482
2477
  token: c.asset.toLowerCase(),
2483
2478
  lltv: c.lltv,
2484
- maxLif: c.maxLif ?? 0n,
2479
+ maxLif: c.maxLif,
2485
2480
  oracle: c.oracle.toLowerCase()
2486
2481
  })),
2487
2482
  BigInt(parameters.maturity),
@@ -2533,7 +2528,7 @@ function creationCode(parameters) {
2533
2528
  collaterals: parameters.obligation.collaterals.map((collateral) => ({
2534
2529
  token: collateral.asset.toLowerCase(),
2535
2530
  lltv: collateral.lltv,
2536
- maxLif: collateral.maxLif ?? 0n,
2531
+ maxLif: collateral.maxLif,
2537
2532
  oracle: collateral.oracle.toLowerCase()
2538
2533
  })),
2539
2534
  maturity: BigInt(parameters.obligation.maturity),
@@ -2567,8 +2562,8 @@ let Status = /* @__PURE__ */ function(Status) {
2567
2562
  }({});
2568
2563
  const OfferSchema = () => {
2569
2564
  return z$2.object({
2570
- maker: z$2.string().transform(transformAddress),
2571
- obligationUnits: z$2.bigint({ coerce: true }).min(0n).max(maxUint256).optional().default(0n),
2565
+ maker: AddressSchema,
2566
+ maxUnits: z$2.bigint({ coerce: true }).min(0n).max(maxUint256).optional().default(0n),
2572
2567
  tick: z$2.coerce.number().int().min(0).max(990),
2573
2568
  maturity: MaturitySchema,
2574
2569
  rcfThreshold: z$2.bigint({ coerce: true }).min(0n).max(maxUint256),
@@ -2585,13 +2580,13 @@ const OfferSchema = () => {
2585
2580
  z$2.bigint()
2586
2581
  ]).optional().default("0x0000000000000000000000000000000000000000000000000000000000000000").transform(transformBytes32),
2587
2582
  buy: z$2.boolean(),
2588
- loanToken: z$2.string().transform(transformAddress),
2583
+ loanToken: AddressSchema,
2589
2584
  collaterals: CollateralsSchema,
2590
2585
  callback: z$2.object({
2591
- address: z$2.string().transform(transformAddress),
2586
+ address: AddressSchema,
2592
2587
  data: z$2.string().transform(transformHex)
2593
2588
  }),
2594
- receiverIfMakerIsSeller: z$2.string().transform(transformAddress),
2589
+ receiverIfMakerIsSeller: AddressSchema,
2595
2590
  exitOnly: z$2.boolean().optional().default(false)
2596
2591
  }).refine((data) => data.start < data.expiry, {
2597
2592
  message: "start must be before expiry",
@@ -2644,7 +2639,7 @@ function toSnakeCase(offer) {
2644
2639
  */
2645
2640
  const serialize = (offer) => ({
2646
2641
  maker: offer.maker,
2647
- obligationUnits: offer.obligationUnits.toString(),
2642
+ maxUnits: offer.maxUnits.toString(),
2648
2643
  tick: offer.tick,
2649
2644
  maturity: Number(offer.maturity),
2650
2645
  rcfThreshold: offer.rcfThreshold.toString(),
@@ -2702,7 +2697,7 @@ function random(config) {
2702
2697
  const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
2703
2698
  const unit = BigInt(10) ** BigInt(loanTokenDecimals);
2704
2699
  const amountBase = BigInt(100 + int(999901));
2705
- const obligationUnitsScaled = config?.obligationUnits ?? amountBase * unit;
2700
+ const maxUnitsScaled = config?.maxUnits ?? amountBase * unit;
2706
2701
  const emptyCallback = {
2707
2702
  address: zeroAddress,
2708
2703
  data: "0x"
@@ -2710,7 +2705,7 @@ function random(config) {
2710
2705
  const maker = config?.maker ?? address();
2711
2706
  return from$11({
2712
2707
  maker,
2713
- obligationUnits: obligationUnitsScaled,
2708
+ maxUnits: maxUnitsScaled,
2714
2709
  tick,
2715
2710
  maturity,
2716
2711
  rcfThreshold: config?.rcfThreshold ?? 0n,
@@ -2764,7 +2759,7 @@ function hash$1(offer) {
2764
2759
  * Obligation obligation; // nested tuple
2765
2760
  * bool buy;
2766
2761
  * address maker;
2767
- * uint256 obligationUnits;
2762
+ * uint256 maxUnits;
2768
2763
  * uint256 start;
2769
2764
  * uint256 expiry;
2770
2765
  * uint256 tick;
@@ -2830,7 +2825,7 @@ const OfferAbi = [{
2830
2825
  type: "address"
2831
2826
  },
2832
2827
  {
2833
- name: "obligationUnits",
2828
+ name: "maxUnits",
2834
2829
  type: "uint256"
2835
2830
  },
2836
2831
  {
@@ -2878,7 +2873,7 @@ function encode(offer) {
2878
2873
  collaterals: offer.collaterals.map((c) => ({
2879
2874
  token: c.asset,
2880
2875
  lltv: c.lltv,
2881
- maxLif: c.maxLif ?? 0n,
2876
+ maxLif: c.maxLif,
2882
2877
  oracle: c.oracle
2883
2878
  })),
2884
2879
  maturity: BigInt(offer.maturity),
@@ -2886,7 +2881,7 @@ function encode(offer) {
2886
2881
  },
2887
2882
  buy: offer.buy,
2888
2883
  maker: offer.maker,
2889
- obligationUnits: offer.obligationUnits,
2884
+ maxUnits: offer.maxUnits,
2890
2885
  start: BigInt(offer.start),
2891
2886
  expiry: BigInt(offer.expiry),
2892
2887
  tick: BigInt(offer.tick),
@@ -2948,7 +2943,7 @@ const takeEvent = {
2948
2943
  internalType: "uint256"
2949
2944
  },
2950
2945
  {
2951
- name: "obligationUnits",
2946
+ name: "units",
2952
2947
  type: "uint256",
2953
2948
  indexed: false,
2954
2949
  internalType: "uint256"
@@ -3034,7 +3029,7 @@ const repayEvent = {
3034
3029
  internalType: "bytes32"
3035
3030
  },
3036
3031
  {
3037
- name: "obligationUnits",
3032
+ name: "units",
3038
3033
  type: "uint256",
3039
3034
  indexed: false,
3040
3035
  internalType: "uint256"
@@ -3315,24 +3310,79 @@ function assertTick(tick) {
3315
3310
  if (!Number.isInteger(tick) || tick < 0 || tick > TICK_RANGE) throw new InvalidTickError(tick);
3316
3311
  }
3317
3312
  /**
3318
- * Converts obligation units to assets using the tick price (mulDivDown).
3319
- * @param obligationUnits - The obligation units to convert.
3313
+ * Converts units into maker-side assets using the raw tick price surface.
3314
+ * This is the public forward conversion used by the router for lot sizing and other maker-facing capacity math.
3315
+ * - `buy = true` -> `floor(units * price / WAD)`
3316
+ * - `buy = false` -> `ceil(units * price / WAD)`
3317
+ * @param units - The units to convert.
3320
3318
  * @param tick - The offer tick.
3321
- * @returns The equivalent amount in assets.
3319
+ * @param buy - Whether the maker side of the offer is buy (`true`) or sell (`false`).
3320
+ * @returns The equivalent amount in maker-side assets.
3322
3321
  */
3323
- function obligationUnitsToAssets(obligationUnits, tick) {
3324
- return obligationUnits * tickToPrice(tick) / WAD$1;
3322
+ function unitsToAssets(units, tick, buy) {
3323
+ const price = tickToPrice(tick);
3324
+ return buy ? unitsToFloorAssets(units, price) : unitsToCeilAssets(units, price);
3325
3325
  }
3326
3326
  /**
3327
- * Converts assets to obligation units using the tick price (mulDivUp).
3328
- * @param assets - The asset amount to convert.
3327
+ * Converts a maker-side asset cap into a safe units cap using the raw tick price.
3328
+ * Buy offers use the max-safe inverse of `floor(units * price / WAD)`.
3329
+ * Sell offers use the max-safe inverse of `ceil(units * price / WAD)`.
3330
+ * The result is clamped to the remaining offer size because the asset cap may have been rounded before inversion.
3331
+ * Zero integer asset caps can still admit positive units on floor-rounded sub-WAD buy ticks, and tick=0 keeps the
3332
+ * whole remaining interval safe because the raw maker-asset surface stays at zero.
3333
+ * @param assets - The integer maker-side asset cap.
3329
3334
  * @param tick - The offer tick.
3330
- * @returns The equivalent amount in obligation units.
3335
+ * @param buy - Whether the maker side of the offer is buy (`true`) or sell (`false`).
3336
+ * @param remainingUnits - The maximum units still available on the offer.
3337
+ * @returns The greatest safe units amount under the maker-side raw-price surface.
3331
3338
  */
3332
- function assetsToObligationUnits(assets, tick) {
3339
+ function assetsToUnits(assets, tick, buy, remainingUnits) {
3340
+ if (remainingUnits <= 0n) return 0n;
3341
+ if (assets < 0n) return 0n;
3333
3342
  const price = tickToPrice(tick);
3334
- if (price === 0n) return 0n;
3335
- return (assets * WAD$1 + price - 1n) / price;
3343
+ if (price === 0n) return remainingUnits;
3344
+ const units = buy ? floorAssetsToUnits(assets, price) : ceilAssetsToUnits(assets, price);
3345
+ return units > remainingUnits ? remainingUnits : units;
3346
+ }
3347
+ /**
3348
+ * Forward conversion for raw-price floor surfaces.
3349
+ * This matches buy-side maker assets and the legacy floor-only helper.
3350
+ * @param units - The units to convert.
3351
+ * @param price - The raw tick price.
3352
+ * @returns The floor-rounded maker-side assets.
3353
+ */
3354
+ function unitsToFloorAssets(units, price) {
3355
+ return units * price / WAD$1;
3356
+ }
3357
+ /**
3358
+ * Forward conversion for raw-price ceil surfaces.
3359
+ * This matches sell-side maker assets, where the maker receives `sellerAssets` rounded up onchain.
3360
+ * @param units - The units to convert.
3361
+ * @param price - The raw tick price.
3362
+ * @returns The ceil-rounded maker-side assets.
3363
+ */
3364
+ function unitsToCeilAssets(units, price) {
3365
+ return (units * price + WAD$1 - 1n) / WAD$1;
3366
+ }
3367
+ /**
3368
+ * Max-safe inverse for floor surfaces.
3369
+ * It returns the greatest units value whose floor-rounded maker assets stay within `assets`.
3370
+ * @param assets - The maker-side asset cap.
3371
+ * @param price - The raw tick price.
3372
+ * @returns The greatest safe obligation-units amount under `floor(units * price / WAD) <= assets`.
3373
+ */
3374
+ function floorAssetsToUnits(assets, price) {
3375
+ return ((assets + 1n) * WAD$1 - 1n) / price;
3376
+ }
3377
+ /**
3378
+ * Max-safe inverse for ceil surfaces.
3379
+ * It returns the greatest units value whose ceil-rounded maker assets stay within `assets`.
3380
+ * @param assets - The maker-side asset cap.
3381
+ * @param price - The raw tick price.
3382
+ * @returns The greatest safe obligation-units amount under `ceil(units * price / WAD) <= assets`.
3383
+ */
3384
+ function ceilAssetsToUnits(assets, price) {
3385
+ return assets * WAD$1 / price;
3336
3386
  }
3337
3387
  var InvalidTickError = class extends BaseError {
3338
3388
  name = "Tick.InvalidTickError";
@@ -3814,7 +3864,7 @@ const DEFAULT_BATCH_SIZE = 1500;
3814
3864
 
3815
3865
  //#endregion
3816
3866
  //#region src/database/drizzle/VERSION.ts
3817
- const VERSION = "router_v1.15";
3867
+ const VERSION = "router_v1.16";
3818
3868
 
3819
3869
  //#endregion
3820
3870
  //#region src/database/drizzle/schema.ts
@@ -3962,7 +4012,7 @@ const oracles = s.table(EnumTableName.ORACLES, {
3962
4012
  const offers = s.table(EnumTableName.OFFERS, {
3963
4013
  hash: varchar("hash", { length: 66 }).notNull(),
3964
4014
  obligationId: varchar("obligation_id", { length: 66 }).notNull().references(() => obligationIdKeys.obligationId, { onDelete: "cascade" }),
3965
- obligationUnits: numeric("obligation_units", {
4015
+ maxUnits: numeric("max_units", {
3966
4016
  precision: 78,
3967
4017
  scale: 0
3968
4018
  }).notNull().default("0"),
@@ -4003,8 +4053,8 @@ const offers = s.table(EnumTableName.OFFERS, {
4003
4053
  index("offers_obligation_side_tick_idx").on(table.obligationId, table.buy, table.tick),
4004
4054
  index("offers_obligation_active_tick_idx").on(table.obligationId, table.buy, table.expiry, table.maturity, table.start, table.tick),
4005
4055
  index("offers_group_maker_idx").on(table.groupMaker, table.hash, table.obligationId),
4006
- index("offers_group_winner_expr_idx").on(table.groupChainId, table.groupMaker, table.group, table.obligationId, table.buy, sql`(CASE WHEN "buy" THEN -"tick" ELSE "tick" END)`, table.blockNumber, sql`"obligation_units" DESC`, table.hash),
4007
- index("offers_book_winners_covering_idx").on(table.obligationId, table.buy, table.groupChainId, table.groupMaker, table.group, sql`(CASE WHEN "buy" THEN -"tick" ELSE "tick" END)`, table.blockNumber, sql`"obligation_units" DESC`, table.hash)
4056
+ index("offers_group_winner_expr_idx").on(table.groupChainId, table.groupMaker, table.group, table.obligationId, table.buy, sql`(CASE WHEN "buy" THEN -"tick" ELSE "tick" END)`, table.blockNumber, sql`"max_units" DESC`, table.hash),
4057
+ index("offers_book_winners_covering_idx").on(table.obligationId, table.buy, table.groupChainId, table.groupMaker, table.group, sql`(CASE WHEN "buy" THEN -"tick" ELSE "tick" END)`, table.blockNumber, sql`"max_units" DESC`, table.hash)
4008
4058
  ]);
4009
4059
  const offersCallbacks = s.table(EnumTableName.OFFERS_CALLBACKS, {
4010
4060
  offerHash: varchar("offer_hash", { length: 66 }).notNull(),
@@ -4312,7 +4362,7 @@ function compositeKey(g) {
4312
4362
  function create$30(db) {
4313
4363
  return {
4314
4364
  create: async (groups$3) => {
4315
- if (groups$3.length === 0) return;
4365
+ if (groups$3.length === 0) return 0;
4316
4366
  const rows = groups$3.map((group) => ({
4317
4367
  chainId: group.chainId,
4318
4368
  maker: group.maker.toLowerCase(),
@@ -4320,7 +4370,12 @@ function create$30(db) {
4320
4370
  consumed: (group.consumed ?? 0n).toString(),
4321
4371
  blockNumber: group.blockNumber
4322
4372
  }));
4323
- for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE)) await db.insert(groups).values(batch).onConflictDoNothing();
4373
+ let insertedCount = 0;
4374
+ for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE)) {
4375
+ const inserted = await db.insert(groups).values(batch).onConflictDoNothing().returning();
4376
+ insertedCount += inserted.length;
4377
+ }
4378
+ return insertedCount;
4324
4379
  },
4325
4380
  exists: async (groups$4) => {
4326
4381
  if (groups$4.length === 0) return [];
@@ -4417,16 +4472,16 @@ const maxCollaterals = ({ max }) => single("max_collaterals", `Validates that an
4417
4472
  if (offer.collaterals.length > max) return { message: `Offer has ${offer.collaterals.length} collaterals, exceeding the maximum of ${max}` };
4418
4473
  });
4419
4474
  /**
4420
- * A validation rule that checks if the offer's obligationUnits is non-zero.
4475
+ * A validation rule that checks if the offer's maxUnits is non-zero.
4421
4476
  * The contract requires a positive amount; this rule rejects early.
4422
4477
  * @returns The issue that was found. If the offer is valid, this will be undefined.
4423
4478
  */
4424
- const amountNonZero = () => single("amount_non_zero", "Validates that obligationUnits is non-zero", (offer) => {
4425
- if (offer.obligationUnits === 0n) return { message: "obligationUnits must be non-zero" };
4479
+ const amountNonZero = () => single("amount_non_zero", "Validates that maxUnits is non-zero", (offer) => {
4480
+ if (offer.maxUnits === 0n) return { message: "maxUnits must be non-zero" };
4426
4481
  });
4427
4482
  /**
4428
4483
  * A batch validation rule that ensures all offers within the same group are consistent.
4429
- * All offers sharing the same group must have the same loan token, obligationUnits,
4484
+ * All offers sharing the same group must have the same loan token, maxUnits,
4430
4485
  * and side (buy/sell). The contract tracks consumed per group and requires these to match.
4431
4486
  */
4432
4487
  const groupConsistency = () => batch("group_consistency", "Validates that all offers in a group have the same loan token, obligation amounts, and side", (offers) => {
@@ -4443,13 +4498,13 @@ const groupConsistency = () => batch("group_consistency", "Validates that all of
4443
4498
  if (indices.length <= 1) continue;
4444
4499
  const reference = offers[indices[0]];
4445
4500
  const refLoanToken = reference.loanToken.toLowerCase();
4446
- const refUnits = reference.obligationUnits;
4501
+ const refUnits = reference.maxUnits;
4447
4502
  const refBuy = reference.buy;
4448
4503
  for (let j = 1; j < indices.length; j++) {
4449
4504
  const idx = indices[j];
4450
4505
  const offer = offers[idx];
4451
4506
  if (offer.loanToken.toLowerCase() !== refLoanToken) issues.set(idx, { message: `All offers in a group must have the same loan token. Expected ${reference.loanToken}, got ${offer.loanToken}` });
4452
- else if (offer.obligationUnits !== refUnits) issues.set(idx, { message: `All offers in a group must have the same obligationUnits. Expected ${refUnits}, got ${offer.obligationUnits}` });
4507
+ else if (offer.maxUnits !== refUnits) issues.set(idx, { message: `All offers in a group must have the same maxUnits. Expected ${refUnits}, got ${offer.maxUnits}` });
4453
4508
  else if (offer.buy !== refBuy) issues.set(idx, { message: `All offers in a group must be on the same side. Expected ${refBuy ? "buy" : "sell"}, got ${offer.buy ? "buy" : "sell"}` });
4454
4509
  }
4455
4510
  }
@@ -4666,7 +4721,7 @@ function from$5(level) {
4666
4721
  return {
4667
4722
  tick: level.tick,
4668
4723
  price: price.toString(),
4669
- obligation_units: level.obligationUnits.toString(),
4724
+ max_units: level.maxUnits.toString(),
4670
4725
  count: level.count
4671
4726
  };
4672
4727
  }
@@ -4775,7 +4830,7 @@ function from$3(input) {
4775
4830
  },
4776
4831
  buy: input.buy,
4777
4832
  maker: input.maker,
4778
- obligation_units: input.obligationUnits.toString(),
4833
+ max_units: input.maxUnits.toString(),
4779
4834
  start: input.start,
4780
4835
  expiry: input.expiry,
4781
4836
  tick: input.tick,
@@ -4934,7 +4989,7 @@ const offerExample = {
4934
4989
  },
4935
4990
  buy: false,
4936
4991
  maker: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
4937
- obligation_units: "369216000000000000000000",
4992
+ max_units: "369216000000000000000000",
4938
4993
  start: 1761922790,
4939
4994
  expiry: 1761922799,
4940
4995
  tick: 495,
@@ -4977,7 +5032,7 @@ const missingCollectorExample = {
4977
5032
  };
4978
5033
  const validateOfferExample = {
4979
5034
  maker: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
4980
- obligation_units: "369216000000000000000000",
5035
+ max_units: "369216000000000000000000",
4981
5036
  tick: 495,
4982
5037
  maturity: 1761922799,
4983
5038
  rcf_threshold: "0",
@@ -5131,8 +5186,8 @@ __decorate([ApiProperty({
5131
5186
  })], OfferDataResponse.prototype, "maker", void 0);
5132
5187
  __decorate([ApiProperty({
5133
5188
  type: "string",
5134
- example: offerExample.offer.obligation_units
5135
- })], OfferDataResponse.prototype, "obligation_units", void 0);
5189
+ example: offerExample.offer.max_units
5190
+ })], OfferDataResponse.prototype, "max_units", void 0);
5136
5191
  __decorate([ApiProperty({
5137
5192
  type: "number",
5138
5193
  example: offerExample.offer.start
@@ -5386,9 +5441,9 @@ __decorate([ApiProperty({
5386
5441
  })], ValidateOfferRequest.prototype, "maker", void 0);
5387
5442
  __decorate([ApiProperty({
5388
5443
  type: "string",
5389
- example: validateOfferExample.obligation_units,
5444
+ example: validateOfferExample.max_units,
5390
5445
  required: false
5391
- })], ValidateOfferRequest.prototype, "obligation_units", void 0);
5446
+ })], ValidateOfferRequest.prototype, "max_units", void 0);
5392
5447
  __decorate([ApiProperty({
5393
5448
  type: "number",
5394
5449
  example: validateOfferExample.tick,
@@ -5522,7 +5577,7 @@ __decorate([ApiProperty({
5522
5577
  __decorate([ApiProperty({
5523
5578
  type: "string",
5524
5579
  example: "369216000000000000000000"
5525
- })], BookLevelResponse.prototype, "obligation_units", void 0);
5580
+ })], BookLevelResponse.prototype, "max_units", void 0);
5526
5581
  __decorate([ApiProperty({
5527
5582
  type: "number",
5528
5583
  example: 5
@@ -7530,11 +7585,11 @@ function offerBalanceTerm(balance, price, lltv) {
7530
7585
  * @param lotLower - Lot lower bound (in assets).
7531
7586
  * @param lotUpper - Lot upper bound (in assets).
7532
7587
  * @param consumed - Group consumed amount (in obligation units).
7533
- * @param obligationUnits - Offer obligation units.
7588
+ * @param maxUnits - Offer obligation units.
7534
7589
  */
7535
- function lotBalance(offerBalance, offset, positionConsumed, lotLower, lotUpper, consumed, obligationUnits) {
7590
+ function lotBalance(offerBalance, offset, positionConsumed, lotLower, lotUpper, consumed, maxUnits) {
7536
7591
  const lotSize = sql`(COALESCE(${lotUpper}::numeric, 0) - COALESCE(${lotLower}::numeric, 0))`;
7537
- return sql`GREATEST(0, LEAST(COALESCE(${offerBalance}, 0) + COALESCE(${offset}, 0) + COALESCE(${positionConsumed}, 0) - COALESCE(${lotLower}::numeric, 0), ${lotSize} - ${sql`CASE WHEN ${obligationUnits}::numeric > 0 THEN COALESCE(${consumed}::numeric, 0) * ${lotSize} / ${obligationUnits}::numeric ELSE 0 END`}))`;
7592
+ return sql`GREATEST(0, LEAST(COALESCE(${offerBalance}, 0) + COALESCE(${offset}, 0) + COALESCE(${positionConsumed}, 0) - COALESCE(${lotLower}::numeric, 0), ${lotSize} - ${sql`CASE WHEN ${maxUnits}::numeric > 0 THEN COALESCE(${consumed}::numeric, 0) * ${lotSize} / ${maxUnits}::numeric ELSE 0 END`}))`;
7538
7593
  }
7539
7594
  /**
7540
7595
  * Callback contribution: amount contributed by one callback in loan terms.
@@ -7546,17 +7601,23 @@ function contribution(lotBalance, lotLower) {
7546
7601
  return sql`CASE WHEN ${lotLower} IS NULL THEN 0 ELSE ${lotBalance} END`;
7547
7602
  }
7548
7603
  /**
7549
- * Takeable amount in assets.
7550
- * Converts the obligation-unit capacity to assets using the tick price,
7551
- * then compares with available (already in assets from lot balance).
7552
- * @param obligationUnits - Offer obligation units.
7604
+ * Takeable amount in maker-side assets.
7605
+ * Buy offers use the floor maker-asset surface for remaining obligation units.
7606
+ * Sell offers use the ceil maker-asset surface for remaining obligation units.
7607
+ * The result is then compared with available (already in assets from lot balance).
7608
+ * @param maxUnits - Offer obligation units.
7553
7609
  * @param consumed - Group consumed amount (in obligation units).
7554
7610
  * @param available - Total available from callbacks/positions (in assets).
7555
7611
  * @param price - Tick price (from tick_prices CTE).
7612
+ * @param buy - Offer side (`true` for buy, `false` for sell).
7556
7613
  */
7557
- function takeable(obligationUnits, consumed, available, price) {
7614
+ function takeable(maxUnits, consumed, available, price, buy) {
7558
7615
  const WAD_SQL = sql`(10::numeric ^ 18)`;
7559
- return sql`GREATEST(0, LEAST(${sql`(${sql`(${obligationUnits}::numeric * ${price} / ${WAD_SQL})`} - ${sql`(${consumed}::numeric * ${price} / ${WAD_SQL})`})`}, ${available}))`;
7616
+ const remainingUnits = sql`GREATEST(COALESCE(${maxUnits}::numeric, 0) - COALESCE(${consumed}::numeric, 0), 0)`;
7617
+ return sql`GREATEST(0, LEAST(${sql`CASE WHEN ${buy}
7618
+ THEN TRUNC(${remainingUnits} * COALESCE(${price}::numeric, 0) / ${WAD_SQL})
7619
+ ELSE CEIL(${remainingUnits} * COALESCE(${price}::numeric, 0) / ${WAD_SQL})
7620
+ END`}, ${available}))`;
7560
7621
  }
7561
7622
  /** Precomputed tick→price lookup (all 991 ticks). */
7562
7623
  const TICK_PRICES = Array.from({ length: TICK_RANGE + 1 }, (_, i) => [i, tickToPrice(i).toString()]);
@@ -7588,7 +7649,7 @@ function offerAvailabilityCTEs(params) {
7588
7649
  return sql`
7589
7650
  , group_winners AS (
7590
7651
  SELECT DISTINCT ON (o.group_chain_id, o.group_maker, o.group_group, o.obligation_id, o.buy)
7591
- o.group_chain_id, o.group_maker, o.group_group, o.obligation_id, o.buy, o.obligation_units
7652
+ o.group_chain_id, o.group_maker, o.group_group, o.obligation_id, o.buy, o.max_units
7592
7653
  FROM ${offers} o
7593
7654
  LEFT JOIN ${validations} v
7594
7655
  ON v.offer_hash = o.hash
@@ -7605,7 +7666,7 @@ function offerAvailabilityCTEs(params) {
7605
7666
  ORDER BY
7606
7667
  o.group_chain_id, o.group_maker, o.group_group, o.obligation_id, o.buy,
7607
7668
  CASE WHEN o.buy THEN -o.tick ELSE o.tick END ASC,
7608
- o.block_number ASC, o.obligation_units DESC, o.hash ASC
7669
+ o.block_number ASC, o.max_units DESC, o.hash ASC
7609
7670
  ),
7610
7671
  relevant_positions AS (
7611
7672
  SELECT DISTINCT c.position_chain_id, c.position_contract, c.position_user
@@ -7630,8 +7691,8 @@ function offerAvailabilityCTEs(params) {
7630
7691
  COALESCE(
7631
7692
  SUM(
7632
7693
  CASE
7633
- WHEN wo.obligation_units::numeric > 0
7634
- THEN COALESCE(g.consumed::numeric, 0) * (l.upper::numeric - l.lower::numeric) / wo.obligation_units::numeric
7694
+ WHEN wo.max_units::numeric > 0
7695
+ THEN COALESCE(g.consumed::numeric, 0) * (l.upper::numeric - l.lower::numeric) / wo.max_units::numeric
7635
7696
  ELSE 0
7636
7697
  END
7637
7698
  ) OVER (
@@ -7678,14 +7739,14 @@ function offerAvailabilityCTEs(params) {
7678
7739
  src.obligation_id,
7679
7740
  src.group_group,
7680
7741
  src.consumed,
7681
- src.obligation_units,
7742
+ src.max_units,
7682
7743
  c.id AS callback_id,
7683
7744
  c.position_chain_id,
7684
7745
  c.position_contract,
7685
7746
  c.position_user,
7686
7747
  l.lower AS lot_lower,
7687
7748
  l.upper AS lot_upper,
7688
- ${lotBalance(sql`vb.offer_balance`, sql`pos_offsets.total_offset`, sql`pc.consumed`, sql`l.lower`, sql`l.upper`, sql`src.consumed`, sql`src.obligation_units`)} AS lot_balance
7749
+ ${lotBalance(sql`vb.offer_balance`, sql`pos_offsets.total_offset`, sql`pc.consumed`, sql`l.lower`, sql`l.upper`, sql`src.consumed`, sql`src.max_units`)} AS lot_balance
7689
7750
  FROM ${source} src
7690
7751
  LEFT JOIN ${offersCallbacks} oc
7691
7752
  ON oc.offer_hash = src.hash
@@ -7753,7 +7814,7 @@ function offerTakeabilityCTEs(params) {
7753
7814
  offer_takeable AS (
7754
7815
  SELECT src.hash, src.obligation_id,
7755
7816
  COALESCE(oa.available, 0) AS available,
7756
- ${takeable(sql`src.obligation_units`, sql`src.consumed`, sql`COALESCE(oa.available, 0)`, sql`tp.price`)} AS takeable
7817
+ ${takeable(sql`src.max_units`, sql`src.consumed`, sql`COALESCE(oa.available, 0)`, sql`tp.price`, sql`src.buy`)} AS takeable
7757
7818
  FROM ${source} src
7758
7819
  LEFT JOIN offer_available oa ON oa.hash = src.hash AND oa.obligation_id = src.obligation_id
7759
7820
  LEFT JOIN tick_prices tp ON tp.tick = src.tick
@@ -7892,7 +7953,7 @@ function create$27(config) {
7892
7953
  const db = config.db;
7893
7954
  const logger = getLogger();
7894
7955
  const getOffers = async (parameters) => {
7895
- const { side, obligationId, cursor: cursorString } = parameters;
7956
+ const { side, obligationId, cursor: cursorString, requirePositiveTakeableAssets = false } = parameters;
7896
7957
  const requestedLimit = parameters.limit ?? DEFAULT_LIMIT$4;
7897
7958
  const priceSortDirection = side === "sell" ? "asc" : "desc";
7898
7959
  const inputCursor = Cursor.decode(cursorString, logger);
@@ -7913,7 +7974,8 @@ function create$27(config) {
7913
7974
  now,
7914
7975
  priceSortDirection,
7915
7976
  cursor: inputCursor,
7916
- limit: Math.min(requestedLimit, MAX_TOTAL_OFFERS - previouslyReturned)
7977
+ limit: Math.min(requestedLimit, MAX_TOTAL_OFFERS - previouslyReturned),
7978
+ requirePositiveTakeableAssets
7917
7979
  });
7918
7980
  const lastReturnedOffer = rows[rows.length - 1];
7919
7981
  const newTotalReturned = previouslyReturned + rows.length;
@@ -7945,16 +8007,16 @@ function create$27(config) {
7945
8007
  for (const row of rows) {
7946
8008
  const existing = tickMap.get(row.tick);
7947
8009
  if (existing) {
7948
- existing.obligationUnits += row.takeable;
8010
+ existing.maxUnits += row.takeable;
7949
8011
  existing.count += 1;
7950
8012
  } else tickMap.set(row.tick, {
7951
- obligationUnits: row.takeable,
8013
+ maxUnits: row.takeable,
7952
8014
  count: 1
7953
8015
  });
7954
8016
  }
7955
8017
  const levels = Array.from(tickMap.entries()).map(([tick, level]) => ({
7956
8018
  tick,
7957
- obligationUnits: level.obligationUnits,
8019
+ maxUnits: level.maxUnits,
7958
8020
  count: level.count
7959
8021
  }));
7960
8022
  levels.sort((a, b) => tickSortDirection === "asc" ? a.tick - b.tick : b.tick - a.tick);
@@ -7971,15 +8033,15 @@ function create$27(config) {
7971
8033
  }
7972
8034
  /** Get offers with computed takeable based on lot balance. */
7973
8035
  async function _getOffers(db, params) {
7974
- const { obligationId, side, now, priceSortDirection, cursor, limit } = params;
8036
+ const { obligationId, side, now, priceSortDirection, cursor, limit, requirePositiveTakeableAssets } = params;
7975
8037
  const raw = await db.execute(sql`
7976
8038
  WITH collats AS MATERIALIZED (
7977
8039
  SELECT oia.obligation_id,
7978
8040
  COALESCE(jsonb_agg(jsonb_build_object(
7979
8041
  'asset', oc.asset,
7980
8042
  'oracle', oc.oracle_address,
7981
- 'lltv', oc.lltv,
7982
- 'maxLif', oc.max_lif
8043
+ 'lltv', oc.lltv::text,
8044
+ 'maxLif', oc.max_lif::text
7983
8045
  ) ORDER BY oc.asset), '[]'::jsonb) AS collaterals
7984
8046
  FROM ${obligationIdKeys} oia
7985
8047
  JOIN ${obligationCollateralsV2} oc
@@ -8004,7 +8066,7 @@ async function _getOffers(db, params) {
8004
8066
  AND (s.code IS NULL OR s.code = ${Status.VALID})
8005
8067
  ORDER BY
8006
8068
  o.group_chain_id, o.group_maker, o."group_group",
8007
- CASE WHEN o.buy THEN -o.tick ELSE o.tick END ASC, o.block_number ASC, o.obligation_units DESC, o.hash ASC
8069
+ CASE WHEN o.buy THEN -o.tick ELSE o.tick END ASC, o.block_number ASC, o.max_units DESC, o.hash ASC
8008
8070
  ),
8009
8071
  enriched AS (
8010
8072
  SELECT
@@ -8013,7 +8075,7 @@ async function _getOffers(db, params) {
8013
8075
  CASE WHEN ${priceSortDirection === "asc" ? sql`TRUE` : sql`FALSE`}
8014
8076
  THEN w.tick ELSE -w.tick END AS tick_norm,
8015
8077
  w.block_number AS block_norm,
8016
- -w.obligation_units AS obligation_units_norm,
8078
+ -w.max_units AS max_units_norm,
8017
8079
  w.hash AS hash_norm
8018
8080
  FROM winners w
8019
8081
  JOIN ${groups} g
@@ -8034,24 +8096,34 @@ async function _getOffers(db, params) {
8034
8096
  SELECT e.*, COALESCE(ot.available, 0) AS available, ot.takeable
8035
8097
  FROM enriched e
8036
8098
  JOIN offer_takeable ot ON ot.hash = e.hash AND ot.obligation_id = e.obligation_id
8037
- WHERE ot.takeable > 0
8099
+ WHERE (
8100
+ ot.takeable > 0
8101
+ OR (
8102
+ COALESCE(e.max_units::numeric, 0) > COALESCE(e.consumed::numeric, 0)
8103
+ AND (
8104
+ e.tick = 0
8105
+ OR (e.buy AND e.tick < ${TICK_RANGE})
8106
+ )
8107
+ )
8108
+ )
8109
+ ${requirePositiveTakeableAssets ? sql`AND TRUNC(COALESCE(ot.takeable, 0)::numeric) > 0` : sql``}
8038
8110
  ${cursor != null ? sql`
8039
- AND (e.tick_norm, e.block_norm, e.obligation_units_norm, e.hash_norm)
8111
+ AND (e.tick_norm, e.block_norm, e.max_units_norm, e.hash_norm)
8040
8112
  > (
8041
8113
  CASE WHEN ${priceSortDirection === "asc" ? sql`TRUE` : sql`FALSE`}
8042
8114
  THEN ${cursor.tick}::integer ELSE -${cursor.tick}::integer END,
8043
8115
  ${cursor.blockNumber},
8044
- -${cursor.obligationUnits}::numeric,
8116
+ -${cursor.maxUnits}::numeric,
8045
8117
  ${cursor.hash}
8046
8118
  )` : sql``}
8047
- ORDER BY e.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`}, e.block_number ASC, e.obligation_units DESC, e.hash ASC
8119
+ ORDER BY e.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`}, e.block_number ASC, e.max_units DESC, e.hash ASC
8048
8120
  LIMIT ${limit}
8049
8121
  )
8050
8122
  SELECT
8051
8123
  p.hash,
8052
8124
  p.obligation_id,
8053
8125
  p.group_maker,
8054
- p.obligation_units,
8126
+ p.max_units,
8055
8127
  p.consumed,
8056
8128
  p.tick,
8057
8129
  p.maturity,
@@ -8081,17 +8153,20 @@ async function _getOffers(db, params) {
8081
8153
  ORDER BY
8082
8154
  p.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`},
8083
8155
  p.block_number ASC,
8084
- p.obligation_units DESC,
8156
+ p.max_units DESC,
8085
8157
  p.hash ASC;
8086
8158
  `);
8087
8159
  return {
8088
8160
  rows: raw.rows.map((row) => {
8089
8161
  const receiverIfMakerIsSeller = (row.receiver_if_maker_is_seller ?? row.group_maker).toLowerCase();
8162
+ const maxUnits = BigInt(row.max_units ?? 0);
8163
+ const consumed = BigInt(row.consumed ?? 0);
8164
+ const remainingUnits = maxUnits > consumed ? maxUnits - consumed : 0n;
8090
8165
  return {
8091
8166
  hash: row.hash,
8092
8167
  obligationId: row.obligation_id,
8093
8168
  maker: row.group_maker,
8094
- obligationUnits: BigInt(row.obligation_units ?? 0),
8169
+ maxUnits,
8095
8170
  tick: row.tick,
8096
8171
  maturity: row.maturity,
8097
8172
  expiry: row.expiry,
@@ -8106,7 +8181,7 @@ async function _getOffers(db, params) {
8106
8181
  asset: c.asset,
8107
8182
  oracle: c.oracle,
8108
8183
  lltv: BigInt(c.lltv),
8109
- maxLif: BigInt(c.maxLif ?? 0)
8184
+ maxLif: BigInt(c.maxLif)
8110
8185
  })),
8111
8186
  callback: {
8112
8187
  address: row.callback_address,
@@ -8115,9 +8190,9 @@ async function _getOffers(db, params) {
8115
8190
  receiverIfMakerIsSeller,
8116
8191
  exitOnly: row.exit_only,
8117
8192
  blockNumber: row.block_number,
8118
- consumed: BigInt(row.consumed ?? 0),
8193
+ consumed,
8119
8194
  available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
8120
- takeable: assetsToObligationUnits(BigInt(String(row.takeable).split(".")[0] ?? "0"), row.tick),
8195
+ takeable: assetsToUnits(BigInt(String(row.takeable).split(".")[0] ?? "0"), row.tick, row.buy, remainingUnits),
8121
8196
  root: row.tree_root ? row.tree_root.toLowerCase() : void 0,
8122
8197
  signature: row.root_signature ? row.root_signature.toLowerCase() : void 0,
8123
8198
  proof: row.proof_nodes ? splitProofs(row.proof_nodes) : void 0
@@ -8133,7 +8208,7 @@ let Cursor;
8133
8208
  side,
8134
8209
  tick: row.tick,
8135
8210
  blockNumber: row.blockNumber,
8136
- obligationUnits: row.obligationUnits.toString(),
8211
+ maxUnits: row.maxUnits.toString(),
8137
8212
  hash: row.hash,
8138
8213
  totalReturned,
8139
8214
  now
@@ -8144,7 +8219,7 @@ let Cursor;
8144
8219
  if (cursorString == null) return null;
8145
8220
  try {
8146
8221
  const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
8147
- if ((v?.side === "buy" || v?.side === "sell") && typeof v?.tick === "number" && Number.isInteger(v.tick) && typeof v?.blockNumber === "number" && Number.isInteger(v.blockNumber) && typeof v?.obligationUnits === "string" && /^-?\d+$/.test(v.obligationUnits) && isHex(v?.hash) && typeof v?.totalReturned === "number" && Number.isInteger(v.totalReturned) && typeof v?.now === "number" && Number.isInteger(v.now)) return v;
8222
+ if ((v?.side === "buy" || v?.side === "sell") && typeof v?.tick === "number" && Number.isInteger(v.tick) && typeof v?.blockNumber === "number" && Number.isInteger(v.blockNumber) && typeof v?.maxUnits === "string" && /^-?\d+$/.test(v.maxUnits) && isHex(v?.hash) && typeof v?.totalReturned === "number" && Number.isInteger(v.totalReturned) && typeof v?.now === "number" && Number.isInteger(v.now)) return v;
8148
8223
  throw new Error("Invalid cursor");
8149
8224
  } catch {
8150
8225
  logger.error({
@@ -8196,7 +8271,10 @@ let LevelCursor;
8196
8271
  function create$26(db) {
8197
8272
  return {
8198
8273
  upsert: async (inputs) => {
8199
- if (inputs.length === 0) return;
8274
+ if (inputs.length === 0) return {
8275
+ callbacksInserted: 0,
8276
+ offersCallbacksInserted: 0
8277
+ };
8200
8278
  const idCache = /* @__PURE__ */ new Map();
8201
8279
  const seenCallbackIds = /* @__PURE__ */ new Set();
8202
8280
  const callbacksRows = [];
@@ -8236,7 +8314,10 @@ function create$26(db) {
8236
8314
  });
8237
8315
  }
8238
8316
  }
8239
- if (offersCallbacksRows.length === 0) return;
8317
+ if (offersCallbacksRows.length === 0) return {
8318
+ callbacksInserted: 0,
8319
+ offersCallbacksInserted: 0
8320
+ };
8240
8321
  const offerHashes = [...new Set(offersCallbacksRows.map((r) => r.offerHash))];
8241
8322
  const existingOffers = await db.select({
8242
8323
  hash: offers.hash,
@@ -8244,10 +8325,25 @@ function create$26(db) {
8244
8325
  }).from(offers).where(inArray(offers.hash, offerHashes));
8245
8326
  const existingKeys = new Set(existingOffers.map((r) => `${String(r.hash).toLowerCase()}:${String(r.obligationId).toLowerCase()}`));
8246
8327
  const filteredOffersCallbacksRows = offersCallbacksRows.filter((r) => existingKeys.has(`${r.offerHash}:${r.obligationId}`));
8247
- if (filteredOffersCallbacksRows.length === 0) return;
8248
- await db.transaction(async (dbTx) => {
8249
- for (const batch of batch$1(callbacksRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(callbacks).values(batch).onConflictDoNothing();
8250
- for (const batch of batch$1(filteredOffersCallbacksRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(offersCallbacks).values(batch).onConflictDoNothing();
8328
+ if (filteredOffersCallbacksRows.length === 0) return {
8329
+ callbacksInserted: 0,
8330
+ offersCallbacksInserted: 0
8331
+ };
8332
+ return await db.transaction(async (dbTx) => {
8333
+ let callbacksInserted = 0;
8334
+ for (const batch of batch$1(callbacksRows, DEFAULT_BATCH_SIZE)) {
8335
+ const inserted = await dbTx.insert(callbacks).values(batch).onConflictDoNothing().returning();
8336
+ callbacksInserted += inserted.length;
8337
+ }
8338
+ let offersCallbacksInserted = 0;
8339
+ for (const batch of batch$1(filteredOffersCallbacksRows, DEFAULT_BATCH_SIZE)) {
8340
+ const inserted = await dbTx.insert(offersCallbacks).values(batch).onConflictDoNothing().returning();
8341
+ offersCallbacksInserted += inserted.length;
8342
+ }
8343
+ return {
8344
+ callbacksInserted,
8345
+ offersCallbacksInserted
8346
+ };
8251
8347
  });
8252
8348
  },
8253
8349
  delete: async ({ offers }) => {
@@ -8263,7 +8359,10 @@ function create$26(db) {
8263
8359
  function create$25(db) {
8264
8360
  return {
8265
8361
  create: async (events) => {
8266
- if (events.length === 0) return;
8362
+ if (events.length === 0) return {
8363
+ groupsInserted: 0,
8364
+ consumedEventsInserted: 0
8365
+ };
8267
8366
  const groups$2 = /* @__PURE__ */ new Map();
8268
8367
  for (const event of events) {
8269
8368
  const groupId = `${event.chainId}-${event.maker}-${event.group}`.toLowerCase();
@@ -8274,7 +8373,7 @@ function create$25(db) {
8274
8373
  blockNumber: event.blockNumber
8275
8374
  });
8276
8375
  }
8277
- await db.transaction(async (dbTx) => {
8376
+ return await db.transaction(async (dbTx) => {
8278
8377
  const groupsRows = Array.from(groups$2.values()).map((group) => ({
8279
8378
  chainId: group.chainId,
8280
8379
  maker: group.maker.toLowerCase(),
@@ -8282,7 +8381,11 @@ function create$25(db) {
8282
8381
  consumed: "0",
8283
8382
  blockNumber: group.blockNumber
8284
8383
  }));
8285
- for (const batch of batch$1(groupsRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(groups).values(batch).onConflictDoNothing();
8384
+ let groupsInserted = 0;
8385
+ for (const batch of batch$1(groupsRows, DEFAULT_BATCH_SIZE)) {
8386
+ const inserted = await dbTx.insert(groups).values(batch).onConflictDoNothing().returning();
8387
+ groupsInserted += inserted.length;
8388
+ }
8286
8389
  const eventsRows = events.map((event) => ({
8287
8390
  eventId: event.id,
8288
8391
  chainId: event.chainId,
@@ -8291,7 +8394,15 @@ function create$25(db) {
8291
8394
  amount: event.amount.toString(),
8292
8395
  blockNumber: event.blockNumber
8293
8396
  }));
8294
- for (const batch of batch$1(eventsRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(consumedEvents).values(batch).onConflictDoNothing();
8397
+ let consumedEventsInserted = 0;
8398
+ for (const batch of batch$1(eventsRows, DEFAULT_BATCH_SIZE)) {
8399
+ const inserted = await dbTx.insert(consumedEvents).values(batch).onConflictDoNothing().returning();
8400
+ consumedEventsInserted += inserted.length;
8401
+ }
8402
+ return {
8403
+ groupsInserted,
8404
+ consumedEventsInserted
8405
+ };
8295
8406
  });
8296
8407
  },
8297
8408
  delete: async (parameters) => {
@@ -8324,7 +8435,10 @@ function create$24(db) {
8324
8435
  }));
8325
8436
  },
8326
8437
  create: async (parameters) => {
8327
- if (parameters.length === 0) return;
8438
+ if (parameters.length === 0) return {
8439
+ lotsPositionsInserted: 0,
8440
+ lotsInserted: 0
8441
+ };
8328
8442
  const lotsByKey = /* @__PURE__ */ new Map();
8329
8443
  for (const offer of parameters) {
8330
8444
  const key = `${offer.positionChainId}-${offer.positionContract}-${offer.positionUser}-${offer.group}-${offer.obligationId}`.toLowerCase();
@@ -8342,7 +8456,10 @@ function create$24(db) {
8342
8456
  const groupKey = `${offer.positionChainId}-${offer.positionUser.toLowerCase()}-${offer.group.toLowerCase()}`;
8343
8457
  if (!existingGroupKeys.has(groupKey)) lotsByKey.delete(key);
8344
8458
  }
8345
- if (lotsByKey.size === 0) return;
8459
+ if (lotsByKey.size === 0) return {
8460
+ lotsPositionsInserted: 0,
8461
+ lotsInserted: 0
8462
+ };
8346
8463
  const positionsByKey = /* @__PURE__ */ new Map();
8347
8464
  for (const offer of lotsByKey.values()) {
8348
8465
  const posKey = `${offer.positionChainId}-${offer.positionContract}-${offer.positionUser}`.toLowerCase();
@@ -8353,12 +8470,17 @@ function create$24(db) {
8353
8470
  positionTypeId: offer.positionTypeId
8354
8471
  });
8355
8472
  }
8356
- for (const row of positionsByKey.values()) await db.insert(lotsPositions).values(row).onConflictDoNothing();
8473
+ let lotsPositionsInserted = 0;
8474
+ for (const row of positionsByKey.values()) {
8475
+ const inserted = await db.insert(lotsPositions).values(row).onConflictDoNothing().returning();
8476
+ lotsPositionsInserted += inserted.length;
8477
+ }
8478
+ let lotsInserted = 0;
8357
8479
  for (const offer of lotsByKey.values()) if ((await db.select().from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase()), eq(lots.group, offer.group.toLowerCase()), eq(lots.obligationId, offer.obligationId.toLowerCase()))).limit(1)).length === 0) {
8358
8480
  const maxUpperResult = await db.select({ maxUpper: sql`COALESCE(MAX(${lots.upper}::numeric), 0)` }).from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase()), eq(lots.obligationId, offer.obligationId.toLowerCase())));
8359
8481
  const newLower = BigInt(maxUpperResult[0]?.maxUpper ?? "0");
8360
8482
  const newUpper = newLower + offer.size;
8361
- await db.insert(lots).values({
8483
+ const inserted = await db.insert(lots).values({
8362
8484
  chainId: offer.positionChainId,
8363
8485
  user: offer.positionUser.toLowerCase(),
8364
8486
  contract: offer.positionContract.toLowerCase(),
@@ -8366,8 +8488,13 @@ function create$24(db) {
8366
8488
  obligationId: offer.obligationId.toLowerCase(),
8367
8489
  lower: newLower.toString(),
8368
8490
  upper: newUpper.toString()
8369
- });
8491
+ }).returning();
8492
+ lotsInserted += inserted.length;
8370
8493
  }
8494
+ return {
8495
+ lotsPositionsInserted,
8496
+ lotsInserted
8497
+ };
8371
8498
  }
8372
8499
  };
8373
8500
  }
@@ -8389,7 +8516,7 @@ function create$23(db) {
8389
8516
  chainId: obligationIdKeys.chainId,
8390
8517
  morphoV2: obligationIdKeys.morphoV2,
8391
8518
  loanToken: obligations.loanToken,
8392
- collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${obligationCollateralsV2.oracleAddress}, 'lltv', ${obligationCollateralsV2.lltv}, 'maxLif', ${obligationCollateralsV2.maxLif}))`.as("collaterals"),
8519
+ collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${obligationCollateralsV2.oracleAddress}, 'lltv', ${obligationCollateralsV2.lltv}::text, 'maxLif', ${obligationCollateralsV2.maxLif}::text))`.as("collaterals"),
8393
8520
  maturity: obligations.maturity,
8394
8521
  rcfThreshold: obligations.rcfThreshold
8395
8522
  }).from(obligationIdKeys).innerJoin(obligations, eq(obligationIdKeys.obligationKey, obligations.obligationKey)).innerJoin(obligationCollateralsV2, eq(obligations.obligationKey, obligationCollateralsV2.obligationKey)).groupBy(obligationIdKeys.obligationId, obligationIdKeys.chainId, obligationIdKeys.morphoV2, obligations.loanToken, obligations.maturity, obligations.rcfThreshold).where(and(chainIds !== void 0 && chainIds.length > 0 ? inArray(obligationIdKeys.chainId, chainIds) : void 0, gte(obligations.maturity, now$3))).orderBy(asc(obligationIdKeys.obligationId))).map((row) => ({
@@ -8402,7 +8529,7 @@ function create$23(db) {
8402
8529
  asset: collateral.asset,
8403
8530
  oracle: collateral.oracle,
8404
8531
  lltv: from$14(BigInt(collateral.lltv)),
8405
- maxLif: BigInt(collateral.maxLif ?? 0)
8532
+ maxLif: BigInt(collateral.maxLif)
8406
8533
  })),
8407
8534
  maturity: row.maturity,
8408
8535
  rcfThreshold: BigInt(row.rcfThreshold)
@@ -8410,7 +8537,11 @@ function create$23(db) {
8410
8537
  }));
8411
8538
  },
8412
8539
  create: async (obligations$1) => {
8413
- if (obligations$1.length === 0) return;
8540
+ if (obligations$1.length === 0) return {
8541
+ obligationsInserted: 0,
8542
+ obligationIdKeysInserted: 0,
8543
+ obligationCollateralsInserted: 0
8544
+ };
8414
8545
  const obligationsByKey = /* @__PURE__ */ new Map();
8415
8546
  const obligationIdKeysById = /* @__PURE__ */ new Map();
8416
8547
  for (const input of obligations$1) {
@@ -8420,32 +8551,49 @@ function create$23(db) {
8420
8551
  if (!obligationIdKeysById.has(obligationId)) obligationIdKeysById.set(obligationId, input);
8421
8552
  }
8422
8553
  try {
8423
- await db.transaction(async (dbTx) => {
8554
+ return await db.transaction(async (dbTx) => {
8424
8555
  const obligationRows = Array.from(obligationsByKey.entries()).map(([obligationKey, item]) => ({
8425
8556
  obligationKey,
8426
8557
  loanToken: item.loanToken.toLowerCase(),
8427
8558
  maturity: item.maturity,
8428
8559
  rcfThreshold: item.rcfThreshold.toString()
8429
8560
  }));
8430
- for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
8561
+ let obligationsInserted = 0;
8562
+ for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE)) {
8563
+ const inserted = await dbTx.insert(obligations).values(batch).onConflictDoNothing().returning();
8564
+ obligationsInserted += inserted.length;
8565
+ }
8431
8566
  const obligationIdKeyRows = Array.from(obligationIdKeysById.entries()).map(([obligationId, input]) => ({
8432
8567
  obligationId,
8433
8568
  obligationKey: key(input.obligation).toLowerCase(),
8434
8569
  chainId: input.chainId,
8435
8570
  morphoV2: input.morphoV2.toLowerCase()
8436
8571
  }));
8437
- for (const batch of batch$1(obligationIdKeyRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligationIdKeys).values(batch).onConflictDoNothing();
8572
+ let obligationIdKeysInserted = 0;
8573
+ for (const batch of batch$1(obligationIdKeyRows, DEFAULT_BATCH_SIZE)) {
8574
+ const inserted = await dbTx.insert(obligationIdKeys).values(batch).onConflictDoNothing().returning();
8575
+ obligationIdKeysInserted += inserted.length;
8576
+ }
8438
8577
  const collateralRows = Array.from(obligationsByKey.entries()).flatMap(([obligationKey, item]) => {
8439
8578
  return [...item.collaterals].sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())).map((collateral, collateralIndex) => ({
8440
8579
  obligationKey,
8441
8580
  asset: collateral.asset.toLowerCase(),
8442
8581
  oracleAddress: collateral.oracle.toLowerCase(),
8443
8582
  lltv: collateral.lltv,
8444
- maxLif: collateral.maxLif ?? 0n,
8583
+ maxLif: collateral.maxLif,
8445
8584
  collateralIndex
8446
8585
  }));
8447
8586
  });
8448
- for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
8587
+ let obligationCollateralsInserted = 0;
8588
+ for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE)) {
8589
+ const inserted = await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing().returning();
8590
+ obligationCollateralsInserted += inserted.length;
8591
+ }
8592
+ return {
8593
+ obligationsInserted,
8594
+ obligationIdKeysInserted,
8595
+ obligationCollateralsInserted
8596
+ };
8449
8597
  });
8450
8598
  } catch (err) {
8451
8599
  const error = err instanceof Error ? err : new Error(String(err));
@@ -8529,8 +8677,8 @@ function create$22(config) {
8529
8677
  jsonb_build_object(
8530
8678
  'asset', ${obligationCollateralsV2.asset},
8531
8679
  'oracle', ${obligationCollateralsV2.oracleAddress},
8532
- 'lltv', ${obligationCollateralsV2.lltv},
8533
- 'maxLif', ${obligationCollateralsV2.maxLif}
8680
+ 'lltv', ${obligationCollateralsV2.lltv}::text,
8681
+ 'maxLif', ${obligationCollateralsV2.maxLif}::text
8534
8682
  )
8535
8683
  ),
8536
8684
  '[]'::jsonb
@@ -8539,7 +8687,7 @@ function create$22(config) {
8539
8687
  hash: offers.hash,
8540
8688
  obligationId: offers.obligationId,
8541
8689
  maker: offers.groupMaker,
8542
- obligationUnits: offers.obligationUnits,
8690
+ maxUnits: offers.maxUnits,
8543
8691
  tick: offers.tick,
8544
8692
  maturity: offers.maturity,
8545
8693
  rcfThreshold: obligations.rcfThreshold,
@@ -8562,7 +8710,7 @@ function create$22(config) {
8562
8710
  hash: row.hash,
8563
8711
  obligationId: row.obligationId,
8564
8712
  maker: row.maker,
8565
- obligationUnits: BigInt(row.obligationUnits),
8713
+ maxUnits: BigInt(row.maxUnits),
8566
8714
  tick: row.tick,
8567
8715
  maturity: from$15(row.maturity),
8568
8716
  rcfThreshold: BigInt(row.rcfThreshold),
@@ -8577,7 +8725,7 @@ function create$22(config) {
8577
8725
  asset: c.asset,
8578
8726
  oracle: c.oracle,
8579
8727
  lltv: BigInt(c.lltv),
8580
- maxLif: BigInt(c.maxLif ?? 0)
8728
+ maxLif: BigInt(c.maxLif)
8581
8729
  })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
8582
8730
  callback: {
8583
8731
  address: row.callbackAddress,
@@ -8702,25 +8850,30 @@ function create$20(db) {
8702
8850
  }));
8703
8851
  },
8704
8852
  upsert: async (oracles$2) => {
8705
- if (oracles$2.length === 0) return;
8853
+ if (oracles$2.length === 0) return 0;
8706
8854
  const rows = oracles$2.map((o) => ({
8707
8855
  chainId: o.chainId,
8708
8856
  address: o.address.toLowerCase(),
8709
8857
  price: o.price !== null ? o.price.toString() : null,
8710
8858
  blockNumber: o.blockNumber
8711
8859
  }));
8712
- await db.transaction(async (dbTx) => {
8713
- for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE)) await dbTx.insert(oracles).values(batch).onConflictDoUpdate({
8714
- target: [oracles.chainId, oracles.address],
8715
- set: {
8716
- price: sql`COALESCE(EXCLUDED.price, ${oracles.price})`,
8717
- blockNumber: sql`CASE
8860
+ return await db.transaction(async (dbTx) => {
8861
+ let upsertedCount = 0;
8862
+ for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE)) {
8863
+ const upserted = await dbTx.insert(oracles).values(batch).onConflictDoUpdate({
8864
+ target: [oracles.chainId, oracles.address],
8865
+ set: {
8866
+ price: sql`COALESCE(EXCLUDED.price, ${oracles.price})`,
8867
+ blockNumber: sql`CASE
8718
8868
  WHEN EXCLUDED.price IS NULL THEN ${oracles.blockNumber}
8719
8869
  ELSE EXCLUDED.block_number
8720
8870
  END`,
8721
- updatedAt: sql`NOW()`
8722
- }
8723
- });
8871
+ updatedAt: sql`NOW()`
8872
+ }
8873
+ }).returning();
8874
+ upsertedCount += upserted.length;
8875
+ }
8876
+ return upsertedCount;
8724
8877
  });
8725
8878
  }
8726
8879
  };
@@ -8859,8 +9012,8 @@ const create$19 = (db) => {
8859
9012
  l.obligation_id,
8860
9013
  SUM(
8861
9014
  CASE
8862
- WHEN offer_agg.obligation_units > 0
8863
- THEN COALESCE(g.consumed::numeric, 0) * (l.upper::numeric - l.lower::numeric) / offer_agg.obligation_units
9015
+ WHEN offer_agg.max_units > 0
9016
+ THEN COALESCE(g.consumed::numeric, 0) * (l.upper::numeric - l.lower::numeric) / offer_agg.max_units
8864
9017
  ELSE 0
8865
9018
  END
8866
9019
  ) AS consumed
@@ -8875,7 +9028,7 @@ const create$19 = (db) => {
8875
9028
  group_maker,
8876
9029
  "group_group",
8877
9030
  obligation_id,
8878
- MAX(obligation_units::numeric) AS obligation_units
9031
+ MAX(max_units::numeric) AS max_units
8879
9032
  FROM ${offers}
8880
9033
  GROUP BY group_chain_id, group_maker, "group_group", obligation_id
8881
9034
  ) offer_agg
@@ -9318,7 +9471,7 @@ function create$16(parameters) {
9318
9471
  obligationId: obligationIdKeys.obligationId,
9319
9472
  chainId: obligationIdKeys.chainId,
9320
9473
  loanToken: obligations.loanToken,
9321
- collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${obligationCollateralsV2.oracleAddress}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
9474
+ collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${obligationCollateralsV2.oracleAddress}, 'lltv', ${obligationCollateralsV2.lltv}::text, 'maxLif', ${obligationCollateralsV2.maxLif}::text))`.as("collaterals"),
9322
9475
  maturity: obligations.maturity,
9323
9476
  rcfThreshold: obligations.rcfThreshold,
9324
9477
  askTick: sql`MAX(${bestQuotes.askTick})`.as("ask_tick"),
@@ -9351,7 +9504,8 @@ function create$16(parameters) {
9351
9504
  collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$13({
9352
9505
  asset: collateral.asset,
9353
9506
  oracle: collateral.oracle,
9354
- lltv: from$14(BigInt(collateral.lltv))
9507
+ lltv: from$14(BigInt(collateral.lltv)),
9508
+ maxLif: BigInt(collateral.maxLif)
9355
9509
  })),
9356
9510
  maturity: row.maturity,
9357
9511
  rcfThreshold: BigInt(row.rcfThreshold)
@@ -9811,7 +9965,7 @@ async function postMigrate(driver) {
9811
9965
  WHERE o.group_chain_id = t.chain_id
9812
9966
  AND o.group_maker = t.maker
9813
9967
  AND o.group_group = t."group"
9814
- AND o.obligation_units <= t.consumed;
9968
+ AND o.max_units <= t.consumed;
9815
9969
  RETURN NULL;
9816
9970
  END;
9817
9971
  $$;
@@ -9840,7 +9994,7 @@ async function postMigrate(driver) {
9840
9994
  AND g.chain_id = t.group_chain_id
9841
9995
  AND g.maker = t.group_maker
9842
9996
  AND g."group" = t."group"
9843
- AND o.obligation_units <= g.consumed;
9997
+ AND o.max_units <= g.consumed;
9844
9998
  RETURN NULL;
9845
9999
  END;
9846
10000
  $$;`);
@@ -10452,6 +10606,7 @@ const INDEXED_EVENT_PREFIX = "indexer.collector.";
10452
10606
  const INDEXED_EVENT_SUFFIX$1 = "_indexed";
10453
10607
  const LOGS_INGESTED_TOTAL_METRIC = "router_indexer_logs_ingested_total";
10454
10608
  const LOGS_INDEXED_TOTAL_METRIC = "router_indexer_logs_indexed_total";
10609
+ const LOGS_DROPPED_TOTAL_METRIC = "router_indexer_logs_dropped_total";
10455
10610
  const LOG_ENTRIES_TOTAL_METRIC = "router_indexer_log_entries_total";
10456
10611
  const TICK_DURATION_SECONDS_METRIC = "router_indexer_tick_duration_seconds";
10457
10612
  const TICK_PHASE_DURATION_SECONDS_METRIC = "router_indexer_tick_phase_duration_seconds";
@@ -10460,6 +10615,7 @@ const CHAIN_RPC_HEAD_AGE_SECONDS_METRIC = "router_indexer_chain_rpc_head_age_sec
10460
10615
  const CHAIN_RPC_HEAD_UNAVAILABLE_TOTAL_METRIC = "router_indexer_chain_rpc_head_unavailable_total";
10461
10616
  const ENTITIES_COMMITTED_TOTAL_METRIC = "router_indexer_entities_committed_total";
10462
10617
  const ENTITIES_ATTEMPTED_TOTAL_METRIC = "router_indexer_entities_attempted_total";
10618
+ const DB_ROWS_COMMITTED_TOTAL_METRIC = "router_indexer_db_rows_committed_total";
10463
10619
  const ENTITY_COMMIT_LAST_UNIXTIME_METRIC = "router_indexer_entity_commit_last_unixtime";
10464
10620
  const COLLECTOR_COMMIT_LAST_UNIXTIME_METRIC = "router_indexer_collector_commit_last_unixtime";
10465
10621
  const tickDurationBucketsSeconds = [
@@ -10517,6 +10673,11 @@ const chainSyncModeLabels = [
10517
10673
  "unknown"
10518
10674
  ];
10519
10675
  const headToDbLatencyExcludedEvents = new Set([INDEXER_COLLECTOR_TRANSFERS_INDEXED]);
10676
+ const droppedLogEvents = new Set([
10677
+ INDEXER_COLLECTOR_OFFER_TREE_DECODE_FAILED,
10678
+ INDEXER_COLLECTOR_OFFER_TREE_REJECTED,
10679
+ INDEXER_COLLECTOR_LOG_SKIPPED
10680
+ ]);
10520
10681
  const collectorNameByIndexedEvent$1 = new Map([
10521
10682
  [INDEXER_COLLECTOR_OFFERS_INDEXED, "offers"],
10522
10683
  [INDEXER_COLLECTOR_EVENTS_INDEXED, "morpho_v2"],
@@ -10541,6 +10702,16 @@ function create$15() {
10541
10702
  labelNames: ["chain_id", "collector"],
10542
10703
  registers: [registry]
10543
10704
  });
10705
+ const logsDroppedTotal = new Counter({
10706
+ name: LOGS_DROPPED_TOTAL_METRIC,
10707
+ help: "Total collector inputs dropped for stable reasons.",
10708
+ labelNames: [
10709
+ "chain_id",
10710
+ "collector",
10711
+ "reason"
10712
+ ],
10713
+ registers: [registry]
10714
+ });
10544
10715
  const logEntriesTotal = new Counter({
10545
10716
  name: LOG_ENTRIES_TOTAL_METRIC,
10546
10717
  help: "Total indexer log entries by level.",
@@ -10603,6 +10774,17 @@ function create$15() {
10603
10774
  ],
10604
10775
  registers: [registry]
10605
10776
  });
10777
+ const dbRowsCommittedTotal = new Counter({
10778
+ name: DB_ROWS_COMMITTED_TOTAL_METRIC,
10779
+ help: "Total database rows inserted, upserted, or deleted by collector commits.",
10780
+ labelNames: [
10781
+ "chain_id",
10782
+ "collector",
10783
+ "table",
10784
+ "operation"
10785
+ ],
10786
+ registers: [registry]
10787
+ });
10606
10788
  const entityCommitLastUnixtime = new Gauge({
10607
10789
  name: ENTITY_COMMIT_LAST_UNIXTIME_METRIC,
10608
10790
  help: "Unix timestamp in seconds for the last successful entity commit.",
@@ -10638,8 +10820,11 @@ function create$15() {
10638
10820
  return;
10639
10821
  case "runtime.log": {
10640
10822
  const level = normalizeLogLevel(observation.level);
10641
- if (level === null) return;
10642
- logEntriesTotal.inc({ level }, 1);
10823
+ if (level !== null && observation.emitted !== false) logEntriesTotal.inc({ level }, 1);
10824
+ observeRuntimeLog({
10825
+ observation,
10826
+ logsDroppedTotal
10827
+ });
10643
10828
  return;
10644
10829
  }
10645
10830
  case "commit.applied":
@@ -10647,6 +10832,7 @@ function create$15() {
10647
10832
  observation,
10648
10833
  entitiesCommittedTotal,
10649
10834
  entitiesAttemptedTotal,
10835
+ dbRowsCommittedTotal,
10650
10836
  entityCommitLastUnixtime,
10651
10837
  collectorCommitLastUnixtime
10652
10838
  });
@@ -10693,6 +10879,20 @@ function observeRuntimeEvent(parameters) {
10693
10879
  if (headToDbInsertLatencyMs === null) return;
10694
10880
  parameters.headToDbInsertLatencySeconds.observe(labels, sanitizeDurationSeconds(headToDbInsertLatencyMs / 1e3));
10695
10881
  }
10882
+ function observeRuntimeLog(parameters) {
10883
+ const { observation } = parameters;
10884
+ if (observation.event === void 0 || !droppedLogEvents.has(observation.event)) return;
10885
+ const collector = normalizeLabelValue(observation.fields?.collector);
10886
+ const reason = normalizeLabelValue(observation.fields?.reason);
10887
+ if (collector === null || reason === null) return;
10888
+ const count = parseNonNegativeInt(observation.fields?.count) ?? 1;
10889
+ if (count <= 0) return;
10890
+ parameters.logsDroppedTotal.inc({
10891
+ chain_id: renderChainIdLabel(observation.chainId),
10892
+ collector,
10893
+ reason
10894
+ }, count);
10895
+ }
10696
10896
  function observeCommitApplied(parameters) {
10697
10897
  const { observation } = parameters;
10698
10898
  const chainId = renderChainIdLabel(observation.chainId);
@@ -10726,6 +10926,19 @@ function observeCommitApplied(parameters) {
10726
10926
  entity
10727
10927
  }, committedAtUnixSeconds);
10728
10928
  }
10929
+ for (const dbRow of observation.dbRows) {
10930
+ const collector = normalizeLabelValue(dbRow.collector);
10931
+ const table = normalizeLabelValue(dbRow.table);
10932
+ const operation = normalizeDbRowCommitOperation(dbRow.operation);
10933
+ const count = parseNonNegativeInt(dbRow.count);
10934
+ if (collector === null || table === null || operation === null || count === null || count <= 0) continue;
10935
+ parameters.dbRowsCommittedTotal.inc({
10936
+ chain_id: chainId,
10937
+ collector,
10938
+ table,
10939
+ operation
10940
+ }, count);
10941
+ }
10729
10942
  }
10730
10943
  function shouldObserveHeadToDbInsertLatency(event) {
10731
10944
  return !headToDbLatencyExcludedEvents.has(event);
@@ -10751,6 +10964,10 @@ function normalizeCommitOperation(operation) {
10751
10964
  if (operation === "inserted" || operation === "upserted" || operation === "deleted" || operation === "ignored") return operation;
10752
10965
  return null;
10753
10966
  }
10967
+ function normalizeDbRowCommitOperation(operation) {
10968
+ if (operation === "inserted" || operation === "upserted" || operation === "deleted") return operation;
10969
+ return null;
10970
+ }
10754
10971
  function normalizeLabelValue(value) {
10755
10972
  if (typeof value !== "string") return null;
10756
10973
  const candidate = value.trim();
@@ -10843,7 +11060,7 @@ function create$14(parameters) {
10843
11060
  });
10844
11061
  }
10845
11062
  },
10846
- onCommitApplied: ({ collectors, stats, committedAtUnixMs }) => {
11063
+ onCommitApplied: ({ collectors, stats, dbRows, committedAtUnixMs }) => {
10847
11064
  if (sinks.length === 0) return;
10848
11065
  try {
10849
11066
  emitObservation(sinks, {
@@ -10851,6 +11068,7 @@ function create$14(parameters) {
10851
11068
  chainId,
10852
11069
  collectors,
10853
11070
  stats,
11071
+ dbRows,
10854
11072
  committedAtUnixMs
10855
11073
  });
10856
11074
  } catch (error) {
@@ -10906,12 +11124,14 @@ function createObservedLogger(parameters) {
10906
11124
  const wrap = (level, emit) => {
10907
11125
  const enabled = isLogFnEnabled(emit);
10908
11126
  return (entry) => {
10909
- if (enabled && isIndexerComponent(entry.component)) try {
11127
+ if (isIndexerComponent(entry.component) && (enabled || isStructuredCollectorDropLog(entry))) try {
10910
11128
  const chainId = parseChainId(entry.chain_id);
10911
11129
  if (chainId !== null) parameters.onLogEntry({
10912
11130
  type: "runtime.log",
10913
11131
  chainId,
10914
- level
11132
+ level,
11133
+ ...extractRuntimeLogMetadata(entry),
11134
+ ...enabled ? {} : { emitted: false }
10915
11135
  });
10916
11136
  } catch {}
10917
11137
  emit(entry);
@@ -10926,6 +11146,18 @@ function createObservedLogger(parameters) {
10926
11146
  fatal: wrap("fatal", parameters.logger.fatal)
10927
11147
  };
10928
11148
  }
11149
+ function extractRuntimeLogMetadata(entry) {
11150
+ const event = typeof entry.event === "string" ? entry.event : void 0;
11151
+ const fields = Object.fromEntries(Object.entries(entry).filter(([key]) => key !== "msg" && key !== "component" && key !== "chain_id" && key !== "event"));
11152
+ return {
11153
+ ...event === void 0 ? {} : { event },
11154
+ ...Object.keys(fields).length === 0 ? {} : { fields }
11155
+ };
11156
+ }
11157
+ function isStructuredCollectorDropLog(entry) {
11158
+ if (entry.event !== INDEXER_COLLECTOR_OFFER_TREE_DECODE_FAILED && entry.event !== INDEXER_COLLECTOR_OFFER_TREE_REJECTED && entry.event !== INDEXER_COLLECTOR_LOG_SKIPPED) return false;
11159
+ return typeof entry.collector === "string" && typeof entry.reason === "string";
11160
+ }
10929
11161
  function emitObservation(sinks, observation) {
10930
11162
  for (const sink of sinks) sink.observe(observation);
10931
11163
  }
@@ -10999,7 +11231,7 @@ async function getBook(params, db) {
10999
11231
  levels_count: levels.length,
11000
11232
  has_next_cursor: nextCursor != null,
11001
11233
  first_level_tick: firstLevel?.tick ?? null,
11002
- first_level_obligation_units: firstLevel?.obligationUnits.toString() ?? null,
11234
+ first_level_max_units: firstLevel?.maxUnits.toString() ?? null,
11003
11235
  first_level_count: firstLevel?.count ?? null
11004
11236
  });
11005
11237
  return success({
@@ -11418,8 +11650,8 @@ async function getOffersQuery(db, parameters) {
11418
11650
  COALESCE(jsonb_agg(jsonb_build_object(
11419
11651
  'asset', oc.asset,
11420
11652
  'oracle', oc.oracle_address,
11421
- 'lltv', oc.lltv,
11422
- 'maxLif', oc.max_lif
11653
+ 'lltv', oc.lltv::text,
11654
+ 'maxLif', oc.max_lif::text
11423
11655
  ) ORDER BY oc.asset), '[]'::jsonb) AS collaterals
11424
11656
  FROM ${obligationIdKeys} oia
11425
11657
  JOIN ${obligationCollateralsV2} oc
@@ -11436,7 +11668,7 @@ async function getOffersQuery(db, parameters) {
11436
11668
  mo.hash,
11437
11669
  mo.obligation_id,
11438
11670
  mo.group_maker,
11439
- mo.obligation_units,
11671
+ mo.max_units,
11440
11672
  mo.consumed,
11441
11673
  mo.tick,
11442
11674
  mo.maturity,
@@ -11468,11 +11700,14 @@ async function getOffersQuery(db, parameters) {
11468
11700
  ORDER BY mo.hash ASC, mo.obligation_id ASC;
11469
11701
  `)).rows.map((row) => {
11470
11702
  const receiverIfMakerIsSeller = (row.receiver_if_maker_is_seller ?? row.group_maker).toLowerCase();
11703
+ const maxUnits = BigInt(row.max_units ?? 0);
11704
+ const consumed = BigInt(row.consumed ?? 0);
11705
+ const remainingUnits = maxUnits > consumed ? maxUnits - consumed : 0n;
11471
11706
  return {
11472
11707
  hash: row.hash,
11473
11708
  obligationId: row.obligation_id,
11474
11709
  maker: row.group_maker,
11475
- obligationUnits: BigInt(row.obligation_units ?? 0),
11710
+ maxUnits,
11476
11711
  tick: row.tick,
11477
11712
  maturity: row.maturity,
11478
11713
  expiry: row.expiry,
@@ -11487,7 +11722,7 @@ async function getOffersQuery(db, parameters) {
11487
11722
  asset: c.asset,
11488
11723
  oracle: c.oracle,
11489
11724
  lltv: BigInt(c.lltv),
11490
- maxLif: BigInt(c.maxLif ?? 0)
11725
+ maxLif: BigInt(c.maxLif)
11491
11726
  })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
11492
11727
  callback: {
11493
11728
  address: row.callback_address,
@@ -11495,9 +11730,9 @@ async function getOffersQuery(db, parameters) {
11495
11730
  },
11496
11731
  receiverIfMakerIsSeller,
11497
11732
  exitOnly: row.exit_only,
11498
- consumed: BigInt(row.consumed ?? 0),
11733
+ consumed,
11499
11734
  available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
11500
- takeable: assetsToObligationUnits(BigInt(String(row.takeable ?? "0").split(".")[0] ?? "0"), row.tick),
11735
+ takeable: assetsToUnits(BigInt(String(row.takeable ?? "0").split(".")[0] ?? "0"), row.tick, row.buy, remainingUnits),
11501
11736
  blockNumber: row.block_number,
11502
11737
  root: row.tree_root ? row.tree_root.toLowerCase() : void 0,
11503
11738
  signature: row.root_signature ? row.root_signature.toLowerCase() : void 0,
@@ -11541,7 +11776,8 @@ async function getOffers(queryParameters, db) {
11541
11776
  side: query.side,
11542
11777
  obligationId: query.obligation_id,
11543
11778
  cursor: query.cursor,
11544
- limit: query.limit
11779
+ limit: query.limit,
11780
+ requirePositiveTakeableAssets: true
11545
11781
  });
11546
11782
  logger.debug({
11547
11783
  service: "api_controller",
@@ -11693,6 +11929,7 @@ function createApp$1(parameters) {
11693
11929
  origin: "*",
11694
11930
  exposeHeaders: ["x-request-id"]
11695
11931
  }));
11932
+ app.get("/", (c) => c.redirect("/docs"));
11696
11933
  app.get("/v1/offers", async (c) => {
11697
11934
  const { statusCode, body } = await services.getOffers(c.req.query());
11698
11935
  return c.json(body, statusCode);
@@ -12117,6 +12354,10 @@ function* ask(operation) {
12117
12354
 
12118
12355
  //#endregion
12119
12356
  //#region src/indexer/engine/adapter/appliers/applyChainState.ts
12357
+ const emptyOutcome = () => ({
12358
+ stats: [],
12359
+ dbRows: []
12360
+ });
12120
12361
  function isChainUninitializedError$1(error) {
12121
12362
  return error instanceof Error && error.message.includes("Chain state not initialized");
12122
12363
  }
@@ -12165,7 +12406,7 @@ const chainStateAppliers = {
12165
12406
  context.chainMutationState.expectedTipHashForWrite = advanceParameters.tipHash;
12166
12407
  }
12167
12408
  context.chainMutationState.expectedChainState = nextChainState;
12168
- return [];
12409
+ return emptyOutcome();
12169
12410
  },
12170
12411
  collector: async ({ mutation, dbTx, context }) => {
12171
12412
  const collectorName = context.toCollectorName(mutation.name);
@@ -12179,7 +12420,7 @@ const chainStateAppliers = {
12179
12420
  blockNumber: mutation.cursor,
12180
12421
  epoch: BigInt(mutation.epoch)
12181
12422
  });
12182
- return [];
12423
+ return emptyOutcome();
12183
12424
  }
12184
12425
  };
12185
12426
 
@@ -12192,33 +12433,66 @@ const LOTS_PENDING_LINK_TYPE = "offers_lots_missing_group";
12192
12433
 
12193
12434
  //#endregion
12194
12435
  //#region src/indexer/engine/adapter/appliers/applyMorphoV2.ts
12436
+ const outcome$3 = (parameters) => ({
12437
+ stats: parameters.stats ?? [],
12438
+ dbRows: parameters.dbRows ?? []
12439
+ });
12195
12440
  const morphoV2Appliers = {
12196
12441
  transfers_create: async ({ mutation, dbTx }) => {
12197
- await dbTx.transfers.create(mutation.rows);
12198
- return mutation.rows.length > 0 ? [{
12199
- entity: "transfer",
12200
- operation: "inserted",
12201
- count: mutation.rows.length
12202
- }] : [];
12442
+ const insertedTransfers = await dbTx.transfers.create(mutation.rows);
12443
+ return outcome$3({
12444
+ stats: mutation.rows.length > 0 ? [{
12445
+ entity: "transfer",
12446
+ operation: "inserted",
12447
+ count: mutation.rows.length
12448
+ }] : [],
12449
+ dbRows: insertedTransfers > 0 ? [{
12450
+ table: "transfers",
12451
+ operation: "inserted",
12452
+ count: insertedTransfers
12453
+ }] : []
12454
+ });
12203
12455
  },
12204
12456
  consumed_create: async ({ mutation, dbTx }) => {
12205
- await dbTx.consumed.create(mutation.rows);
12206
- return mutation.rows.length > 0 ? [{
12207
- entity: "consumed_event",
12208
- operation: "inserted",
12209
- count: mutation.rows.length
12210
- }] : [];
12457
+ const inserted = await dbTx.consumed.create(mutation.rows);
12458
+ return outcome$3({
12459
+ stats: mutation.rows.length > 0 ? [{
12460
+ entity: "consumed_event",
12461
+ operation: "inserted",
12462
+ count: mutation.rows.length
12463
+ }] : [],
12464
+ dbRows: [...inserted.groupsInserted > 0 ? [{
12465
+ table: "groups",
12466
+ operation: "inserted",
12467
+ count: inserted.groupsInserted
12468
+ }] : [], ...inserted.consumedEventsInserted > 0 ? [{
12469
+ table: "consumed_events",
12470
+ operation: "inserted",
12471
+ count: inserted.consumedEventsInserted
12472
+ }] : []]
12473
+ });
12211
12474
  },
12212
12475
  seizures_record: async ({ mutation, dbTx, context }) => {
12213
- await context.pendingStore.applySeizureFacts({
12476
+ const materialized = await context.pendingStore.applySeizureFacts({
12214
12477
  dbTx,
12215
12478
  seizureFacts: mutation.rows
12216
12479
  });
12217
- return mutation.rows.length > 0 ? [{
12218
- entity: "seizure_fact",
12219
- operation: "inserted",
12220
- count: mutation.rows.length
12221
- }] : [];
12480
+ return outcome$3({
12481
+ stats: mutation.rows.length > 0 ? [{
12482
+ entity: "seizure_fact",
12483
+ operation: "inserted",
12484
+ count: mutation.rows.length
12485
+ }] : [],
12486
+ dbRows: [...materialized.positionsUpserted > 0 ? [{
12487
+ table: "positions",
12488
+ operation: "upserted",
12489
+ count: materialized.positionsUpserted
12490
+ }] : [], ...materialized.transfersInserted > 0 ? [{
12491
+ table: "transfers",
12492
+ operation: "inserted",
12493
+ count: materialized.transfersInserted
12494
+ }] : []]
12495
+ });
12222
12496
  },
12223
12497
  morpho_v2_rewind: async ({ mutation, dbTx, context }) => {
12224
12498
  const deletedConsumedEvents = await dbTx.consumed.delete({
@@ -12242,17 +12516,35 @@ const morphoV2Appliers = {
12242
12516
  });
12243
12517
  const deletedTransfers = deletedDebtTransfers + deletedCollateralTransfers;
12244
12518
  const stats = [];
12245
- if (deletedConsumedEvents > 0) stats.push({
12246
- entity: "consumed_event",
12247
- operation: "deleted",
12248
- count: deletedConsumedEvents
12249
- });
12250
- if (deletedTransfers > 0) stats.push({
12251
- entity: "transfer",
12252
- operation: "deleted",
12253
- count: deletedTransfers
12519
+ const dbRows = [];
12520
+ if (deletedConsumedEvents > 0) {
12521
+ stats.push({
12522
+ entity: "consumed_event",
12523
+ operation: "deleted",
12524
+ count: deletedConsumedEvents
12525
+ });
12526
+ dbRows.push({
12527
+ table: "consumed_events",
12528
+ operation: "deleted",
12529
+ count: deletedConsumedEvents
12530
+ });
12531
+ }
12532
+ if (deletedTransfers > 0) {
12533
+ stats.push({
12534
+ entity: "transfer",
12535
+ operation: "deleted",
12536
+ count: deletedTransfers
12537
+ });
12538
+ dbRows.push({
12539
+ table: "transfers",
12540
+ operation: "deleted",
12541
+ count: deletedTransfers
12542
+ });
12543
+ }
12544
+ return outcome$3({
12545
+ stats,
12546
+ dbRows
12254
12547
  });
12255
- return stats;
12256
12548
  }
12257
12549
  };
12258
12550
 
@@ -12263,7 +12555,12 @@ const buildGroupReferenceKey = (chainId, maker, group) => `${chainId}:${maker.to
12263
12555
  const buildCallbacksMissingOfferReference = (row) => `${row.offerHash.toLowerCase()}:${row.obligationId.toLowerCase()}`;
12264
12556
  const buildMerklePathMissingOfferReference = (row) => `${row.offerHash.toLowerCase()}:${row.obligationId.toLowerCase()}:${row.treeRoot.toLowerCase()}`;
12265
12557
  const buildLotMissingGroupReference = (row) => `${row.positionChainId}:${row.positionUser.toLowerCase()}:${row.group.toLowerCase()}:${row.obligationId.toLowerCase()}`;
12266
- const stat = (parameters) => parameters.count > 0 ? [parameters] : [];
12558
+ const entityStat = (parameters) => parameters.count > 0 ? [parameters] : [];
12559
+ const dbRowStat = (parameters) => parameters.count > 0 ? [parameters] : [];
12560
+ const outcome$2 = (parameters) => ({
12561
+ stats: parameters.stats ?? [],
12562
+ dbRows: parameters.dbRows ?? []
12563
+ });
12267
12564
  async function selectExistingOfferKeys(parameters) {
12268
12565
  const { context, dbTx, references } = parameters;
12269
12566
  if (references.length === 0) return /* @__PURE__ */ new Set();
@@ -12344,45 +12641,93 @@ async function partitionLotsByGroupExistence(parameters) {
12344
12641
  }
12345
12642
  const offersAppliers = {
12346
12643
  obligations_create: async ({ mutation, dbTx }) => {
12347
- await dbTx.obligations.create(mutation.rows);
12348
- return stat({
12349
- entity: "obligation",
12350
- operation: "inserted",
12351
- count: mutation.rows.length
12644
+ const inserted = await dbTx.obligations.create(mutation.rows);
12645
+ return outcome$2({
12646
+ stats: entityStat({
12647
+ entity: "obligation",
12648
+ operation: "inserted",
12649
+ count: mutation.rows.length
12650
+ }),
12651
+ dbRows: [
12652
+ ...dbRowStat({
12653
+ table: "obligations",
12654
+ operation: "inserted",
12655
+ count: inserted.obligationsInserted
12656
+ }),
12657
+ ...dbRowStat({
12658
+ table: "obligation_id_keys",
12659
+ operation: "inserted",
12660
+ count: inserted.obligationIdKeysInserted
12661
+ }),
12662
+ ...dbRowStat({
12663
+ table: "obligation_collaterals_v2",
12664
+ operation: "inserted",
12665
+ count: inserted.obligationCollateralsInserted
12666
+ })
12667
+ ]
12352
12668
  });
12353
12669
  },
12354
12670
  groups_create: async ({ mutation, dbTx }) => {
12355
- await dbTx.groups.create(mutation.rows);
12356
- return stat({
12357
- entity: "group",
12358
- operation: "inserted",
12359
- count: mutation.rows.length
12671
+ const insertedGroups = await dbTx.groups.create(mutation.rows);
12672
+ return outcome$2({
12673
+ stats: entityStat({
12674
+ entity: "group",
12675
+ operation: "inserted",
12676
+ count: mutation.rows.length
12677
+ }),
12678
+ dbRows: dbRowStat({
12679
+ table: "groups",
12680
+ operation: "inserted",
12681
+ count: insertedGroups
12682
+ })
12360
12683
  });
12361
12684
  },
12362
12685
  offers_create: async ({ mutation, dbTx }) => {
12363
- await dbTx.offers.create(mutation.batches);
12364
- return stat({
12365
- entity: "offer",
12366
- operation: "inserted",
12367
- count: mutation.batches.reduce((total, batch) => total + batch.offers.length, 0)
12686
+ const insertedOffers = await dbTx.offers.create(mutation.batches);
12687
+ return outcome$2({
12688
+ stats: entityStat({
12689
+ entity: "offer",
12690
+ operation: "inserted",
12691
+ count: insertedOffers.length
12692
+ }),
12693
+ dbRows: dbRowStat({
12694
+ table: "offers",
12695
+ operation: "inserted",
12696
+ count: insertedOffers.length
12697
+ })
12368
12698
  });
12369
12699
  },
12370
12700
  offers_rewind: async ({ mutation, dbTx, context }) => {
12371
- return stat({
12372
- entity: "offer",
12373
- operation: "deleted",
12374
- count: await dbTx.offers.delete({
12375
- chainId: context.chainId,
12376
- blockNumberGte: mutation.blockNumberGte
12701
+ const deletedOffers = await dbTx.offers.delete({
12702
+ chainId: context.chainId,
12703
+ blockNumberGte: mutation.blockNumberGte
12704
+ });
12705
+ return outcome$2({
12706
+ stats: entityStat({
12707
+ entity: "offer",
12708
+ operation: "deleted",
12709
+ count: deletedOffers
12710
+ }),
12711
+ dbRows: dbRowStat({
12712
+ table: "offers",
12713
+ operation: "deleted",
12714
+ count: deletedOffers
12377
12715
  })
12378
12716
  });
12379
12717
  },
12380
12718
  trees_upsert: async ({ mutation, dbTx }) => {
12381
- await dbTx.trees.upsert(mutation.rows);
12382
- return stat({
12383
- entity: "tree",
12384
- operation: "upserted",
12385
- count: mutation.rows.length
12719
+ const upsertedRoots = await dbTx.trees.upsert(mutation.rows);
12720
+ return outcome$2({
12721
+ stats: entityStat({
12722
+ entity: "tree",
12723
+ operation: "upserted",
12724
+ count: mutation.rows.length
12725
+ }),
12726
+ dbRows: dbRowStat({
12727
+ table: "trees",
12728
+ operation: "upserted",
12729
+ count: upsertedRoots.length
12730
+ })
12386
12731
  });
12387
12732
  },
12388
12733
  trees_upsert_paths: async ({ mutation, dbTx, context }) => {
@@ -12403,15 +12748,22 @@ const offersAppliers = {
12403
12748
  blockNumber: context.pendingLinkReferenceBlockNumber()
12404
12749
  }))
12405
12750
  });
12406
- return [...stat({
12407
- entity: "tree_path",
12408
- operation: "upserted",
12409
- count: resolvableRows.length
12410
- }), ...stat({
12411
- entity: "tree_path",
12412
- operation: "ignored",
12413
- count: missingRows.length
12414
- })];
12751
+ return outcome$2({
12752
+ stats: [...entityStat({
12753
+ entity: "tree_path",
12754
+ operation: "upserted",
12755
+ count: resolvableRows.length
12756
+ }), ...entityStat({
12757
+ entity: "tree_path",
12758
+ operation: "ignored",
12759
+ count: missingRows.length
12760
+ })],
12761
+ dbRows: dbRowStat({
12762
+ table: "merkle_paths",
12763
+ operation: "upserted",
12764
+ count: resolvableRows.length
12765
+ })
12766
+ });
12415
12767
  },
12416
12768
  callbacks_upsert: async ({ mutation, dbTx, context }) => {
12417
12769
  const { resolvableRows, missingRows } = await partitionCallbacksByOfferExistence({
@@ -12419,7 +12771,10 @@ const offersAppliers = {
12419
12771
  dbTx,
12420
12772
  rows: mutation.rows
12421
12773
  });
12422
- if (resolvableRows.length > 0) await dbTx.callbacks.upsert(resolvableRows);
12774
+ const inserted = resolvableRows.length > 0 ? await dbTx.callbacks.upsert(resolvableRows) : {
12775
+ callbacksInserted: 0,
12776
+ offersCallbacksInserted: 0
12777
+ };
12423
12778
  await context.pendingStore.recordIgnoredLinks({
12424
12779
  dbTx,
12425
12780
  records: missingRows.map((row) => ({
@@ -12431,22 +12786,36 @@ const offersAppliers = {
12431
12786
  blockNumber: context.pendingLinkReferenceBlockNumber()
12432
12787
  }))
12433
12788
  });
12434
- return [...stat({
12435
- entity: "callback",
12436
- operation: "upserted",
12437
- count: resolvableRows.length
12438
- }), ...stat({
12439
- entity: "callback",
12440
- operation: "ignored",
12441
- count: missingRows.length
12442
- })];
12789
+ return outcome$2({
12790
+ stats: [...entityStat({
12791
+ entity: "callback",
12792
+ operation: "upserted",
12793
+ count: resolvableRows.length
12794
+ }), ...entityStat({
12795
+ entity: "callback",
12796
+ operation: "ignored",
12797
+ count: missingRows.length
12798
+ })],
12799
+ dbRows: [...dbRowStat({
12800
+ table: "callbacks",
12801
+ operation: "inserted",
12802
+ count: inserted.callbacksInserted
12803
+ }), ...dbRowStat({
12804
+ table: "offers_callbacks",
12805
+ operation: "inserted",
12806
+ count: inserted.offersCallbacksInserted
12807
+ })]
12808
+ });
12443
12809
  },
12444
12810
  lots_create: async ({ mutation, dbTx, context }) => {
12445
12811
  const { resolvableRows, missingRows } = await partitionLotsByGroupExistence({
12446
12812
  dbTx,
12447
12813
  rows: mutation.rows
12448
12814
  });
12449
- if (resolvableRows.length > 0) await dbTx.lots.create(resolvableRows);
12815
+ const inserted = resolvableRows.length > 0 ? await dbTx.lots.create(resolvableRows) : {
12816
+ lotsPositionsInserted: 0,
12817
+ lotsInserted: 0
12818
+ };
12450
12819
  await context.pendingStore.recordIgnoredLinks({
12451
12820
  dbTx,
12452
12821
  records: missingRows.map((row) => ({
@@ -12458,43 +12827,76 @@ const offersAppliers = {
12458
12827
  blockNumber: context.pendingLinkReferenceBlockNumber()
12459
12828
  }))
12460
12829
  });
12461
- return [...stat({
12462
- entity: "lot",
12463
- operation: "inserted",
12464
- count: resolvableRows.length
12465
- }), ...stat({
12466
- entity: "lot",
12467
- operation: "ignored",
12468
- count: missingRows.length
12469
- })];
12830
+ return outcome$2({
12831
+ stats: [...entityStat({
12832
+ entity: "lot",
12833
+ operation: "inserted",
12834
+ count: resolvableRows.length
12835
+ }), ...entityStat({
12836
+ entity: "lot",
12837
+ operation: "ignored",
12838
+ count: missingRows.length
12839
+ })],
12840
+ dbRows: [...dbRowStat({
12841
+ table: "lots_positions",
12842
+ operation: "inserted",
12843
+ count: inserted.lotsPositionsInserted
12844
+ }), ...dbRowStat({
12845
+ table: "lots",
12846
+ operation: "inserted",
12847
+ count: inserted.lotsInserted
12848
+ })]
12849
+ });
12470
12850
  }
12471
12851
  };
12472
12852
 
12473
12853
  //#endregion
12474
12854
  //#region src/indexer/engine/adapter/appliers/applyOracle.ts
12855
+ const outcome$1 = (parameters) => ({
12856
+ stats: parameters.stats ?? [],
12857
+ dbRows: parameters.dbRows ?? []
12858
+ });
12475
12859
  const oracleApplier = { oracle: async ({ mutation, dbTx, context }) => {
12476
- await dbTx.oracles.upsert(mutation.rows.map((row) => ({
12860
+ const upsertedOracles = await dbTx.oracles.upsert(mutation.rows.map((row) => ({
12477
12861
  chainId: context.chainId,
12478
12862
  ...row,
12479
12863
  address: row.address
12480
12864
  })));
12481
- return mutation.rows.length > 0 ? [{
12482
- entity: "oracle",
12483
- operation: "upserted",
12484
- count: mutation.rows.length
12485
- }] : [];
12865
+ return outcome$1({
12866
+ stats: mutation.rows.length > 0 ? [{
12867
+ entity: "oracle",
12868
+ operation: "upserted",
12869
+ count: mutation.rows.length
12870
+ }] : [],
12871
+ dbRows: upsertedOracles > 0 ? [{
12872
+ table: "oracles",
12873
+ operation: "upserted",
12874
+ count: upsertedOracles
12875
+ }] : []
12876
+ });
12486
12877
  } };
12487
12878
 
12488
12879
  //#endregion
12489
12880
  //#region src/indexer/engine/adapter/appliers/applyPositions.ts
12881
+ const outcome = (parameters) => ({
12882
+ stats: parameters.stats ?? [],
12883
+ dbRows: parameters.dbRows ?? []
12884
+ });
12490
12885
  const positionsAppliers = {
12491
12886
  positions_upsert: async ({ mutation, dbTx }) => {
12492
- await dbTx.positions.upsert(mutation.rows);
12493
- return mutation.rows.length > 0 ? [{
12494
- entity: "position",
12495
- operation: "upserted",
12496
- count: mutation.rows.length
12497
- }] : [];
12887
+ const upsertedPositions = await dbTx.positions.upsert(mutation.rows);
12888
+ return outcome({
12889
+ stats: mutation.rows.length > 0 ? [{
12890
+ entity: "position",
12891
+ operation: "upserted",
12892
+ count: mutation.rows.length
12893
+ }] : [],
12894
+ dbRows: upsertedPositions > 0 ? [{
12895
+ table: "positions",
12896
+ operation: "upserted",
12897
+ count: upsertedPositions
12898
+ }] : []
12899
+ });
12498
12900
  },
12499
12901
  positions_rewind: async ({ mutation, dbTx, context }) => {
12500
12902
  const deletedPositions = await dbTx.positions.setEmptyAfter({
@@ -12502,11 +12904,11 @@ const positionsAppliers = {
12502
12904
  blockNumber: mutation.blockNumberGte,
12503
12905
  type: Type.ERC20
12504
12906
  });
12505
- return deletedPositions > 0 ? [{
12907
+ return outcome({ stats: deletedPositions > 0 ? [{
12506
12908
  entity: "position",
12507
12909
  operation: "deleted",
12508
12910
  count: deletedPositions
12509
- }] : [];
12911
+ }] : [] });
12510
12912
  }
12511
12913
  };
12512
12914
 
@@ -12819,7 +13221,9 @@ async function materializeSeizureFacts(parameters) {
12819
13221
  if (seizureFacts.length === 0) return {
12820
13222
  unresolvedFacts: [],
12821
13223
  transfersCreate: [],
12822
- positionsUpsert: []
13224
+ positionsUpsert: [],
13225
+ transfersInserted: 0,
13226
+ positionsUpserted: 0
12823
13227
  };
12824
13228
  const resolutionMap = await resolveCollateralAssetRefs({
12825
13229
  dbTx,
@@ -12847,9 +13251,11 @@ async function materializeSeizureFacts(parameters) {
12847
13251
  }));
12848
13252
  }
12849
13253
  const positionsUpsert = toCollateralPositionRows(resolvedTransfers);
13254
+ let positionsUpserted = 0;
13255
+ let transfersInserted = 0;
12850
13256
  if (resolvedTransfers.length > 0) {
12851
- await dbTx.positions.upsert(positionsUpsert);
12852
- await dbTx.transfers.create(resolvedTransfers);
13257
+ positionsUpserted = await dbTx.positions.upsert(positionsUpsert);
13258
+ transfersInserted = await dbTx.transfers.create(resolvedTransfers);
12853
13259
  await recomputeBackfilledCollateralBalances({
12854
13260
  dbTx,
12855
13261
  transfers: resolvedTransfers
@@ -12858,7 +13264,9 @@ async function materializeSeizureFacts(parameters) {
12858
13264
  return {
12859
13265
  unresolvedFacts,
12860
13266
  transfersCreate: resolvedTransfers,
12861
- positionsUpsert
13267
+ positionsUpsert,
13268
+ transfersInserted,
13269
+ positionsUpserted
12862
13270
  };
12863
13271
  }
12864
13272
  const toFactsFromPendingRows = (chainId, rows) => rows.map((row) => ({
@@ -13108,15 +13516,20 @@ const createPendingLinkStore = (parameters) => {
13108
13516
  const resolverTypes = resolverEntries.map((resolver) => resolver.type);
13109
13517
  return {
13110
13518
  applySeizureFacts: async ({ dbTx, seizureFacts }) => {
13519
+ const materialized = await materializeSeizureFacts({
13520
+ dbTx,
13521
+ chainId,
13522
+ seizureFacts
13523
+ });
13111
13524
  await recordUnresolvedSeizures({
13112
13525
  dbTx,
13113
13526
  chainId,
13114
- seizureFacts: (await materializeSeizureFacts({
13115
- dbTx,
13116
- chainId,
13117
- seizureFacts
13118
- })).unresolvedFacts
13527
+ seizureFacts: materialized.unresolvedFacts
13119
13528
  });
13529
+ return {
13530
+ positionsUpserted: materialized.positionsUpserted,
13531
+ transfersInserted: materialized.transfersInserted
13532
+ };
13120
13533
  },
13121
13534
  recordIgnoredLinks: async ({ dbTx, records }) => {
13122
13535
  if (records.length === 0) return;
@@ -13465,6 +13878,7 @@ const create$11 = (parameters) => {
13465
13878
  chainMutationState
13466
13879
  };
13467
13880
  const commitAppliedStats = [];
13881
+ const commitAppliedDbRows = [];
13468
13882
  const collectorsInCommit = /* @__PURE__ */ new Set();
13469
13883
  const dispatchMutation = async (parameters) => {
13470
13884
  const applier = mutationAppliers[parameters.mutation.t];
@@ -13477,18 +13891,24 @@ const create$11 = (parameters) => {
13477
13891
  try {
13478
13892
  await db.transaction(async (dbTx) => {
13479
13893
  for (const mutation of tx.muts) {
13480
- const mutationApplyStats = await dispatchMutation({
13894
+ const mutationApplyOutcome = await dispatchMutation({
13481
13895
  mutation,
13482
13896
  dbTx
13483
13897
  });
13484
13898
  if (mutation.collector === void 0) continue;
13485
13899
  collectorsInCommit.add(mutation.collector);
13486
- for (const stat of mutationApplyStats) commitAppliedStats.push({
13900
+ for (const stat of mutationApplyOutcome.stats) commitAppliedStats.push({
13487
13901
  collector: mutation.collector,
13488
13902
  entity: stat.entity,
13489
13903
  operation: stat.operation,
13490
13904
  count: stat.count
13491
13905
  });
13906
+ for (const dbRow of mutationApplyOutcome.dbRows) commitAppliedDbRows.push({
13907
+ collector: mutation.collector,
13908
+ table: dbRow.table,
13909
+ operation: dbRow.operation,
13910
+ count: dbRow.count
13911
+ });
13492
13912
  }
13493
13913
  });
13494
13914
  } catch (error) {
@@ -13503,6 +13923,7 @@ const create$11 = (parameters) => {
13503
13923
  chainId,
13504
13924
  collectors: [...collectorsInCommit],
13505
13925
  stats: commitAppliedStats,
13926
+ dbRows: commitAppliedDbRows,
13506
13927
  onCommitApplied
13507
13928
  });
13508
13929
  const nowBlock = state.chain.checkpoints[state.chain.checkpoints.length - 1]?.n ?? 0;
@@ -13550,19 +13971,21 @@ const create$11 = (parameters) => {
13550
13971
  function emitCommitAppliedObservation(parameters) {
13551
13972
  if (parameters.collectors.length === 0) return;
13552
13973
  const aggregatedStats = aggregateCommitAppliedStats(parameters.stats);
13974
+ const aggregatedDbRows = aggregateCommitAppliedDbRows(parameters.dbRows);
13553
13975
  try {
13554
13976
  parameters.onCommitApplied({
13555
13977
  chainId: parameters.chainId,
13556
13978
  collectors: parameters.collectors,
13557
13979
  committedAtUnixMs: Date.now(),
13558
- stats: aggregatedStats
13980
+ stats: aggregatedStats,
13981
+ dbRows: aggregatedDbRows
13559
13982
  });
13560
13983
  } catch {}
13561
13984
  }
13562
13985
  function aggregateCommitAppliedStats(stats) {
13563
13986
  const aggregatedByKey = /* @__PURE__ */ new Map();
13564
13987
  for (const stat of stats) {
13565
- const count = normalizeMutationApplyStatCount(stat.count);
13988
+ const count = normalizePositiveCount(stat.count);
13566
13989
  if (count === null) continue;
13567
13990
  const key = `${stat.collector}\u0000${stat.entity}\u0000${stat.operation}`;
13568
13991
  const previous = aggregatedByKey.get(key);
@@ -13583,7 +14006,31 @@ function aggregateCommitAppliedStats(stats) {
13583
14006
  return left.operation.localeCompare(right.operation);
13584
14007
  });
13585
14008
  }
13586
- function normalizeMutationApplyStatCount(value) {
14009
+ function aggregateCommitAppliedDbRows(dbRows) {
14010
+ const aggregatedByKey = /* @__PURE__ */ new Map();
14011
+ for (const dbRow of dbRows) {
14012
+ const count = normalizePositiveCount(dbRow.count);
14013
+ if (count === null) continue;
14014
+ const key = `${dbRow.collector}\u0000${dbRow.table}\u0000${dbRow.operation}`;
14015
+ const previous = aggregatedByKey.get(key);
14016
+ if (previous === void 0) {
14017
+ aggregatedByKey.set(key, {
14018
+ collector: dbRow.collector,
14019
+ table: dbRow.table,
14020
+ operation: dbRow.operation,
14021
+ count
14022
+ });
14023
+ continue;
14024
+ }
14025
+ previous.count += count;
14026
+ }
14027
+ return [...aggregatedByKey.values()].sort((left, right) => {
14028
+ if (left.collector !== right.collector) return left.collector.localeCompare(right.collector);
14029
+ if (left.table !== right.table) return left.table.localeCompare(right.table);
14030
+ return left.operation.localeCompare(right.operation);
14031
+ });
14032
+ }
14033
+ function normalizePositiveCount(value) {
13587
14034
  if (!Number.isFinite(value)) return null;
13588
14035
  const normalized = Math.trunc(value);
13589
14036
  return normalized > 0 ? normalized : null;
@@ -13813,7 +14260,7 @@ function processConsumedLogs(parameters) {
13813
14260
  * A debt is modeled as a negative position: borrowing = transfer FROM user TO zeroAddress,
13814
14261
  * repayment = FROM zeroAddress TO user. The contract field is the obligationId.
13815
14262
  *
13816
- * **Take event** — Every take is bilateral when obligationUnits > 0:
14263
+ * **Take event** — Every take is bilateral when units > 0:
13817
14264
  * - Buyer (maker if offerIsBuy, taker otherwise): debt decreases (repaid) → from: 0x0 → to: buyer
13818
14265
  * - Seller (taker if offerIsBuy, maker otherwise): debt increases (borrows) → from: seller → to: 0x0
13819
14266
  *
@@ -13845,14 +14292,14 @@ function processDebtTransfers(parameters) {
13845
14292
  if (eventName === takeEvent.name) {
13846
14293
  const args = rawLog.args;
13847
14294
  const obligationId = args?.id_;
13848
- if (obligationId === void 0 || args?.maker === void 0 || args?.taker === void 0 || args?.offerIsBuy === void 0 || args?.obligationUnits === void 0) {
14295
+ if (obligationId === void 0 || args?.maker === void 0 || args?.taker === void 0 || args?.offerIsBuy === void 0 || args?.units === void 0) {
13849
14296
  logger.debug({
13850
14297
  event: INDEXER_COLLECTOR_LOG_SKIPPED,
13851
14298
  msg: "Skipping Take log because it is missing required args for debt"
13852
14299
  });
13853
14300
  continue;
13854
14301
  }
13855
- if (args.obligationUnits === 0n) continue;
14302
+ if (args.units === 0n) continue;
13856
14303
  const buyer = args.offerIsBuy ? args.maker : args.taker;
13857
14304
  const seller = args.offerIsBuy ? args.taker : args.maker;
13858
14305
  const blockNumber = Number(rawLog.blockNumber);
@@ -13862,7 +14309,7 @@ function processDebtTransfers(parameters) {
13862
14309
  contract: obligationId,
13863
14310
  from: zeroAddress,
13864
14311
  to: buyer,
13865
- value: args.obligationUnits,
14312
+ value: args.units,
13866
14313
  type: Type.DEBT_OF,
13867
14314
  asset: zeroAddress,
13868
14315
  blockNumber
@@ -13873,7 +14320,7 @@ function processDebtTransfers(parameters) {
13873
14320
  contract: obligationId,
13874
14321
  from: seller,
13875
14322
  to: zeroAddress,
13876
- value: args.obligationUnits,
14323
+ value: args.units,
13877
14324
  type: Type.DEBT_OF,
13878
14325
  asset: zeroAddress,
13879
14326
  blockNumber
@@ -13883,21 +14330,21 @@ function processDebtTransfers(parameters) {
13883
14330
  if (eventName === repayEvent.name) {
13884
14331
  const args = rawLog.args;
13885
14332
  const obligationId = args?.id_;
13886
- if (obligationId === void 0 || args?.obligationUnits === void 0 || args?.onBehalf === void 0) {
14333
+ if (obligationId === void 0 || args?.units === void 0 || args?.onBehalf === void 0) {
13887
14334
  logger.debug({
13888
14335
  event: INDEXER_COLLECTOR_LOG_SKIPPED,
13889
14336
  msg: "Skipping Repay log because it is missing required args"
13890
14337
  });
13891
14338
  continue;
13892
14339
  }
13893
- if (args.obligationUnits === 0n) continue;
14340
+ if (args.units === 0n) continue;
13894
14341
  transfers.push(from$7({
13895
14342
  id: `${baseId}-debt-repay`,
13896
14343
  chainId,
13897
14344
  contract: obligationId,
13898
14345
  from: zeroAddress,
13899
14346
  to: args.onBehalf,
13900
- value: args.obligationUnits,
14347
+ value: args.units,
13901
14348
  type: Type.DEBT_OF,
13902
14349
  asset: zeroAddress,
13903
14350
  blockNumber: Number(rawLog.blockNumber)
@@ -14221,6 +14668,15 @@ const processBatchesUntilCursorAdvance = async (stream, cursor, processBatch, op
14221
14668
  const collectorName$7 = "morpho_v2";
14222
14669
  const RESOLVE_CONSUMED_YIELD_EVERY_ITEMS = 256;
14223
14670
  const RESOLVE_CONSUMED_YIELD_EVERY_MS = 8;
14671
+ const MORPHO_DERIVED_LOG_ID_SUFFIXES = [
14672
+ "-debt-buyer",
14673
+ "-debt-seller",
14674
+ "-debt-repay",
14675
+ "-debt-liquidate",
14676
+ "-collateral-supply",
14677
+ "-collateral-withdraw",
14678
+ "-collateral-liquidate"
14679
+ ];
14224
14680
  const runNativeStep$2 = async (parameters) => {
14225
14681
  const { request, client, db, logger } = parameters;
14226
14682
  const consumedEvents = [];
@@ -14228,7 +14684,6 @@ const runNativeStep$2 = async (parameters) => {
14228
14684
  const collateralTransfers = [];
14229
14685
  const seizureFacts = [];
14230
14686
  let logsIngestedCount = 0;
14231
- let logsIndexedCount = 0;
14232
14687
  const streamStep = await processBatchesUntilCursorAdvance(streamLogs({
14233
14688
  client,
14234
14689
  contractAddress: client.chain.custom.morpho.address,
@@ -14253,7 +14708,13 @@ const runNativeStep$2 = async (parameters) => {
14253
14708
  logs,
14254
14709
  strict: false
14255
14710
  });
14256
- logsIndexedCount += parsedLogs.length;
14711
+ const untrackedLogsCount = logs.length - parsedLogs.length;
14712
+ if (untrackedLogsCount > 0) logger.debug({
14713
+ event: INDEXER_COLLECTOR_LOG_SKIPPED,
14714
+ msg: "Skipping logs because they do not match tracked Morpho V2 events",
14715
+ reason: "untracked_event",
14716
+ count: untrackedLogsCount
14717
+ });
14257
14718
  consumedEvents.push(...processConsumedLogs({
14258
14719
  logs: parsedLogs,
14259
14720
  chainId: client.chain.id
@@ -14282,14 +14743,21 @@ const runNativeStep$2 = async (parameters) => {
14282
14743
  logsIndexedCount: 0
14283
14744
  };
14284
14745
  const { nextCursor } = streamStep;
14746
+ const resolvedConsumedEvents = await resolveConsumedEvents({
14747
+ db,
14748
+ consumedEvents,
14749
+ logger
14750
+ });
14751
+ const logsIndexedCount = countIndexedMorphoLogs({
14752
+ consumedEvents: resolvedConsumedEvents,
14753
+ debtTransfers,
14754
+ collateralTransfers,
14755
+ seizureFacts
14756
+ });
14285
14757
  const domainMutations = buildDomainMutations$1({
14286
14758
  debtTransfers,
14287
14759
  collateralTransfers,
14288
- consumedEvents: await resolveConsumedEvents({
14289
- db,
14290
- consumedEvents,
14291
- logger
14292
- }),
14760
+ consumedEvents: resolvedConsumedEvents,
14293
14761
  seizureFacts
14294
14762
  });
14295
14763
  return {
@@ -14325,6 +14793,18 @@ const create$10 = (parameters) => {
14325
14793
  });
14326
14794
  };
14327
14795
  };
14796
+ function countIndexedMorphoLogs(parameters) {
14797
+ const indexedLogIds = /* @__PURE__ */ new Set();
14798
+ for (const consumedEvent of parameters.consumedEvents) indexedLogIds.add(consumedEvent.id);
14799
+ for (const transfer of parameters.debtTransfers) indexedLogIds.add(stripMorphoDerivedLogIdSuffix(transfer.id));
14800
+ for (const transfer of parameters.collateralTransfers) indexedLogIds.add(stripMorphoDerivedLogIdSuffix(transfer.id));
14801
+ for (const seizureFact of parameters.seizureFacts) indexedLogIds.add(stripMorphoDerivedLogIdSuffix(seizureFact.id));
14802
+ return indexedLogIds.size;
14803
+ }
14804
+ function stripMorphoDerivedLogIdSuffix(id) {
14805
+ for (const suffix of MORPHO_DERIVED_LOG_ID_SUFFIXES) if (id.endsWith(suffix)) return id.slice(0, -suffix.length);
14806
+ return id;
14807
+ }
14328
14808
  async function resolveConsumedEvents(parameters) {
14329
14809
  const { db, consumedEvents, logger } = parameters;
14330
14810
  if (consumedEvents.length === 0) return [];
@@ -14580,7 +15060,7 @@ const decodeCallbacks = (parameters) => {
14580
15060
  if (!isEmptyCallback(offer)) continue;
14581
15061
  if (offer.buy) {
14582
15062
  const loanToken = offer.loanToken.toLowerCase();
14583
- const positionTypeId$1 = positionTypeId(Type.ERC20);
15063
+ const positionTypeId$2 = positionTypeId(Type.ERC20);
14584
15064
  positions.push(from$9({
14585
15065
  chainId,
14586
15066
  contract: loanToken,
@@ -14593,10 +15073,10 @@ const decodeCallbacks = (parameters) => {
14593
15073
  positionChainId: chainId,
14594
15074
  positionContract: loanToken,
14595
15075
  positionUser: offer.maker,
14596
- positionTypeId: positionTypeId$1,
15076
+ positionTypeId: positionTypeId$2,
14597
15077
  group: offer.group,
14598
15078
  obligationId,
14599
- size: obligationUnitsToAssets(offer.obligationUnits, offer.tick)
15079
+ size: unitsToAssets(offer.maxUnits, offer.tick, offer.buy)
14600
15080
  });
14601
15081
  callbacks.push({
14602
15082
  offerHash: hash$1(offer),
@@ -14605,22 +15085,22 @@ const decodeCallbacks = (parameters) => {
14605
15085
  chainId,
14606
15086
  contract: loanToken,
14607
15087
  user: offer.maker,
14608
- positionTypeId: positionTypeId$1,
15088
+ positionTypeId: positionTypeId$2,
14609
15089
  type: CallbackType.Empty
14610
15090
  }]
14611
15091
  });
14612
15092
  continue;
14613
15093
  }
14614
15094
  const contract = obligationId;
14615
- const positionTypeId$2 = positionTypeId(Type.COLLATERAL_OF);
15095
+ const positionTypeId$1 = positionTypeId(Type.COLLATERAL_OF);
14616
15096
  lots.push({
14617
15097
  positionChainId: chainId,
14618
15098
  positionContract: contract,
14619
15099
  positionUser: offer.maker,
14620
- positionTypeId: positionTypeId$2,
15100
+ positionTypeId: positionTypeId$1,
14621
15101
  group: offer.group,
14622
15102
  obligationId,
14623
- size: obligationUnitsToAssets(offer.obligationUnits, offer.tick)
15103
+ size: unitsToAssets(offer.maxUnits, offer.tick, offer.buy)
14624
15104
  });
14625
15105
  callbacks.push({
14626
15106
  offerHash: hash$1(offer),
@@ -14629,7 +15109,7 @@ const decodeCallbacks = (parameters) => {
14629
15109
  chainId,
14630
15110
  contract,
14631
15111
  user: offer.maker,
14632
- positionTypeId: positionTypeId$2,
15112
+ positionTypeId: positionTypeId$1,
14633
15113
  type: CallbackType.Empty
14634
15114
  }]
14635
15115
  });
@@ -15269,9 +15749,11 @@ const runNativeStep = async (parameters) => {
15269
15749
  });
15270
15750
  transfers.push(...stepTransfers.transfers);
15271
15751
  logsIndexedCount += stepTransfers.transfers.length;
15272
- for (let index = 0; index < stepTransfers.skippedInvalidLogs; index += 1) logger.debug({
15752
+ if (stepTransfers.skippedInvalidLogs > 0) logger.debug({
15273
15753
  event: INDEXER_COLLECTOR_LOG_SKIPPED,
15274
- msg: "Skipping log because it is missing required fields"
15754
+ msg: "Skipping transfer logs because they are missing required fields",
15755
+ reason: "missing_required_fields",
15756
+ count: stepTransfers.skippedInvalidLogs
15275
15757
  });
15276
15758
  if (shouldContinueStream({
15277
15759
  pendingPositionsCount: pendingNewPositions.length,
@@ -16674,7 +17156,7 @@ const init = (parameters) => {
16674
17156
  maker: item.offer.maker,
16675
17157
  group: item.offer.group,
16676
17158
  tick: item.offer.tick,
16677
- assets: item.offer.obligationUnits.toString(),
17159
+ max_units: item.offer.maxUnits.toString(),
16678
17160
  buy: item.offer.buy
16679
17161
  });
16680
17162
  }
@@ -17997,7 +18479,7 @@ function buildOfferAssociationsFromOffers(parameters) {
17997
18479
  user: offer.maker,
17998
18480
  type: Type.ERC20,
17999
18481
  asset: loanToken,
18000
- balance: obligationUnitsToAssets(offer.obligationUnits, offer.tick),
18482
+ balance: unitsToAssets(offer.maxUnits, offer.tick, true),
18001
18483
  blockNumber
18002
18484
  }));
18003
18485
  callbacks.push({
@@ -18018,7 +18500,7 @@ function buildOfferAssociationsFromOffers(parameters) {
18018
18500
  positionTypeId: erc20TypeId,
18019
18501
  group: offer.group,
18020
18502
  obligationId,
18021
- size: obligationUnitsToAssets(offer.obligationUnits, offer.tick)
18503
+ size: unitsToAssets(offer.maxUnits, offer.tick, offer.buy)
18022
18504
  });
18023
18505
  }
18024
18506
  return {
@@ -18146,7 +18628,8 @@ function createMockOffers(parameters) {
18146
18628
  const collaterals = collateralSelections.map((asset) => from$13({
18147
18629
  asset,
18148
18630
  oracle: allowedOracles[int(allowedOracles.length)],
18149
- lltv
18631
+ lltv,
18632
+ maxLif: 0n
18150
18633
  }));
18151
18634
  if (!chainRegistry.getById(chain.id)) throw new Error(`Missing chain config for id ${chain.id}`);
18152
18635
  return {