@morpho-dev/router 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +28 -18
  2. package/dist/cli.js +3020 -2174
  3. package/dist/cli.js.map +1 -1
  4. package/dist/drizzle/VERSION.ts +3 -0
  5. package/dist/drizzle/drizzle.config.ts +18 -0
  6. package/dist/drizzle/index.ts +2 -0
  7. package/dist/drizzle/{router_v1.4/0000_add_obligation_id.sql → router_v1.5/0000_add_block_number_to_liquidity_graph_and_offer_liquidity_pools_relation.sql} +49 -39
  8. package/dist/drizzle/router_v1.5/0001_create_new_relations_to_prepare_new_liquidity_model.sql +55 -0
  9. package/dist/drizzle/router_v1.5/0002_add_new_offer_status_relation.sql +9 -0
  10. package/dist/drizzle/router_v1.5/0003_insert-status-code.sql +1 -0
  11. package/dist/drizzle/router_v1.5/0004_add_index_for_fast_book_lookup.sql +3 -0
  12. package/dist/drizzle/router_v1.5/0005_add_group_consumed_events_table.sql +12 -0
  13. package/dist/drizzle/router_v1.5/0006_add-trigger-for-consumed-events.sql +58 -0
  14. package/dist/drizzle/router_v1.5/0007_update_index_for_fast_book_lookup.sql +5 -0
  15. package/dist/drizzle/router_v1.5/0008_rename_consumed_events_table.sql +8 -0
  16. package/dist/drizzle/{router_v1.4 → router_v1.5}/meta/0000_snapshot.json +83 -27
  17. package/dist/drizzle/{router_v1.4 → router_v1.5}/meta/0001_snapshot.json +459 -27
  18. package/dist/drizzle/router_v1.5/meta/0002_snapshot.json +1463 -0
  19. package/dist/drizzle/router_v1.5/meta/0003_snapshot.json +1463 -0
  20. package/dist/drizzle/router_v1.5/meta/0004_snapshot.json +1569 -0
  21. package/dist/drizzle/router_v1.5/meta/0005_snapshot.json +1664 -0
  22. package/dist/drizzle/router_v1.5/meta/0006_snapshot.json +1664 -0
  23. package/dist/drizzle/router_v1.5/meta/0007_snapshot.json +1752 -0
  24. package/dist/drizzle/router_v1.5/meta/0008_snapshot.json +1752 -0
  25. package/dist/drizzle/router_v1.5/meta/_journal.json +69 -0
  26. package/dist/drizzle/schema.ts +363 -0
  27. package/dist/index.browser.d.cts +884 -156
  28. package/dist/index.browser.d.ts +884 -156
  29. package/dist/index.browser.js +1433 -1010
  30. package/dist/index.browser.js.map +1 -1
  31. package/dist/index.browser.mjs +1426 -1009
  32. package/dist/index.browser.mjs.map +1 -1
  33. package/dist/index.node.d.cts +2578 -930
  34. package/dist/index.node.d.ts +2578 -930
  35. package/dist/index.node.js +6544 -5459
  36. package/dist/index.node.js.map +1 -1
  37. package/dist/index.node.mjs +6769 -5688
  38. package/dist/index.node.mjs.map +1 -1
  39. package/package.json +17 -13
  40. package/dist/drizzle/router_v1.4/0001_update-primary-key-on-link.sql +0 -3
  41. package/dist/drizzle/router_v1.4/meta/_journal.json +0 -20
@@ -2,12 +2,19 @@
2
2
 
3
3
  var v4 = require('zod/v4');
4
4
  var viem = require('viem');
5
- var zodOpenapi = require('zod-openapi');
5
+ require('reflect-metadata');
6
+ var openapiMetadata = require('openapi-metadata');
7
+ var decorators = require('openapi-metadata/decorators');
8
+ var z9 = require('zod');
9
+ var jsBase64 = require('js-base64');
10
+ var createOpenApiFetchClient = require('openapi-fetch');
6
11
  var actions = require('viem/actions');
7
12
  var chains$1 = require('viem/chains');
8
- var z7 = require('zod');
9
13
  var accounts = require('viem/accounts');
10
- var jsBase64 = require('js-base64');
14
+ var merkleTree = require('@openzeppelin/merkle-tree');
15
+ var pako = require('pako');
16
+
17
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
18
 
12
19
  function _interopNamespace(e) {
13
20
  if (e && e.__esModule) return e;
@@ -27,14 +34,24 @@ function _interopNamespace(e) {
27
34
  return Object.freeze(n);
28
35
  }
29
36
 
30
- var z7__namespace = /*#__PURE__*/_interopNamespace(z7);
37
+ var z9__namespace = /*#__PURE__*/_interopNamespace(z9);
38
+ var createOpenApiFetchClient__default = /*#__PURE__*/_interopDefault(createOpenApiFetchClient);
31
39
 
32
40
  var __defProp = Object.defineProperty;
41
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
33
42
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
34
43
  var __export = (target, all) => {
35
44
  for (var name in all)
36
45
  __defProp(target, name, { get: all[name], enumerable: true });
37
46
  };
47
+ var __decorateClass = (decorators, target, key, kind) => {
48
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
49
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
50
+ if (decorator = decorators[i])
51
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
52
+ if (kind && result) __defProp(target, key, result);
53
+ return result;
54
+ };
38
55
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
39
56
 
40
57
  // src/api/Schema/index.ts
@@ -44,8 +61,11 @@ __export(Schema_exports, {
44
61
  ChainsHealthResponse: () => ChainsHealthResponse,
45
62
  CollectorHealth: () => CollectorHealth,
46
63
  CollectorsHealthResponse: () => CollectorsHealthResponse,
64
+ HealthController: () => HealthController,
47
65
  ObligationResponse: () => ObligationResponse_exports,
66
+ ObligationsController: () => ObligationsController,
48
67
  OfferResponse: () => OfferResponse_exports,
68
+ OffersController: () => OffersController,
49
69
  OpenApi: () => OpenApi,
50
70
  RouterStatusResponse: () => RouterStatusResponse,
51
71
  parse: () => parse,
@@ -80,6 +100,7 @@ __export(ObligationResponse_exports, {
80
100
  var Format_exports = {};
81
101
  __export(Format_exports, {
82
102
  fromSnakeCase: () => fromSnakeCase,
103
+ stringifyBigint: () => stringifyBigint,
83
104
  toSnakeCase: () => toSnakeCase
84
105
  });
85
106
  function toSnakeCase(obj) {
@@ -139,9 +160,621 @@ __export(OfferResponse_exports, {
139
160
  from: () => from2
140
161
  });
141
162
  function from2(offer) {
142
- return toSnakeCase(offer);
163
+ const result = toSnakeCase(offer);
164
+ return { ...result, signature: result.signature ?? null };
165
+ }
166
+ var API_ERROR_CODES = [
167
+ "VALIDATION_ERROR",
168
+ "NOT_FOUND",
169
+ "INTERNAL_SERVER_ERROR",
170
+ "BAD_REQUEST"
171
+ ];
172
+
173
+ // src/api/Schema/openapi.ts
174
+ var timestampExample = "2024-01-01T12:00:00.000Z";
175
+ var offerCursorExample = "eyJvZmZzZXQiOjEwMH0";
176
+ var obligationCursorExample = "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc";
177
+ var offerExample = {
178
+ hash: "0xac4bd8318ec914f89f8af913f162230575b0ac0696a19256bc12138c5cfe1427",
179
+ offering: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
180
+ assets: "369216000000000000000000",
181
+ rate: "2750000000000000000",
182
+ maturity: 1761922799,
183
+ expiry: 1761922799,
184
+ start: 1761922790,
185
+ nonce: "571380",
186
+ buy: false,
187
+ chain_id: "1",
188
+ loan_token: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078",
189
+ collaterals: [
190
+ {
191
+ asset: "0x34Cf890dB685FC536E05652FB41f02090c3fb751",
192
+ oracle: "0x45093658BE7f90B63D7c359e8f408e503c2D9401",
193
+ lltv: "860000000000000000"
194
+ }
195
+ ],
196
+ callback: {
197
+ address: "0x1111111111111111111111111111111111111111",
198
+ data: "0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000034cf890db685fc536e05652fb41f02090c3fb751000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000108e644e3ab01184155270aa92a00000000000",
199
+ gas_limit: "500000"
200
+ },
201
+ signature: "0x1234567890123456789012345678901234567890123456789012345678901234123456789012345678901234567890123456789012345678901234567890123400",
202
+ consumed: "0",
203
+ block_number: 2942933377146801
204
+ };
205
+ var collectorsHealthExample = {
206
+ name: "mempool_offers",
207
+ chain_id: "1",
208
+ block_number: 21345678,
209
+ updated_at: timestampExample,
210
+ lag: 0,
211
+ status: "live"
212
+ };
213
+ var chainsHealthExample = {
214
+ chain_id: "1",
215
+ block_number: 21345678,
216
+ updated_at: timestampExample
217
+ };
218
+ var routerStatusExample = {
219
+ status: "live"
220
+ };
221
+ var Meta = class {
222
+ };
223
+ __decorateClass([
224
+ decorators.ApiProperty({ type: "string", example: timestampExample })
225
+ ], Meta.prototype, "timestamp", 2);
226
+ var SuccessResponse = class {
227
+ };
228
+ __decorateClass([
229
+ decorators.ApiProperty({ type: "string", enum: ["success"] })
230
+ ], SuccessResponse.prototype, "status", 2);
231
+ __decorateClass([
232
+ decorators.ApiProperty({ type: () => Meta })
233
+ ], SuccessResponse.prototype, "meta", 2);
234
+ var ErrorResponse = class {
235
+ };
236
+ __decorateClass([
237
+ decorators.ApiProperty({ type: "string", enum: API_ERROR_CODES, example: "VALIDATION_ERROR" })
238
+ ], ErrorResponse.prototype, "code", 2);
239
+ __decorateClass([
240
+ decorators.ApiProperty({
241
+ type: "string",
242
+ example: "Limit must be greater than 0."
243
+ })
244
+ ], ErrorResponse.prototype, "message", 2);
245
+ __decorateClass([
246
+ decorators.ApiProperty({
247
+ type: "object",
248
+ example: [
249
+ {
250
+ field: "limit",
251
+ issue: "Limit must be greater than 0."
252
+ }
253
+ ]
254
+ })
255
+ ], ErrorResponse.prototype, "details", 2);
256
+ var BadRequestResponse = class {
257
+ };
258
+ __decorateClass([
259
+ decorators.ApiProperty({ type: "string", enum: ["error"] })
260
+ ], BadRequestResponse.prototype, "status", 2);
261
+ __decorateClass([
262
+ decorators.ApiProperty({ type: () => ErrorResponse })
263
+ ], BadRequestResponse.prototype, "error", 2);
264
+ __decorateClass([
265
+ decorators.ApiProperty({ type: () => Meta })
266
+ ], BadRequestResponse.prototype, "meta", 2);
267
+ var CollateralResponse = class {
268
+ };
269
+ __decorateClass([
270
+ decorators.ApiProperty({ type: "string", example: "0x34Cf890dB685FC536E05652FB41f02090c3fb751" })
271
+ ], CollateralResponse.prototype, "asset", 2);
272
+ __decorateClass([
273
+ decorators.ApiProperty({ type: "string", example: "0x45093658BE7f90B63D7c359e8f408e503c2D9401" })
274
+ ], CollateralResponse.prototype, "oracle", 2);
275
+ __decorateClass([
276
+ decorators.ApiProperty({ type: "string", example: "860000000000000000" })
277
+ ], CollateralResponse.prototype, "lltv", 2);
278
+ var AskResponse = class {
279
+ };
280
+ __decorateClass([
281
+ decorators.ApiProperty({ type: "string", example: "1000000000000000000" })
282
+ ], AskResponse.prototype, "rate", 2);
283
+ var BidResponse = class {
284
+ };
285
+ __decorateClass([
286
+ decorators.ApiProperty({ type: "string", example: "1000000000000000000" })
287
+ ], BidResponse.prototype, "rate", 2);
288
+ var OfferCallbackResponse = class {
289
+ };
290
+ __decorateClass([
291
+ decorators.ApiProperty({ type: "string", example: offerExample.callback.address })
292
+ ], OfferCallbackResponse.prototype, "address", 2);
293
+ __decorateClass([
294
+ decorators.ApiProperty({ type: "string", example: offerExample.callback.data })
295
+ ], OfferCallbackResponse.prototype, "data", 2);
296
+ __decorateClass([
297
+ decorators.ApiProperty({ type: "string", example: offerExample.callback.gas_limit })
298
+ ], OfferCallbackResponse.prototype, "gas_limit", 2);
299
+ var OfferListItemResponse = class {
300
+ };
301
+ __decorateClass([
302
+ decorators.ApiProperty({ type: "string", example: offerExample.hash })
303
+ ], OfferListItemResponse.prototype, "hash", 2);
304
+ __decorateClass([
305
+ decorators.ApiProperty({ type: "string", example: offerExample.offering })
306
+ ], OfferListItemResponse.prototype, "offering", 2);
307
+ __decorateClass([
308
+ decorators.ApiProperty({ type: "string", example: offerExample.assets })
309
+ ], OfferListItemResponse.prototype, "assets", 2);
310
+ __decorateClass([
311
+ decorators.ApiProperty({ type: "string", example: offerExample.rate })
312
+ ], OfferListItemResponse.prototype, "rate", 2);
313
+ __decorateClass([
314
+ decorators.ApiProperty({ type: "number", example: offerExample.maturity })
315
+ ], OfferListItemResponse.prototype, "maturity", 2);
316
+ __decorateClass([
317
+ decorators.ApiProperty({ type: "number", example: offerExample.expiry })
318
+ ], OfferListItemResponse.prototype, "expiry", 2);
319
+ __decorateClass([
320
+ decorators.ApiProperty({ type: "number", example: offerExample.start })
321
+ ], OfferListItemResponse.prototype, "start", 2);
322
+ __decorateClass([
323
+ decorators.ApiProperty({ type: "string", example: offerExample.nonce })
324
+ ], OfferListItemResponse.prototype, "nonce", 2);
325
+ __decorateClass([
326
+ decorators.ApiProperty({ type: "boolean", example: offerExample.buy })
327
+ ], OfferListItemResponse.prototype, "buy", 2);
328
+ __decorateClass([
329
+ decorators.ApiProperty({ type: "string", example: offerExample.chain_id })
330
+ ], OfferListItemResponse.prototype, "chain_id", 2);
331
+ __decorateClass([
332
+ decorators.ApiProperty({ type: "string", example: offerExample.loan_token })
333
+ ], OfferListItemResponse.prototype, "loan_token", 2);
334
+ __decorateClass([
335
+ decorators.ApiProperty({ type: () => [CollateralResponse], example: offerExample.collaterals })
336
+ ], OfferListItemResponse.prototype, "collaterals", 2);
337
+ __decorateClass([
338
+ decorators.ApiProperty({ type: () => OfferCallbackResponse, example: offerExample.callback })
339
+ ], OfferListItemResponse.prototype, "callback", 2);
340
+ __decorateClass([
341
+ decorators.ApiProperty({ type: "string", example: offerExample.consumed })
342
+ ], OfferListItemResponse.prototype, "consumed", 2);
343
+ __decorateClass([
344
+ decorators.ApiProperty({ type: "number", example: offerExample.block_number })
345
+ ], OfferListItemResponse.prototype, "block_number", 2);
346
+ __decorateClass([
347
+ decorators.ApiProperty({ type: "string", nullable: true, example: offerExample.signature })
348
+ ], OfferListItemResponse.prototype, "signature", 2);
349
+ var ObligationResponse = class {
350
+ };
351
+ __decorateClass([
352
+ decorators.ApiProperty({
353
+ type: "string",
354
+ example: "0x12590ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9f67"
355
+ })
356
+ ], ObligationResponse.prototype, "id", 2);
357
+ __decorateClass([
358
+ decorators.ApiProperty({ type: "string", example: "1" })
359
+ ], ObligationResponse.prototype, "chain_id", 2);
360
+ __decorateClass([
361
+ decorators.ApiProperty({ type: "string", example: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078" })
362
+ ], ObligationResponse.prototype, "loan_token", 2);
363
+ __decorateClass([
364
+ decorators.ApiProperty({ type: () => [CollateralResponse] })
365
+ ], ObligationResponse.prototype, "collaterals", 2);
366
+ __decorateClass([
367
+ decorators.ApiProperty({ type: "number", example: 1761922800 })
368
+ ], ObligationResponse.prototype, "maturity", 2);
369
+ __decorateClass([
370
+ decorators.ApiProperty({ type: () => AskResponse })
371
+ ], ObligationResponse.prototype, "ask", 2);
372
+ __decorateClass([
373
+ decorators.ApiProperty({ type: () => BidResponse })
374
+ ], ObligationResponse.prototype, "bid", 2);
375
+ var ObligationListResponse = class extends SuccessResponse {
376
+ };
377
+ __decorateClass([
378
+ decorators.ApiProperty({ type: "string", nullable: true, example: obligationCursorExample })
379
+ ], ObligationListResponse.prototype, "cursor", 2);
380
+ __decorateClass([
381
+ decorators.ApiProperty({
382
+ type: () => [ObligationResponse],
383
+ description: "List of obligations with takable offers."
384
+ })
385
+ ], ObligationListResponse.prototype, "data", 2);
386
+ var ObligationSingleSuccessResponse = class extends SuccessResponse {
387
+ };
388
+ __decorateClass([
389
+ decorators.ApiProperty({ type: "string", nullable: true, example: null })
390
+ ], ObligationSingleSuccessResponse.prototype, "cursor", 2);
391
+ __decorateClass([
392
+ decorators.ApiProperty({ type: () => ObligationResponse, description: "Obligation details." })
393
+ ], ObligationSingleSuccessResponse.prototype, "data", 2);
394
+ var OfferListResponse = class extends SuccessResponse {
395
+ };
396
+ __decorateClass([
397
+ decorators.ApiProperty({ type: "string", nullable: true, example: offerCursorExample })
398
+ ], OfferListResponse.prototype, "cursor", 2);
399
+ __decorateClass([
400
+ decorators.ApiProperty({
401
+ type: () => [OfferListItemResponse],
402
+ description: "Offers matching the provided filters.",
403
+ example: [offerExample]
404
+ })
405
+ ], OfferListResponse.prototype, "data", 2);
406
+ var RouterStatusDataResponse = class {
407
+ };
408
+ __decorateClass([
409
+ decorators.ApiProperty({ type: "string", enum: ["live", "syncing"], example: routerStatusExample.status })
410
+ ], RouterStatusDataResponse.prototype, "status", 2);
411
+ var RouterStatusSuccessResponse = class extends SuccessResponse {
412
+ };
413
+ __decorateClass([
414
+ decorators.ApiProperty({
415
+ type: () => RouterStatusDataResponse,
416
+ description: "Aggregated router status.",
417
+ example: routerStatusExample
418
+ })
419
+ ], RouterStatusSuccessResponse.prototype, "data", 2);
420
+ var CollectorHealthResponse = class {
421
+ };
422
+ __decorateClass([
423
+ decorators.ApiProperty({ type: "string", example: collectorsHealthExample.name })
424
+ ], CollectorHealthResponse.prototype, "name", 2);
425
+ __decorateClass([
426
+ decorators.ApiProperty({ type: "string", example: collectorsHealthExample.chain_id })
427
+ ], CollectorHealthResponse.prototype, "chain_id", 2);
428
+ __decorateClass([
429
+ decorators.ApiProperty({ type: "number", nullable: true, example: collectorsHealthExample.block_number })
430
+ ], CollectorHealthResponse.prototype, "block_number", 2);
431
+ __decorateClass([
432
+ decorators.ApiProperty({ type: "string", nullable: true, example: collectorsHealthExample.updated_at })
433
+ ], CollectorHealthResponse.prototype, "updated_at", 2);
434
+ __decorateClass([
435
+ decorators.ApiProperty({ type: "number", nullable: true, example: collectorsHealthExample.lag })
436
+ ], CollectorHealthResponse.prototype, "lag", 2);
437
+ __decorateClass([
438
+ decorators.ApiProperty({
439
+ type: "string",
440
+ enum: ["live", "lagging", "unknown"],
441
+ example: collectorsHealthExample.status
442
+ })
443
+ ], CollectorHealthResponse.prototype, "status", 2);
444
+ var CollectorsHealthSuccessResponse = class extends SuccessResponse {
445
+ };
446
+ __decorateClass([
447
+ decorators.ApiProperty({
448
+ type: () => [CollectorHealthResponse],
449
+ description: "Collectors health details and sync status.",
450
+ example: [collectorsHealthExample]
451
+ })
452
+ ], CollectorsHealthSuccessResponse.prototype, "data", 2);
453
+ var ChainHealthResponse = class {
454
+ };
455
+ __decorateClass([
456
+ decorators.ApiProperty({ type: "string", example: chainsHealthExample.chain_id })
457
+ ], ChainHealthResponse.prototype, "chain_id", 2);
458
+ __decorateClass([
459
+ decorators.ApiProperty({ type: "number", example: chainsHealthExample.block_number })
460
+ ], ChainHealthResponse.prototype, "block_number", 2);
461
+ __decorateClass([
462
+ decorators.ApiProperty({ type: "string", example: chainsHealthExample.updated_at })
463
+ ], ChainHealthResponse.prototype, "updated_at", 2);
464
+ var ChainsHealthSuccessResponse = class extends SuccessResponse {
465
+ };
466
+ __decorateClass([
467
+ decorators.ApiProperty({
468
+ type: () => [ChainHealthResponse],
469
+ description: "Latest processed block per chain.",
470
+ example: [chainsHealthExample]
471
+ })
472
+ ], ChainsHealthSuccessResponse.prototype, "data", 2);
473
+ var OffersController = class {
474
+ async getOffers() {
475
+ }
476
+ };
477
+ __decorateClass([
478
+ decorators.ApiOperation({
479
+ methods: ["get"],
480
+ path: "/v1/offers",
481
+ summary: "List all offers",
482
+ description: "Returns a list of offers for a given obligation and side. Offers are sorted by the best rate (depending on the side), their block number (older offers first) and their assets (bigger offers first)."
483
+ }),
484
+ decorators.ApiQuery({ name: "side", type: "string", required: true, enum: ["buy", "sell"], example: "buy" }),
485
+ decorators.ApiQuery({
486
+ name: "obligation_id",
487
+ type: "string",
488
+ required: true,
489
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234",
490
+ description: "Obligation id used to filter offers."
491
+ }),
492
+ decorators.ApiQuery({
493
+ name: "cursor",
494
+ type: "string",
495
+ example: offerCursorExample,
496
+ description: "Pagination cursor in base64url-encoded format."
497
+ }),
498
+ decorators.ApiQuery({
499
+ name: "limit",
500
+ type: "number",
501
+ example: 10,
502
+ description: "Maximum number of offers to return."
503
+ }),
504
+ decorators.ApiResponse({ status: 200, description: "Success", type: OfferListResponse })
505
+ ], OffersController.prototype, "getOffers", 1);
506
+ OffersController = __decorateClass([
507
+ decorators.ApiTags("Offers"),
508
+ decorators.ApiResponse({ status: 400, description: "Bad Request", type: BadRequestResponse })
509
+ ], OffersController);
510
+ var HealthController = class {
511
+ async getRouterStatus() {
512
+ }
513
+ async getCollectorsHealth() {
514
+ }
515
+ async getChainsHealth() {
516
+ }
517
+ };
518
+ __decorateClass([
519
+ decorators.ApiOperation({
520
+ methods: ["get"],
521
+ path: "/v1/health",
522
+ summary: "Retrieve global health",
523
+ description: "Returns the aggregated status of the router."
524
+ }),
525
+ decorators.ApiResponse({ status: 200, description: "Success", type: RouterStatusSuccessResponse })
526
+ ], HealthController.prototype, "getRouterStatus", 1);
527
+ __decorateClass([
528
+ decorators.ApiOperation({
529
+ methods: ["get"],
530
+ path: "/v1/health/collectors",
531
+ summary: "Retrieve collectors health",
532
+ description: "Returns the latest block numbers processed by collectors and their sync status."
533
+ }),
534
+ decorators.ApiResponse({ status: 200, description: "Success", type: CollectorsHealthSuccessResponse })
535
+ ], HealthController.prototype, "getCollectorsHealth", 1);
536
+ __decorateClass([
537
+ decorators.ApiOperation({
538
+ methods: ["get"],
539
+ path: "/v1/health/chains",
540
+ summary: "Retrieve chains health",
541
+ description: "Returns the latest block that can be processed by collectors for each chain."
542
+ }),
543
+ decorators.ApiResponse({ status: 200, description: "Success", type: ChainsHealthSuccessResponse })
544
+ ], HealthController.prototype, "getChainsHealth", 1);
545
+ HealthController = __decorateClass([
546
+ decorators.ApiTags("Health")
547
+ ], HealthController);
548
+ var ObligationsController = class {
549
+ async getObligations() {
550
+ }
551
+ async getObligation() {
552
+ }
553
+ };
554
+ __decorateClass([
555
+ decorators.ApiOperation({
556
+ methods: ["get"],
557
+ path: "/v1/obligations",
558
+ summary: "List all obligations",
559
+ description: "Returns a list of obligations with their current best ask and bid. Obligations are sorted by their id in ascending order by default."
560
+ }),
561
+ decorators.ApiQuery({
562
+ name: "cursor",
563
+ type: "string",
564
+ example: obligationCursorExample
565
+ }),
566
+ decorators.ApiQuery({ name: "limit", type: "number", example: 10 }),
567
+ decorators.ApiResponse({ status: 200, description: "Success", type: ObligationListResponse })
568
+ ], ObligationsController.prototype, "getObligations", 1);
569
+ __decorateClass([
570
+ decorators.ApiOperation({
571
+ methods: ["get"],
572
+ path: "/v1/obligations/{obligationId}",
573
+ summary: "Get an obligation",
574
+ description: "Returns an obligation by its id."
575
+ }),
576
+ decorators.ApiResponse({ status: 200, description: "Success", type: ObligationSingleSuccessResponse })
577
+ ], ObligationsController.prototype, "getObligation", 1);
578
+ ObligationsController = __decorateClass([
579
+ decorators.ApiTags("Obligations"),
580
+ decorators.ApiResponse({ status: 400, description: "Bad Request", type: BadRequestResponse })
581
+ ], ObligationsController);
582
+ var OpenApi = async () => await openapiMetadata.generateDocument({
583
+ controllers: [OffersController, ObligationsController, HealthController],
584
+ document: {
585
+ openapi: "3.1.0",
586
+ info: {
587
+ title: "Router API",
588
+ version: "1.0.0",
589
+ description: "API for the Morpho Router"
590
+ },
591
+ servers: [
592
+ {
593
+ url: "https://router.morpho.dev",
594
+ description: "Production server"
595
+ },
596
+ {
597
+ url: "http://localhost:7891",
598
+ description: "Local development server"
599
+ }
600
+ ]
601
+ }
602
+ });
603
+
604
+ // src/database/utils/Cursor.ts
605
+ var Cursor_exports = {};
606
+ __export(Cursor_exports, {
607
+ decode: () => decode,
608
+ encode: () => encode,
609
+ validate: () => validate
610
+ });
611
+ function validate(cursor) {
612
+ if (!cursor || typeof cursor !== "object") {
613
+ throw new Error("Cursor must be an object");
614
+ }
615
+ const c = cursor;
616
+ if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
617
+ throw new Error(
618
+ `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
619
+ );
620
+ }
621
+ if (!["asc", "desc"].includes(c.dir)) {
622
+ throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
623
+ }
624
+ if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
625
+ throw new Error(
626
+ `Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
627
+ );
628
+ }
629
+ const validations = {
630
+ rate: {
631
+ field: "rate",
632
+ type: "string",
633
+ pattern: /^\d+$/,
634
+ error: "numeric string"
635
+ },
636
+ amount: {
637
+ field: "assets",
638
+ type: "string",
639
+ pattern: /^\d+$/,
640
+ error: "numeric string"
641
+ },
642
+ maturity: {
643
+ field: "maturity",
644
+ type: "number",
645
+ validator: (val) => val > 0,
646
+ error: "positive number"
647
+ },
648
+ expiry: {
649
+ field: "expiry",
650
+ type: "number",
651
+ validator: (val) => val > 0,
652
+ error: "positive number"
653
+ }
654
+ };
655
+ const validation = validations[c.sort];
656
+ if (!validation) {
657
+ throw new Error(`Invalid sort field: ${c.sort}`);
658
+ }
659
+ const fieldValue = c[validation.field];
660
+ if (!fieldValue) {
661
+ throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
662
+ }
663
+ if (typeof fieldValue !== validation.type) {
664
+ throw new Error(
665
+ `${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
666
+ );
667
+ }
668
+ if (validation.pattern && !validation.pattern.test(fieldValue)) {
669
+ throw new Error(
670
+ `Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
671
+ );
672
+ }
673
+ if (validation.validator && !validation.validator(fieldValue)) {
674
+ throw new Error(
675
+ `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
676
+ );
677
+ }
678
+ if (c.page !== void 0) {
679
+ if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
680
+ throw new Error("Invalid page: must be a positive integer");
681
+ }
682
+ }
683
+ return true;
684
+ }
685
+ function encode(c) {
686
+ return jsBase64.Base64.encodeURL(JSON.stringify(c));
687
+ }
688
+ function decode(token2) {
689
+ if (!token2) return null;
690
+ const decoded = JSON.parse(jsBase64.Base64.decode(token2));
691
+ validate(decoded);
692
+ return decoded;
693
+ }
694
+
695
+ // src/api/Schema/requests.ts
696
+ var MAX_LIMIT = 100;
697
+ var DEFAULT_LIMIT = 20;
698
+ var PaginationQueryParams = z9__namespace.object({
699
+ cursor: z9__namespace.string().optional().refine(
700
+ (val) => {
701
+ if (!val) return true;
702
+ try {
703
+ const decoded = Cursor_exports.decode(val);
704
+ return decoded !== null;
705
+ } catch (_error) {
706
+ return false;
707
+ }
708
+ },
709
+ {
710
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
711
+ }
712
+ ).meta({
713
+ description: "Pagination cursor in base64url-encoded format",
714
+ example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
715
+ }),
716
+ limit: z9__namespace.string().regex(/^[1-9]\d*$/, {
717
+ message: "Limit must be a positive integer"
718
+ }).transform((val) => Number.parseInt(val, 10)).pipe(
719
+ z9__namespace.number().max(MAX_LIMIT, {
720
+ message: `Limit cannot exceed ${MAX_LIMIT}`
721
+ })
722
+ ).optional().default(DEFAULT_LIMIT).meta({
723
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
724
+ example: 10
725
+ })
726
+ });
727
+ var GetOffersQueryParams = z9__namespace.object({
728
+ ...PaginationQueryParams.shape,
729
+ side: z9__namespace.enum(["buy", "sell"]).meta({
730
+ description: "Side of the offer.",
731
+ example: "buy"
732
+ }),
733
+ obligation_id: z9__namespace.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
734
+ description: "Offers obligation id",
735
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
736
+ })
737
+ });
738
+ var GetObligationsQueryParams = z9__namespace.object({
739
+ ...PaginationQueryParams.shape,
740
+ cursor: z9__namespace.string().optional().meta({
741
+ description: "Obligation id cursor",
742
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
743
+ })
744
+ });
745
+ var GetObligationParams = z9__namespace.object({
746
+ obligation_id: z9__namespace.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
747
+ description: "Obligation id",
748
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
749
+ })
750
+ });
751
+ var schemas = {
752
+ get_offers: GetOffersQueryParams,
753
+ get_obligations: GetObligationsQueryParams,
754
+ get_obligation: GetObligationParams
755
+ };
756
+ function parse(action, query) {
757
+ return schemas[action].parse(query);
758
+ }
759
+ function safeParse(action, query, error) {
760
+ return schemas[action].safeParse(query, {
761
+ error
762
+ });
143
763
  }
144
764
 
765
+ // src/client/Client.ts
766
+ var Client_exports = {};
767
+ __export(Client_exports, {
768
+ HttpForbiddenError: () => HttpForbiddenError,
769
+ HttpGetApiFailedError: () => HttpGetApiFailedError,
770
+ HttpRateLimitError: () => HttpRateLimitError,
771
+ HttpUnauthorizedError: () => HttpUnauthorizedError,
772
+ InvalidUrlError: () => InvalidUrlError,
773
+ connect: () => connect,
774
+ getObligations: () => getObligations,
775
+ getOffers: () => getOffers
776
+ });
777
+
145
778
  // src/core/Abi.ts
146
779
  var Abi_exports = {};
147
780
  __export(Abi_exports, {
@@ -339,11 +972,13 @@ var Morpho = [
339
972
  var Callback_exports = {};
340
973
  __export(Callback_exports, {
341
974
  CallbackType: () => CallbackType,
342
- WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
975
+ decode: () => decode2,
343
976
  decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
344
977
  decodeSellERC20Callback: () => decodeSellERC20Callback,
978
+ encode: () => encode2,
345
979
  encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
346
- encodeSellERC20Callback: () => encodeSellERC20Callback
980
+ encodeSellERC20Callback: () => encodeSellERC20Callback,
981
+ isEmptyCallback: () => isEmptyCallback
347
982
  });
348
983
  var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
349
984
  CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
@@ -351,19 +986,27 @@ var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
351
986
  CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
352
987
  return CallbackType2;
353
988
  })(CallbackType || {});
354
- var WhitelistedCallbackAddresses = {
355
- ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
356
- ["buy_vault_v1_callback" /* BuyVaultV1Callback */]: [
357
- "0x3333333333333333333333333333333333333333",
358
- "0x4444444444444444444444444444444444444444"
359
- // @TODO: update once deployed and add mapping per chain if needed
360
- ].map((address) => address.toLowerCase()),
361
- ["sell_erc20_callback" /* SellERC20Callback */]: [
362
- "0x1111111111111111111111111111111111111111",
363
- "0x2222222222222222222222222222222222222222"
364
- // @TODO: update once deployed and add mapping per chain if needed
365
- ].map((address) => address.toLowerCase())
366
- };
989
+ var isEmptyCallback = (offer) => offer.callback.data === "0x";
990
+ function decode2(type, data) {
991
+ switch (type) {
992
+ case "buy_vault_v1_callback" /* BuyVaultV1Callback */:
993
+ return decodeBuyVaultV1Callback(data);
994
+ case "sell_erc20_callback" /* SellERC20Callback */:
995
+ return decodeSellERC20Callback(data);
996
+ default:
997
+ throw new Error("Invalid callback type");
998
+ }
999
+ }
1000
+ function encode2(type, data) {
1001
+ switch (type) {
1002
+ case "buy_vault_v1_callback" /* BuyVaultV1Callback */:
1003
+ return encodeBuyVaultV1Callback(data);
1004
+ case "sell_erc20_callback" /* SellERC20Callback */:
1005
+ return encodeSellERC20Callback(data);
1006
+ default:
1007
+ throw new Error("Invalid callback type");
1008
+ }
1009
+ }
367
1010
  function decodeBuyVaultV1Callback(data) {
368
1011
  if (!data || data === "0x") throw new Error("Empty callback data");
369
1012
  try {
@@ -505,74 +1148,30 @@ var chains = {
505
1148
  ...chains$1.mainnet,
506
1149
  id: ChainId.ETHEREUM,
507
1150
  name: "ethereum",
508
- whitelistedAssets: new Set(
509
- [
510
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
511
- // USDC
512
- "0x6B175474E89094C44Da98b954EedeAC495271d0F",
513
- // DAI
514
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
515
- // WETH
516
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
517
- // WBTC
518
- ].map((address) => address.toLowerCase())
519
- ),
520
1151
  morpho: "0x0000000000000000000000000000000000000000",
521
1152
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
522
1153
  mempool: {
523
1154
  address: "0x0000000000000000000000000000000000000000",
524
1155
  deploymentBlock: 23347674,
525
1156
  reindexBuffer: 10
526
- },
527
- vaultV1Factory: {
528
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
529
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
530
1157
  }
531
1158
  },
532
1159
  base: {
533
1160
  ...chains$1.base,
534
1161
  id: ChainId.BASE,
535
1162
  name: "base",
536
- whitelistedAssets: new Set(
537
- [
538
- "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
539
- // USDC
540
- "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
541
- // DAI
542
- "0x4200000000000000000000000000000000000006",
543
- // WETH
544
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
545
- // WBTC
546
- ].map((address) => address.toLowerCase())
547
- ),
548
1163
  morpho: "0x0000000000000000000000000000000000000000",
549
1164
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
550
1165
  mempool: {
551
1166
  address: "0x0000000000000000000000000000000000000000",
552
1167
  deploymentBlock: 35449942,
553
1168
  reindexBuffer: 10
554
- },
555
- vaultV1Factory: {
556
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
557
- "v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
558
1169
  }
559
1170
  },
560
1171
  "ethereum-virtual-testnet": {
561
1172
  ...chains$1.mainnet,
562
1173
  id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
563
1174
  name: "ethereum-virtual-testnet",
564
- whitelistedAssets: new Set(
565
- [
566
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
567
- // USDC
568
- "0x6B175474E89094C44Da98b954EedeAC495271d0F",
569
- // DAI
570
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
571
- // WETH
572
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
573
- // WBTC
574
- ].map((address) => address.toLowerCase())
575
- ),
576
1175
  morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
577
1176
  // @TODO: This is mock Consumed contract, update with Terms once stable
578
1177
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -580,28 +1179,12 @@ var chains = {
580
1179
  address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
581
1180
  deploymentBlock: 23224302,
582
1181
  reindexBuffer: 10
583
- },
584
- vaultV1Factory: {
585
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
586
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
587
1182
  }
588
1183
  },
589
1184
  anvil: {
590
1185
  ...chains$1.anvil,
591
1186
  id: ChainId.ANVIL,
592
1187
  name: "anvil",
593
- whitelistedAssets: new Set(
594
- [
595
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
596
- // USDC
597
- "0x6B175474E89094C44Da98b954EedeAC495271d0F",
598
- // DAI
599
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
600
- // WETH
601
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
602
- // WBTC
603
- ].map((address) => address.toLowerCase())
604
- ),
605
1188
  morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
606
1189
  morphoBlue: "0x0000000000000000000000000000000000000000",
607
1190
  // Set dynamically in tests
@@ -609,10 +1192,6 @@ var chains = {
609
1192
  address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
610
1193
  deploymentBlock: 23223727,
611
1194
  reindexBuffer: 10
612
- },
613
- vaultV1Factory: {
614
- "v1.0": "0x0000000000000000000000000000000000000000",
615
- "v1.1": "0x0000000000000000000000000000000000000000"
616
1195
  }
617
1196
  }
618
1197
  };
@@ -627,24 +1206,24 @@ async function* streamLogs(parameters) {
627
1206
  event,
628
1207
  blockNumberGte,
629
1208
  blockNumberLte,
630
- order = "desc",
1209
+ order: order2 = "desc",
631
1210
  options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
632
1211
  } = parameters;
633
1212
  if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
634
1213
  if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
635
- if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
1214
+ if (order2 === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
636
1215
  const latestBlock = (await actions.getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
637
1216
  let toBlock = 0n;
638
- if (order === "asc")
1217
+ if (order2 === "asc")
639
1218
  toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
640
- if (order === "desc")
1219
+ if (order2 === "desc")
641
1220
  toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
642
1221
  let fromBlock = 0n;
643
- if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
644
- if (order === "desc")
1222
+ if (order2 === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
1223
+ if (order2 === "desc")
645
1224
  fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
646
- if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
647
- if (order === "desc") fromBlock = max(fromBlock, toBlock - BigInt(blockWindow));
1225
+ if (order2 === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
1226
+ if (order2 === "desc") fromBlock = max(fromBlock, toBlock - BigInt(blockWindow));
648
1227
  if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
649
1228
  let streaming = true;
650
1229
  while (streaming) {
@@ -656,10 +1235,10 @@ async function* streamLogs(parameters) {
656
1235
  });
657
1236
  logs.sort((a, b) => {
658
1237
  if (a.blockNumber !== b.blockNumber)
659
- return order === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
1238
+ return order2 === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
660
1239
  if (a.transactionIndex !== b.transactionIndex)
661
- return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
662
- return order === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
1240
+ return order2 === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
1241
+ return order2 === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
663
1242
  });
664
1243
  for (const logBatch of batch(logs, maxBatchSize)) {
665
1244
  if (logBatch.length === 0) break;
@@ -670,16 +1249,16 @@ async function* streamLogs(parameters) {
670
1249
  Number(logBatch[logBatch.length - 1]?.blockNumber)
671
1250
  ) : (
672
1251
  // if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
673
- order === "asc" ? Number(toBlock) : Number(fromBlock)
1252
+ order2 === "asc" ? Number(toBlock) : Number(fromBlock)
674
1253
  )
675
1254
  };
676
1255
  }
677
- streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
678
- if (order === "asc") {
1256
+ streaming = order2 === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
1257
+ if (order2 === "asc") {
679
1258
  fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
680
1259
  toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
681
1260
  }
682
- if (order === "desc") {
1261
+ if (order2 === "desc") {
683
1262
  const lowerBound = BigInt(blockNumberGte || 0);
684
1263
  const windowSize = BigInt(blockWindow);
685
1264
  const nextToBlock = max(fromBlock - 1n, lowerBound);
@@ -688,7 +1267,7 @@ async function* streamLogs(parameters) {
688
1267
  fromBlock = nextFromBlock;
689
1268
  }
690
1269
  }
691
- yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
1270
+ yield { logs: [], blockNumber: order2 === "asc" ? Number(toBlock) : Number(fromBlock) };
692
1271
  return;
693
1272
  }
694
1273
  var InvalidBlockRangeError = class extends BaseError {
@@ -727,7 +1306,8 @@ var Collateral_exports = {};
727
1306
  __export(Collateral_exports, {
728
1307
  CollateralSchema: () => CollateralSchema,
729
1308
  CollateralsSchema: () => CollateralsSchema,
730
- from: () => from4
1309
+ from: () => from4,
1310
+ random: () => random
731
1311
  });
732
1312
  var transformHex = (val, ctx) => {
733
1313
  if (viem.isHex(val)) return val;
@@ -737,7 +1317,7 @@ var transformHex = (val, ctx) => {
737
1317
  format: "hex",
738
1318
  error: "not a hex"
739
1319
  });
740
- return z7__namespace.NEVER;
1320
+ return z9__namespace.NEVER;
741
1321
  };
742
1322
  var transformAddress = (val, ctx) => {
743
1323
  if (viem.isAddress(val.toLowerCase())) return val.toLowerCase();
@@ -747,7 +1327,7 @@ var transformAddress = (val, ctx) => {
747
1327
  format: "address",
748
1328
  error: "not a valid address"
749
1329
  });
750
- return z7__namespace.NEVER;
1330
+ return z9__namespace.NEVER;
751
1331
  };
752
1332
 
753
1333
  // src/core/LLTV.ts
@@ -787,7 +1367,7 @@ var InvalidLLTVError = class extends BaseError {
787
1367
  __publicField(this, "name", "LLTV.InvalidLLTVError");
788
1368
  }
789
1369
  };
790
- var LLTVSchema = z7__namespace.bigint({ coerce: true }).refine(
1370
+ var LLTVSchema = z9__namespace.bigint({ coerce: true }).refine(
791
1371
  (lltv) => {
792
1372
  try {
793
1373
  from3(lltv);
@@ -804,12 +1384,12 @@ var LLTVSchema = z7__namespace.bigint({ coerce: true }).refine(
804
1384
  ).transform((lltv) => from3(lltv));
805
1385
 
806
1386
  // src/core/Collateral.ts
807
- var CollateralSchema = z7__namespace.object({
808
- asset: z7__namespace.string().transform(transformAddress),
809
- oracle: z7__namespace.string().transform(transformAddress),
1387
+ var CollateralSchema = z9__namespace.object({
1388
+ asset: z9__namespace.string().transform(transformAddress),
1389
+ oracle: z9__namespace.string().transform(transformAddress),
810
1390
  lltv: LLTVSchema
811
1391
  });
812
- var CollateralsSchema = z7__namespace.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine(
1392
+ var CollateralsSchema = z9__namespace.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine(
813
1393
  (collaterals) => {
814
1394
  for (let i = 1; i < collaterals.length; i++) {
815
1395
  if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
@@ -844,6 +1424,13 @@ var from4 = (parameters) => {
844
1424
  oracle: parameters.oracle.toLowerCase()
845
1425
  };
846
1426
  };
1427
+ function random() {
1428
+ return from4({
1429
+ asset: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1430
+ oracle: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1431
+ lltv: 0.965
1432
+ });
1433
+ }
847
1434
 
848
1435
  // src/core/Liquidity.ts
849
1436
  var Liquidity_exports = {};
@@ -867,20 +1454,20 @@ function calculateMaxDebt(amount, oraclePrice, lltv) {
867
1454
  return maxDebt;
868
1455
  }
869
1456
  function generateBalancePoolId(parameters) {
870
- const { user, chainId, token } = parameters;
871
- return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
1457
+ const { user, chainId, token: token2 } = parameters;
1458
+ return `${user}-${chainId.toString()}-${token2}-balance`.toLowerCase();
872
1459
  }
873
1460
  function generateAllowancePoolId(parameters) {
874
- const { user, chainId, token } = parameters;
875
- return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
1461
+ const { user, chainId, token: token2 } = parameters;
1462
+ return `${user}-${chainId.toString()}-${token2}-allowance`.toLowerCase();
876
1463
  }
877
1464
  function generateSellERC20CallbackPoolId(parameters) {
878
- const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
879
- return `${user}-${chainId.toString()}-${obligationId2}-${token}-${offerHash}-sell_erc20_callback`.toLowerCase();
1465
+ const { user, chainId, obligationId: obligationId2, token: token2, offerHash } = parameters;
1466
+ return `${user}-${chainId.toString()}-${obligationId2}-${token2}-${offerHash}-sell_erc20_callback`.toLowerCase();
880
1467
  }
881
1468
  function generateObligationCollateralPoolId(parameters) {
882
- const { user, chainId, obligationId: obligationId2, token } = parameters;
883
- return `${user}-${chainId.toString()}-${obligationId2}-${token}-obligation-collateral`.toLowerCase();
1469
+ const { user, chainId, obligationId: obligationId2, token: token2 } = parameters;
1470
+ return `${user}-${chainId.toString()}-${obligationId2}-${token2}-obligation-collateral`.toLowerCase();
884
1471
  }
885
1472
  function generateBuyVaultCallbackPoolId(parameters) {
886
1473
  const { user, chainId, vault, offerHash } = parameters;
@@ -910,12 +1497,13 @@ __export(Maturity_exports, {
910
1497
  InvalidFormatError: () => InvalidFormatError,
911
1498
  InvalidOptionError: () => InvalidOptionError2,
912
1499
  MaturitySchema: () => MaturitySchema,
1500
+ MaturityType: () => MaturityType,
913
1501
  from: () => from5
914
1502
  });
915
- var MaturitySchema = z7__namespace.number().int().refine(
916
- (maturity) => {
1503
+ var MaturitySchema = z9__namespace.number().int().refine(
1504
+ (maturity2) => {
917
1505
  try {
918
- from5(maturity);
1506
+ from5(maturity2);
919
1507
  return true;
920
1508
  } catch (_e) {
921
1509
  return false;
@@ -931,7 +1519,16 @@ var MaturitySchema = z7__namespace.number().int().refine(
931
1519
  }
932
1520
  }
933
1521
  }
934
- ).transform((maturity) => maturity);
1522
+ ).transform((maturity2) => maturity2);
1523
+ var MaturityType = /* @__PURE__ */ ((MaturityType2) => {
1524
+ MaturityType2["EndOfWeek"] = "end_of_week";
1525
+ MaturityType2["EndOfNextWeek"] = "end_of_next_week";
1526
+ MaturityType2["EndOfMonth"] = "end_of_month";
1527
+ MaturityType2["EndOfNextMonth"] = "end_of_next_month";
1528
+ MaturityType2["EndOfQuarter"] = "end_of_quarter";
1529
+ MaturityType2["EndOfNextQuarter"] = "end_of_next_quarter";
1530
+ return MaturityType2;
1531
+ })(MaturityType || {});
935
1532
  var MaturityOptions = {
936
1533
  end_of_week: () => endOfWeek(),
937
1534
  end_of_next_week: () => endOfNextWeek(),
@@ -952,8 +1549,26 @@ function from5(ts) {
952
1549
  }
953
1550
  var endOfWeek = () => fridayOfWeek(0);
954
1551
  var endOfNextWeek = () => fridayOfWeek(1);
955
- var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
956
- var endOfNextMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth() + 1);
1552
+ var endOfMonth = () => {
1553
+ const now2 = /* @__PURE__ */ new Date();
1554
+ const year = now2.getUTCFullYear();
1555
+ const month = now2.getUTCMonth();
1556
+ const endOfMonth2 = lastFridayOfMonth(year, month);
1557
+ if (now2.getTime() > endOfMonth2 * 1e3) {
1558
+ return lastFridayOfMonth(year, month + 1);
1559
+ }
1560
+ return endOfMonth2;
1561
+ };
1562
+ var endOfNextMonth = () => {
1563
+ const now2 = /* @__PURE__ */ new Date();
1564
+ const year = now2.getUTCFullYear();
1565
+ const month = now2.getUTCMonth();
1566
+ const endOfMonth2 = lastFridayOfMonth(year, month);
1567
+ if (now2.getTime() > endOfMonth2 * 1e3) {
1568
+ return lastFridayOfMonth(year, month + 2);
1569
+ }
1570
+ return lastFridayOfMonth(year, month + 1);
1571
+ };
957
1572
  var endOfQuarter = () => lastFridayOfQuarter(0);
958
1573
  var endOfNextQuarter = () => lastFridayOfQuarter(1);
959
1574
  var fridayOfWeek = (weeksAhead = 0) => {
@@ -974,8 +1589,8 @@ var lastFridayOfMonth = (year, month) => {
974
1589
  while (lastDayOfMonth15H.getUTCDay() !== 5) {
975
1590
  lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
976
1591
  }
977
- const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
978
- return maturity;
1592
+ const maturity2 = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
1593
+ return maturity2;
979
1594
  };
980
1595
  var lastFridayOfQuarter = (quartersAhead = 0) => {
981
1596
  const now2 = /* @__PURE__ */ new Date();
@@ -1021,11 +1636,11 @@ __export(Obligation_exports, {
1021
1636
  from: () => from6,
1022
1637
  fromSnakeCase: () => fromSnakeCase2,
1023
1638
  id: () => id,
1024
- random: () => random
1639
+ random: () => random2
1025
1640
  });
1026
- var ObligationSchema = z7__namespace.object({
1027
- chainId: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1028
- loanToken: z7__namespace.string().transform(transformAddress),
1641
+ var ObligationSchema = z9__namespace.object({
1642
+ chainId: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1643
+ loanToken: z9__namespace.string().transform(transformAddress),
1029
1644
  collaterals: CollateralsSchema,
1030
1645
  maturity: MaturitySchema
1031
1646
  });
@@ -1083,7 +1698,7 @@ function id(obligation) {
1083
1698
  )
1084
1699
  );
1085
1700
  }
1086
- function random() {
1701
+ function random2() {
1087
1702
  return from6({
1088
1703
  chainId: 1n,
1089
1704
  loanToken: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
@@ -1117,170 +1732,138 @@ __export(Offer_exports, {
1117
1732
  InvalidOfferError: () => InvalidOfferError,
1118
1733
  OfferHashSchema: () => OfferHashSchema,
1119
1734
  OfferSchema: () => OfferSchema,
1735
+ StatusCode: () => StatusCode,
1120
1736
  consumedEvent: () => consumedEvent,
1121
- decode: () => decode,
1737
+ decode: () => decode4,
1122
1738
  domain: () => domain,
1123
- encode: () => encode,
1124
- from: () => from7,
1739
+ encode: () => encode4,
1740
+ from: () => from8,
1125
1741
  fromConsumedLog: () => fromConsumedLog,
1126
1742
  fromSnakeCase: () => fromSnakeCase3,
1127
1743
  hash: () => hash,
1128
1744
  obligationId: () => obligationId,
1129
- random: () => random2,
1745
+ random: () => random3,
1130
1746
  sign: () => sign,
1747
+ signatureMsg: () => signatureMsg,
1131
1748
  toSnakeCase: () => toSnakeCase2,
1132
1749
  types: () => types
1133
1750
  });
1134
1751
 
1135
- // src/utils/index.ts
1136
- var utils_exports = {};
1137
- __export(utils_exports, {
1138
- BaseError: () => BaseError,
1139
- Time: () => time_exports,
1140
- batch: () => batch,
1141
- batchMulticall: () => batchMulticall,
1142
- fromSnakeCase: () => fromSnakeCase,
1143
- lazy: () => lazy,
1144
- max: () => max,
1145
- min: () => min,
1146
- poll: () => poll,
1147
- retry: () => retry,
1148
- toSnakeCase: () => toSnakeCase,
1149
- wait: () => wait
1752
+ // src/core/Tree.ts
1753
+ var Tree_exports = {};
1754
+ __export(Tree_exports, {
1755
+ VERSION: () => VERSION,
1756
+ decode: () => decode3,
1757
+ encode: () => encode3,
1758
+ from: () => from7
1150
1759
  });
1151
-
1152
- // src/utils/retry.ts
1153
- var retry = async (fn, attempts = 3, delayMs = 50) => {
1154
- let lastErr;
1155
- for (let i = 0; i < attempts; i++) {
1156
- try {
1157
- return await fn();
1158
- } catch (err) {
1159
- lastErr = err;
1160
- if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
1161
- }
1162
- }
1163
- throw lastErr;
1760
+ var VERSION = 1;
1761
+ var from7 = (offers) => {
1762
+ const leaves = order(offers).map((offer) => {
1763
+ return [offer.hash];
1764
+ });
1765
+ const tree = merkleTree.StandardMerkleTree.of(leaves, ["bytes32"]);
1766
+ return Object.assign(tree, { offers });
1164
1767
  };
1165
-
1166
- // src/utils/batchMulticall.ts
1167
- async function batchMulticall(parameters) {
1168
- const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
1169
- const results = [];
1170
- for (const callsBatch of batch(calls, batchSize)) {
1171
- const batchResults = await retry(
1172
- () => client.multicall({
1173
- allowFailure: false,
1174
- contracts: callsBatch,
1175
- ...blockNumber ? { blockNumber } : {}
1176
- }),
1177
- retryAttempts,
1178
- retryDelayMs
1179
- );
1180
- results.push(...batchResults);
1768
+ var byHashAsc = (a, b) => a.localeCompare(b);
1769
+ var order = (offers) => {
1770
+ return offers.sort((a, b) => byHashAsc(a.hash, b.hash));
1771
+ };
1772
+ var encode3 = (tree) => {
1773
+ assertRoot(tree.root, tree.offers);
1774
+ const offersPayload = tree.offers.map((offer) => ({
1775
+ offering: offer.offering,
1776
+ assets: offer.assets.toString(),
1777
+ rate: offer.rate.toString(),
1778
+ maturity: Number(offer.maturity),
1779
+ expiry: Number(offer.expiry),
1780
+ start: Number(offer.start),
1781
+ nonce: offer.nonce.toString(),
1782
+ buy: offer.buy,
1783
+ chainId: offer.chainId.toString(),
1784
+ loanToken: offer.loanToken,
1785
+ collaterals: offer.collaterals.map((c) => ({
1786
+ asset: c.asset,
1787
+ oracle: c.oracle,
1788
+ lltv: c.lltv.toString()
1789
+ })),
1790
+ callback: {
1791
+ address: offer.callback.address,
1792
+ data: offer.callback.data,
1793
+ gasLimit: offer.callback.gasLimit.toString()
1794
+ },
1795
+ signature: offer.signature,
1796
+ hash: offer.hash
1797
+ }));
1798
+ const compressed = pako.gzip(JSON.stringify([tree.root, ...offersPayload]));
1799
+ const encoded = new Uint8Array(1 + compressed.length);
1800
+ encoded[0] = VERSION;
1801
+ encoded.set(compressed, 1);
1802
+ return viem.bytesToHex(encoded);
1803
+ };
1804
+ var assertRoot = (root, offers) => {
1805
+ const tree = from7(offers);
1806
+ if (root !== tree.root) {
1807
+ throw new Error(`Invalid root: expected ${tree.root}, got ${root}`);
1181
1808
  }
1182
- return results;
1183
- }
1184
-
1185
- // src/utils/lazy.ts
1186
- function lazy(pollFn) {
1187
- return () => async function* () {
1188
- let active = true;
1189
- let resolveNext = null;
1190
- const queue = [];
1191
- const wait2 = () => new Promise((resolve) => {
1192
- resolveNext = resolve;
1193
- });
1194
- const emit = (item) => {
1195
- queue.push(item);
1196
- resolveNext?.();
1197
- resolveNext = null;
1198
- };
1199
- let unpoll = null;
1200
- const stop = () => {
1201
- active = false;
1202
- unpoll?.();
1203
- resolveNext?.();
1204
- resolveNext = null;
1205
- };
1206
- unpoll = pollFn(emit, { stop });
1207
- try {
1208
- while (active) {
1209
- if (queue.length === 0) await wait2();
1210
- while (queue.length > 0 && active) yield queue.shift();
1211
- }
1212
- } finally {
1213
- stop();
1214
- }
1215
- }();
1216
- }
1217
-
1218
- // src/utils/wait.ts
1219
- async function wait(time) {
1220
- return new Promise((res) => setTimeout(res, time));
1221
- }
1222
-
1223
- // src/utils/poll.ts
1224
- function poll(fn, { interval }) {
1225
- let active = true;
1226
- const unwatch = () => active = false;
1227
- const watch2 = async () => {
1228
- await wait(interval);
1229
- const poll2 = async () => {
1230
- if (!active) return;
1231
- await fn({ unpoll: unwatch });
1232
- await wait(interval);
1233
- poll2();
1234
- };
1235
- poll2();
1236
- };
1237
- watch2();
1238
- return unwatch;
1239
- }
1240
-
1241
- // src/utils/time.ts
1242
- var time_exports = {};
1243
- __export(time_exports, {
1244
- max: () => max2,
1245
- now: () => now
1246
- });
1247
- function now() {
1248
- return Math.floor(Date.now() / 1e3);
1249
- }
1250
- function max2() {
1251
- return 864e16;
1252
- }
1809
+ };
1810
+ var decode3 = (encoded) => {
1811
+ const bytes = viem.hexToBytes(encoded);
1812
+ if (bytes.length < 2) {
1813
+ throw new Error("Invalid payload: too short");
1814
+ }
1815
+ const version = bytes[0];
1816
+ if (version !== (VERSION & 255)) {
1817
+ throw new Error(`Invalid version: expected ${VERSION}, got ${version}`);
1818
+ }
1819
+ const payload = bytes.slice(1);
1820
+ const decoded = pako.ungzip(payload, { to: "string" });
1821
+ const data = JSON.parse(decoded);
1822
+ const root = data[0];
1823
+ const offers = data.slice(1).map((o) => OfferSchema({ omitConsumed: true, omitBlockNumber: true }).parse(o));
1824
+ const tree = from7(offers);
1825
+ if (root !== tree.root) {
1826
+ throw new Error(`Invalid root: expected ${tree.root}, got ${root}`);
1827
+ }
1828
+ return tree;
1829
+ };
1253
1830
 
1254
1831
  // src/core/Offer.ts
1255
- var OfferHashSchema = z7__namespace.string().regex(/^0x[0-9a-fA-F]{64}$/, {
1832
+ var StatusCode = /* @__PURE__ */ ((StatusCode2) => {
1833
+ StatusCode2["VALID"] = "VALID";
1834
+ StatusCode2["NOT_ENOUGH_LIQUIDITY"] = "NOT_ENOUGH_LIQUIDITY";
1835
+ return StatusCode2;
1836
+ })(StatusCode || {});
1837
+ var OfferHashSchema = z9__namespace.string().regex(/^0x[0-9a-fA-F]{64}$/, {
1256
1838
  message: "Hash must be a valid 32-byte hex string"
1257
1839
  }).transform(transformHex);
1258
1840
  var OfferSchema = (parameters) => {
1259
- const { omitHash = false } = parameters || {};
1260
- const now2 = time_exports.now();
1261
- let base = z7__namespace.object({
1262
- offering: z7__namespace.string().transform(transformAddress),
1263
- assets: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1264
- rate: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1841
+ const { omitHash = false, omitConsumed = false, omitBlockNumber = false } = parameters || {};
1842
+ let base = z9__namespace.object({
1843
+ offering: z9__namespace.string().transform(transformAddress),
1844
+ assets: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1845
+ rate: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1265
1846
  maturity: MaturitySchema,
1266
- expiry: z7__namespace.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
1267
- start: z7__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1268
- nonce: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1269
- buy: z7__namespace.boolean(),
1270
- chainId: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1271
- loanToken: z7__namespace.string().transform(transformAddress),
1847
+ expiry: z9__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1848
+ start: z9__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1849
+ nonce: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1850
+ buy: z9__namespace.boolean(),
1851
+ chainId: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1852
+ loanToken: z9__namespace.string().transform(transformAddress),
1272
1853
  collaterals: CollateralsSchema,
1273
- callback: z7__namespace.object({
1274
- address: z7__namespace.string().transform(transformAddress),
1275
- data: z7__namespace.string().transform(transformHex),
1276
- gasLimit: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1854
+ callback: z9__namespace.object({
1855
+ address: z9__namespace.string().transform(transformAddress),
1856
+ data: z9__namespace.string().transform(transformHex),
1857
+ gasLimit: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1277
1858
  }),
1278
- consumed: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1279
- blockNumber: z7__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1280
- signature: z7__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1859
+ signature: z9__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1281
1860
  message: "Signature must be a valid 65-byte hex string"
1282
1861
  }).transform(transformHex).optional()
1283
1862
  });
1863
+ if (!omitConsumed)
1864
+ base = base.extend({ consumed: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256) });
1865
+ if (!omitBlockNumber)
1866
+ base = base.extend({ blockNumber: z9__namespace.number().int().max(Number.MAX_SAFE_INTEGER) });
1284
1867
  if (!omitHash) base = base.extend({ hash: OfferHashSchema });
1285
1868
  return base.refine((data) => data.start < data.expiry, {
1286
1869
  message: "Start must be before expiry",
@@ -1290,7 +1873,7 @@ var OfferSchema = (parameters) => {
1290
1873
  path: ["expiry"]
1291
1874
  });
1292
1875
  };
1293
- function from7(input) {
1876
+ function from8(input) {
1294
1877
  try {
1295
1878
  const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1296
1879
  const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
@@ -1303,13 +1886,13 @@ function from7(input) {
1303
1886
  }
1304
1887
  }
1305
1888
  function fromSnakeCase3(input) {
1306
- return from7(fromSnakeCase(input));
1889
+ return from8(fromSnakeCase(input));
1307
1890
  }
1308
1891
  function toSnakeCase2(offer) {
1309
1892
  return toSnakeCase(offer);
1310
1893
  }
1311
- function random2(config) {
1312
- const chain = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
1894
+ function random3(config) {
1895
+ const chain2 = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
1313
1896
  const loanToken = config?.loanTokens ? config.loanTokens[Math.floor(Math.random() * config.loanTokens.length)] : accounts.privateKeyToAccount(accounts.generatePrivateKey()).address;
1314
1897
  const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [accounts.privateKeyToAccount(accounts.generatePrivateKey()).address];
1315
1898
  const collateralAsset = collateralCandidates[Math.floor(Math.random() * collateralCandidates.length)];
@@ -1317,7 +1900,7 @@ function random2(config) {
1317
1900
  ["end_of_month", 1],
1318
1901
  ["end_of_next_month", 1]
1319
1902
  ]);
1320
- const maturity = config?.maturity ?? from5(maturityOption);
1903
+ const maturity2 = config?.maturity ?? from5(maturityOption);
1321
1904
  const lltv = from3(
1322
1905
  weightedChoice([
1323
1906
  [0.385, 1],
@@ -1353,32 +1936,29 @@ function random2(config) {
1353
1936
  const consumed = config?.consumed !== void 0 ? config.consumed : Math.random() < 0.8 ? 0n : assetsScaled * BigInt(1 + Math.floor(Math.random() * 900)) / 1000n;
1354
1937
  const callbackBySide = (() => {
1355
1938
  if (buy) return { address: viem.zeroAddress, data: "0x", gasLimit: 0n };
1356
- const sellCallbackAddress = WhitelistedCallbackAddresses["sell_erc20_callback" /* SellERC20Callback */][0].toLowerCase();
1939
+ const sellCallbackAddress = "0x3333333333333333333333333333333333333333";
1357
1940
  const amount = assetsScaled * 1000000000000000000000n;
1358
1941
  const data = encodeSellERC20Callback({
1359
1942
  collaterals: [collateralAsset],
1360
1943
  amounts: [amount]
1361
1944
  });
1362
- return { address: sellCallbackAddress, data, gasLimit: 500000n };
1945
+ return { address: sellCallbackAddress, data, gasLimit: 0n };
1363
1946
  })();
1364
- const offer = from7({
1947
+ const offer = from8({
1365
1948
  offering: config?.offering ?? accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1366
1949
  assets: assetsScaled,
1367
1950
  rate,
1368
- maturity,
1369
- expiry: config?.expiry ?? maturity - 1,
1370
- start: config?.start ?? maturity - 10,
1951
+ maturity: maturity2,
1952
+ expiry: config?.expiry ?? maturity2 - 1,
1953
+ start: config?.start ?? maturity2 - 10,
1371
1954
  nonce: BigInt(Math.floor(Math.random() * 1e6)),
1372
1955
  buy,
1373
- chainId: chain.id,
1956
+ chainId: chain2.id,
1374
1957
  loanToken,
1375
- collaterals: config?.collaterals ?? [
1376
- from4({
1377
- asset: collateralAsset,
1378
- oracle: viem.zeroAddress,
1379
- lltv
1380
- })
1381
- ],
1958
+ collaterals: config?.collaterals ?? Array.from({ length: Math.floor(Math.random() * 3) + 1 }, () => ({
1959
+ ...random(),
1960
+ lltv
1961
+ })).sort((a, b) => a.asset.localeCompare(b.asset)),
1382
1962
  callback: config?.callback ?? callbackBySide,
1383
1963
  consumed,
1384
1964
  blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
@@ -1426,31 +2006,16 @@ var types = {
1426
2006
  { name: "gasLimit", type: "uint256" }
1427
2007
  ]
1428
2008
  };
1429
- function sign(offer, wallet) {
2009
+ async function sign(offers, wallet) {
1430
2010
  if (!wallet.account) throw new AccountNotSetError();
1431
- return wallet.signTypedData({
2011
+ return wallet.signMessage({
1432
2012
  account: wallet.account,
1433
- domain: domain(offer.chainId),
1434
- types,
1435
- primaryType: "Offer",
1436
- message: {
1437
- offering: offer.offering.toLowerCase(),
1438
- assets: offer.assets,
1439
- rate: offer.rate,
1440
- maturity: BigInt(offer.maturity),
1441
- expiry: BigInt(offer.expiry),
1442
- nonce: offer.nonce,
1443
- buy: offer.buy,
1444
- loanToken: offer.loanToken.toLowerCase(),
1445
- collaterals: offer.collaterals,
1446
- callback: {
1447
- address: offer.callback.address.toLowerCase(),
1448
- data: offer.callback.data,
1449
- gasLimit: offer.callback.gasLimit
1450
- }
1451
- }
2013
+ message: { raw: signatureMsg(offers) }
1452
2014
  });
1453
2015
  }
2016
+ function signatureMsg(offers) {
2017
+ return from7(offers).root;
2018
+ }
1454
2019
  function hash(offer) {
1455
2020
  return viem.hashTypedData({
1456
2021
  domain: domain(offer.chainId),
@@ -1515,7 +2080,7 @@ var OfferAbi = [
1515
2080
  },
1516
2081
  { name: "signature", type: "bytes" }
1517
2082
  ];
1518
- function encode(offer) {
2083
+ function encode4(offer) {
1519
2084
  return viem.encodeAbiParameters(OfferAbi, [
1520
2085
  offer.offering,
1521
2086
  offer.assets,
@@ -1532,14 +2097,14 @@ function encode(offer) {
1532
2097
  offer.signature ?? "0x"
1533
2098
  ]);
1534
2099
  }
1535
- function decode(data, blockNumber) {
2100
+ function decode4(data, blockNumber) {
1536
2101
  let decoded;
1537
2102
  try {
1538
2103
  decoded = viem.decodeAbiParameters(OfferAbi, data);
1539
2104
  } catch (error) {
1540
2105
  throw new InvalidOfferError(error);
1541
2106
  }
1542
- const offer = from7({
2107
+ const offer = from8({
1543
2108
  offering: decoded[0],
1544
2109
  assets: decoded[1],
1545
2110
  rate: decoded[2],
@@ -1607,20 +2172,20 @@ var Quote_exports = {};
1607
2172
  __export(Quote_exports, {
1608
2173
  InvalidQuoteError: () => InvalidQuoteError,
1609
2174
  QuoteSchema: () => QuoteSchema,
1610
- from: () => from8,
2175
+ from: () => from9,
1611
2176
  fromSnakeCase: () => fromSnakeCase4,
1612
- random: () => random3
2177
+ random: () => random4
1613
2178
  });
1614
- var QuoteSchema = z7__namespace.object({
1615
- obligationId: z7__namespace.string().transform(transformHex),
1616
- ask: z7__namespace.object({
1617
- rate: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
2179
+ var QuoteSchema = z9__namespace.object({
2180
+ obligationId: z9__namespace.string().transform(transformHex),
2181
+ ask: z9__namespace.object({
2182
+ rate: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1618
2183
  }),
1619
- bid: z7__namespace.object({
1620
- rate: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
2184
+ bid: z9__namespace.object({
2185
+ rate: z9__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1621
2186
  })
1622
2187
  });
1623
- function from8(parameters) {
2188
+ function from9(parameters) {
1624
2189
  try {
1625
2190
  const parsedQuote = QuoteSchema.parse(parameters);
1626
2191
  return {
@@ -1633,10 +2198,10 @@ function from8(parameters) {
1633
2198
  }
1634
2199
  }
1635
2200
  function fromSnakeCase4(snake) {
1636
- return from8(fromSnakeCase(snake));
2201
+ return from9(fromSnakeCase(snake));
1637
2202
  }
1638
- function random3() {
1639
- return from8({
2203
+ function random4() {
2204
+ return from9({
1640
2205
  obligationId: Obligation_exports.id(Obligation_exports.random()),
1641
2206
  ask: {
1642
2207
  rate: BigInt(Math.floor(Math.random() * 1e6))
@@ -1656,494 +2221,83 @@ var InvalidQuoteError = class extends BaseError {
1656
2221
  // src/core/types.ts
1657
2222
  var BrandTypeId = Symbol.for("mempool/Brand");
1658
2223
 
1659
- // src/stores/utils/Cursor.ts
1660
- var Cursor_exports = {};
1661
- __export(Cursor_exports, {
1662
- decode: () => decode2,
1663
- encode: () => encode2,
1664
- validate: () => validate
1665
- });
1666
- function validate(cursor) {
1667
- if (!cursor || typeof cursor !== "object") {
1668
- throw new Error("Cursor must be an object");
1669
- }
1670
- const c = cursor;
1671
- if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
1672
- throw new Error(
1673
- `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
1674
- );
1675
- }
1676
- if (!["asc", "desc"].includes(c.dir)) {
1677
- throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
1678
- }
1679
- if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
1680
- throw new Error(
1681
- `Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
1682
- );
1683
- }
1684
- const validations = {
1685
- rate: {
1686
- field: "rate",
1687
- type: "string",
1688
- pattern: /^\d+$/,
1689
- error: "numeric string"
1690
- },
1691
- amount: {
1692
- field: "assets",
1693
- type: "string",
1694
- pattern: /^\d+$/,
1695
- error: "numeric string"
1696
- },
1697
- maturity: {
1698
- field: "maturity",
1699
- type: "number",
1700
- validator: (val) => val > 0,
1701
- error: "positive number"
1702
- },
1703
- expiry: {
1704
- field: "expiry",
1705
- type: "number",
1706
- validator: (val) => val > 0,
1707
- error: "positive number"
1708
- }
2224
+ // src/client/Client.ts
2225
+ function connect(parameters) {
2226
+ const u = new URL(parameters?.url || "https://router.morpho.dev");
2227
+ if (u.protocol !== "http:" && u.protocol !== "https:") throw new InvalidUrlError(u.toString());
2228
+ const headers = parameters?.headers ?? new Headers();
2229
+ headers.set("Content-Type", "application/json");
2230
+ parameters?.apiKey !== void 0 ? headers.set("X-API-Key", parameters.apiKey) : null;
2231
+ const config = { url: u, headers };
2232
+ const apiClient = createOpenApiFetchClient__default.default({
2233
+ baseUrl: config.url.toString(),
2234
+ headers: config.headers
2235
+ });
2236
+ return {
2237
+ ...config,
2238
+ getOffers: (parameters2) => getOffers(apiClient, parameters2),
2239
+ getObligations: (parameters2) => getObligations(apiClient, parameters2)
1709
2240
  };
1710
- const validation = validations[c.sort];
1711
- if (!validation) {
1712
- throw new Error(`Invalid sort field: ${c.sort}`);
1713
- }
1714
- const fieldValue = c[validation.field];
1715
- if (!fieldValue) {
1716
- throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
1717
- }
1718
- if (typeof fieldValue !== validation.type) {
1719
- throw new Error(
1720
- `${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
1721
- );
1722
- }
1723
- if (validation.pattern && !validation.pattern.test(fieldValue)) {
1724
- throw new Error(
1725
- `Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
1726
- );
1727
- }
1728
- if (validation.validator && !validation.validator(fieldValue)) {
1729
- throw new Error(
1730
- `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
1731
- );
1732
- }
1733
- if (c.page !== void 0) {
1734
- if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
1735
- throw new Error("Invalid page: must be a positive integer");
1736
- }
1737
- }
1738
- return true;
1739
- }
1740
- function encode2(c) {
1741
- return jsBase64.Base64.encodeURL(JSON.stringify(c));
1742
- }
1743
- function decode2(token) {
1744
- if (!token) return null;
1745
- const decoded = JSON.parse(jsBase64.Base64.decode(token));
1746
- validate(decoded);
1747
- return decoded;
1748
2241
  }
1749
-
1750
- // src/api/Schema/requests.ts
1751
- var MAX_LIMIT = 100;
1752
- var DEFAULT_LIMIT = 20;
1753
- var PaginationQueryParams = z7__namespace.object({
1754
- cursor: z7__namespace.string().optional().refine(
1755
- (val) => {
1756
- if (!val) return true;
1757
- try {
1758
- const decoded = Cursor_exports.decode(val);
1759
- return decoded !== null;
1760
- } catch (_error) {
1761
- return false;
2242
+ async function getOffers(apiClient, parameters) {
2243
+ const { data, error, response } = await apiClient.GET("/v1/offers", {
2244
+ params: {
2245
+ query: {
2246
+ side: parameters.side,
2247
+ obligation_id: parameters.obligationId,
2248
+ cursor: parameters.cursor,
2249
+ limit: parameters.limit
1762
2250
  }
1763
- },
1764
- {
1765
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1766
- }
1767
- ).meta({
1768
- description: "Pagination cursor in base64url-encoded format",
1769
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
1770
- }),
1771
- limit: z7__namespace.string().regex(/^[1-9]\d*$/, {
1772
- message: "Limit must be a positive integer"
1773
- }).transform((val) => Number.parseInt(val, 10)).pipe(
1774
- z7__namespace.number().max(MAX_LIMIT, {
1775
- message: `Limit cannot exceed ${MAX_LIMIT}`
1776
- })
1777
- ).optional().default(DEFAULT_LIMIT).meta({
1778
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
1779
- example: 10
1780
- })
1781
- });
1782
- var GetOffersQueryParams = z7__namespace.object({
1783
- ...PaginationQueryParams.shape,
1784
- side: z7__namespace.enum(["buy", "sell"]).meta({
1785
- description: "Side of the offer.",
1786
- example: "buy"
1787
- }),
1788
- obligation_id: z7__namespace.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
1789
- description: "Offers obligation id",
1790
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1791
- })
1792
- });
1793
- var GetObligationsQueryParams = z7__namespace.object({
1794
- ...PaginationQueryParams.shape,
1795
- cursor: z7__namespace.string().optional().meta({
1796
- description: "Obligation id cursor",
1797
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1798
- })
1799
- });
1800
- var schemas = {
1801
- get_offers: GetOffersQueryParams,
1802
- get_obligations: GetObligationsQueryParams
1803
- };
1804
- function parse(action, query) {
1805
- return schemas[action].parse(query);
1806
- }
1807
- function safeParse(action, query, error) {
1808
- return schemas[action].safeParse(query, {
1809
- error
1810
- });
1811
- }
1812
-
1813
- // src/api/Schema/openapi.ts
1814
- var timestampExample = "2024-01-01T12:00:00.000Z";
1815
- var cursorExample = "eyJvZmZzZXQiOjEwMH0";
1816
- function makeSuccessResponse(parameters) {
1817
- const { dataSchema, dataDescription, dataExample, cursor } = parameters;
1818
- const withDataMeta = dataDescription ? dataSchema.meta({ description: dataDescription }) : dataSchema;
1819
- return v4.z.object({
1820
- status: v4.z.literal("success"),
1821
- cursor: v4.z.string().nullable(),
1822
- data: v4.z.any(),
1823
- meta: v4.z.object({
1824
- timestamp: v4.z.string()
1825
- })
1826
- }).extend({
1827
- data: withDataMeta
1828
- }).meta({
1829
- example: {
1830
- status: "success",
1831
- cursor,
1832
- data: dataExample,
1833
- meta: { timestamp: timestampExample }
1834
2251
  }
1835
2252
  });
1836
- }
1837
- var OffersSuccessResponseSchema = makeSuccessResponse({
1838
- dataSchema: v4.z.array(v4.z.any()),
1839
- dataDescription: "Offers matching the provided filters.",
1840
- dataExample: [toSnakeCase(Offer_exports.random())],
1841
- cursor: cursorExample
1842
- });
1843
- var ObligationsSuccessResponseSchema = makeSuccessResponse({
1844
- dataSchema: v4.z.array(v4.z.any()),
1845
- dataDescription: "Obligations known to the router.",
1846
- dataExample: [toSnakeCase(Obligation_exports.random())],
1847
- cursor: cursorExample
1848
- });
1849
- var RouterStatusSuccessResponseSchema = makeSuccessResponse({
1850
- dataSchema: RouterStatusResponse,
1851
- dataDescription: "Aggregated router status.",
1852
- dataExample: { status: "live" },
1853
- cursor: null
1854
- });
1855
- var CollectorsHealthSuccessResponseSchema = makeSuccessResponse({
1856
- dataSchema: CollectorsHealthResponse,
1857
- dataDescription: "Collectors health details and sync status.",
1858
- dataExample: [
1859
- {
1860
- name: "mempool_offers",
1861
- chain_id: "1",
1862
- block_number: 21345678,
1863
- updated_at: "2024-01-01T12:00:00.000Z",
1864
- lag: 0,
1865
- status: "live"
1866
- }
1867
- ],
1868
- cursor: null
1869
- });
1870
- var ChainsHealthSuccessResponseSchema = makeSuccessResponse({
1871
- dataSchema: ChainsHealthResponse,
1872
- dataDescription: "Latest processed block per chain.",
1873
- dataExample: [
1874
- {
1875
- chain_id: "1",
1876
- block_number: 21345678,
1877
- updated_at: "2024-01-01T12:00:00.000Z"
1878
- }
1879
- ],
1880
- cursor: null
1881
- });
1882
- var errorResponseSchema = v4.z.object({
1883
- status: v4.z.literal("error"),
1884
- error: v4.z.object({
1885
- code: v4.z.string(),
1886
- message: v4.z.string(),
1887
- details: v4.z.any().optional()
1888
- }),
1889
- meta: v4.z.object({
1890
- timestamp: v4.z.string()
1891
- })
1892
- }).meta({
1893
- description: "Error response wrapper.",
1894
- example: {
1895
- status: "error",
1896
- error: {
1897
- code: "VALIDATION_ERROR",
1898
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object",
1899
- details: [
1900
- {
1901
- field: "cursor",
1902
- issue: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1903
- }
1904
- ]
1905
- },
1906
- meta: {
1907
- timestamp: timestampExample
2253
+ if (error !== void 0) {
2254
+ switch (response.status) {
2255
+ case 401:
2256
+ throw new HttpUnauthorizedError();
2257
+ case 403:
2258
+ throw new HttpForbiddenError();
2259
+ case 429:
2260
+ throw new HttpRateLimitError();
1908
2261
  }
2262
+ throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2263
+ details: JSON.stringify(error)
2264
+ });
1909
2265
  }
1910
- });
1911
- var paths = {
1912
- "/v1/offers": {
1913
- get: {
1914
- summary: "Offers",
1915
- description: "Find offers that match specific criteria",
1916
- tags: ["Offers"],
1917
- requestParams: {
1918
- query: GetOffersQueryParams
1919
- },
1920
- responses: {
1921
- 200: {
1922
- description: "Success",
1923
- content: {
1924
- "application/json": {
1925
- schema: OffersSuccessResponseSchema
1926
- }
1927
- }
1928
- },
1929
- 400: {
1930
- description: "Bad Request",
1931
- content: {
1932
- "application/json": {
1933
- schema: errorResponseSchema
1934
- }
1935
- }
1936
- }
1937
- }
1938
- }
1939
- },
1940
- "/v1/obligations": {
1941
- get: {
1942
- summary: "Obligations",
1943
- description: "List obligations with pagination support",
1944
- tags: ["Obligations"],
1945
- requestParams: {
1946
- query: GetObligationsQueryParams
2266
+ const offers = data?.data.map((item) => {
2267
+ const { signature, ...rest } = item;
2268
+ return Offer_exports.fromSnakeCase({
2269
+ ...rest,
2270
+ offering: item.offering,
2271
+ maturity: Maturity_exports.from(item.maturity),
2272
+ loan_token: item.loan_token,
2273
+ collaterals: item.collaterals.map((collateral) => ({
2274
+ asset: collateral.asset,
2275
+ oracle: collateral.oracle,
2276
+ lltv: collateral.lltv
2277
+ })),
2278
+ callback: {
2279
+ ...item.callback,
2280
+ address: item.callback.address,
2281
+ data: item.callback.data
1947
2282
  },
1948
- responses: {
1949
- 200: {
1950
- description: "Success",
1951
- content: {
1952
- "application/json": {
1953
- schema: ObligationsSuccessResponseSchema
1954
- }
1955
- }
1956
- },
1957
- 400: {
1958
- description: "Bad Request",
1959
- content: {
1960
- "application/json": {
1961
- schema: errorResponseSchema
1962
- }
1963
- }
1964
- }
1965
- }
1966
- }
1967
- },
1968
- "/v1/health": {
1969
- get: {
1970
- summary: "Router status",
1971
- description: "Retrieve the aggregated status of the router.",
1972
- tags: ["Health"],
1973
- responses: {
1974
- 200: {
1975
- description: "Success",
1976
- content: {
1977
- "application/json": {
1978
- schema: RouterStatusSuccessResponseSchema
1979
- }
1980
- }
1981
- }
1982
- }
1983
- }
1984
- },
1985
- "/v1/health/collectors": {
1986
- get: {
1987
- summary: "Collectors health",
1988
- description: "Retrieve the block numbers processed by collectors and their sync status.",
1989
- tags: ["Health"],
1990
- responses: {
1991
- 200: {
1992
- description: "Success",
1993
- content: {
1994
- "application/json": {
1995
- schema: CollectorsHealthSuccessResponseSchema
1996
- }
1997
- }
1998
- }
1999
- }
2000
- }
2001
- },
2002
- "/v1/health/chains": {
2003
- get: {
2004
- summary: "Chains health",
2005
- description: "Retrieve the latest block processed for each chain.",
2006
- tags: ["Health"],
2007
- responses: {
2008
- 200: {
2009
- description: "Success",
2010
- content: {
2011
- "application/json": {
2012
- schema: ChainsHealthSuccessResponseSchema
2013
- }
2014
- }
2015
- }
2016
- }
2017
- }
2018
- }
2019
- };
2020
- var OpenApi = zodOpenapi.createDocument({
2021
- openapi: "3.1.0",
2022
- info: {
2023
- title: "Router API",
2024
- version: "1.0.0",
2025
- description: "API for the Morpho Router"
2026
- },
2027
- tags: [
2028
- {
2029
- name: "Offers"
2030
- },
2031
- {
2032
- name: "Obligations"
2033
- },
2034
- {
2035
- name: "Health"
2036
- }
2037
- ],
2038
- servers: [
2039
- {
2040
- url: "https://router.morpho.dev",
2041
- description: "Production server"
2042
- },
2043
- {
2044
- url: "http://localhost:7891",
2045
- description: "Local development server"
2046
- }
2047
- ],
2048
- paths
2049
- });
2050
-
2051
- // src/client/Client.ts
2052
- var Client_exports = {};
2053
- __export(Client_exports, {
2054
- HttpForbiddenError: () => HttpForbiddenError,
2055
- HttpGetApiFailedError: () => HttpGetApiFailedError,
2056
- HttpRateLimitError: () => HttpRateLimitError,
2057
- HttpUnauthorizedError: () => HttpUnauthorizedError,
2058
- InvalidUrlError: () => InvalidUrlError,
2059
- connect: () => connect,
2060
- getObligations: () => getObligations,
2061
- getOffers: () => getOffers
2062
- });
2063
- function connect(options) {
2064
- const u = new URL(options?.url || "https://router.morpho.dev");
2065
- if (u.protocol !== "http:" && u.protocol !== "https:") {
2066
- throw new InvalidUrlError(u.toString());
2067
- }
2068
- const headers = options?.headers ?? new Headers();
2069
- headers.set("Content-Type", "application/json");
2070
- options?.apiKey !== void 0 ? headers.set("X-API-Key", options.apiKey) : null;
2071
- const config = {
2072
- url: u,
2073
- headers
2074
- };
2075
- return {
2076
- ...config,
2077
- getOffers: (parameters) => getOffers(config, parameters),
2078
- getObligations: (parameters) => getObligations(config, parameters)
2079
- };
2080
- }
2081
- async function getOffers(config, parameters) {
2082
- const url = new URL(`${config.url.toString()}v1/offers`);
2083
- url.searchParams.set("side", parameters.side);
2084
- url.searchParams.set("obligation_id", parameters.obligationId.toString());
2085
- if (parameters.cursor) {
2086
- url.searchParams.set("cursor", parameters.cursor);
2087
- }
2088
- if (parameters.limit !== void 0) {
2089
- url.searchParams.set("limit", parameters.limit.toString());
2090
- }
2091
- const { cursor: returnedCursor, data: offers } = await getApi(config, url);
2092
- const routerOffers = offers.map(Offer_exports.fromSnakeCase);
2093
- return {
2094
- cursor: returnedCursor,
2095
- offers: routerOffers
2096
- };
2097
- }
2098
- async function getObligations(config, parameters) {
2099
- const url = new URL(`${config.url.toString()}v1/obligations`);
2100
- if (parameters?.cursor !== void 0) {
2101
- url.searchParams.set("cursor", parameters.cursor);
2102
- }
2103
- if (parameters?.limit !== void 0) {
2104
- url.searchParams.set("limit", parameters.limit.toString());
2105
- }
2106
- const { cursor: returnedCursor, data: items } = await getApi(config, url);
2107
- const obligations = items.map((item) => {
2108
- const obligation = Obligation_exports.fromSnakeCase(item);
2109
- const { obligationId: _, ...returned } = {
2110
- id: () => Obligation_exports.id(obligation),
2111
- ...obligation,
2112
- ...Quote_exports.fromSnakeCase({ obligation_id: item.id, ask: item.ask, bid: item.bid })
2113
- };
2114
- return returned;
2115
- });
2283
+ ...signature !== null ? { signature: item.signature } : void 0
2284
+ });
2285
+ }) ?? [];
2116
2286
  return {
2117
- cursor: returnedCursor,
2118
- obligations
2287
+ cursor: data?.cursor ?? null,
2288
+ offers
2119
2289
  };
2120
2290
  }
2121
- async function getApi(config, url) {
2122
- const pathname = url.pathname;
2123
- let action;
2124
- switch (true) {
2125
- case pathname.includes("/v1/offers"):
2126
- action = "get_offers";
2127
- break;
2128
- case pathname.includes("/v1/obligations"):
2129
- action = "get_obligations";
2130
- break;
2131
- default:
2132
- throw new HttpGetApiFailedError("Unknown endpoint", {
2133
- details: `Unsupported path: ${pathname}`
2134
- });
2135
- }
2136
- const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
2137
- if (!schemaParseResult.success) {
2138
- throw new HttpGetApiFailedError(`Invalid URL parameters`, {
2139
- details: schemaParseResult.error.issues[0]?.message
2140
- });
2141
- }
2142
- const response = await fetch(url.toString(), {
2143
- method: "GET",
2144
- headers: config.headers
2291
+ async function getObligations(apiClient, parameters) {
2292
+ const { data, error, response } = await apiClient.GET("/v1/obligations", {
2293
+ params: {
2294
+ query: {
2295
+ cursor: parameters?.cursor,
2296
+ limit: parameters?.limit
2297
+ }
2298
+ }
2145
2299
  });
2146
- if (!response.ok) {
2300
+ if (error !== void 0) {
2147
2301
  switch (response.status) {
2148
2302
  case 401:
2149
2303
  throw new HttpUnauthorizedError();
@@ -2153,10 +2307,31 @@ async function getApi(config, url) {
2153
2307
  throw new HttpRateLimitError();
2154
2308
  }
2155
2309
  throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2156
- details: await response.text()
2310
+ details: JSON.stringify(error)
2157
2311
  });
2158
2312
  }
2159
- return response.json();
2313
+ const obligations = data?.data.map((item) => {
2314
+ const obligation = Obligation_exports.fromSnakeCase({
2315
+ chain_id: item.chain_id,
2316
+ loan_token: item.loan_token,
2317
+ collaterals: item.collaterals.map((collateral) => ({
2318
+ asset: collateral.asset,
2319
+ oracle: collateral.oracle,
2320
+ lltv: collateral.lltv
2321
+ })),
2322
+ maturity: Maturity_exports.from(item.maturity)
2323
+ });
2324
+ const { obligationId: _, ...returned } = {
2325
+ id: () => Obligation_exports.id(obligation),
2326
+ ...obligation,
2327
+ ...Quote_exports.fromSnakeCase({ obligation_id: item.id, ask: item.ask, bid: item.bid })
2328
+ };
2329
+ return returned;
2330
+ }) ?? [];
2331
+ return {
2332
+ cursor: data?.cursor ?? null,
2333
+ obligations
2334
+ };
2160
2335
  }
2161
2336
  var InvalidUrlError = class extends BaseError {
2162
2337
  constructor(url) {
@@ -2199,13 +2374,21 @@ var HttpGetApiFailedError = class extends BaseError {
2199
2374
  }
2200
2375
  };
2201
2376
 
2202
- // src/collectors/validations/Validation.ts
2203
- var Validation_exports = {};
2204
- __export(Validation_exports, {
2205
- run: () => run
2377
+ // src/gatekeeper/Gate.ts
2378
+ var Gate_exports = {};
2379
+ __export(Gate_exports, {
2380
+ batch: () => batch2,
2381
+ run: () => run,
2382
+ single: () => single
2206
2383
  });
2384
+ function single(name, run2) {
2385
+ return { kind: "single", name, run: run2 };
2386
+ }
2387
+ function batch2(name, run2) {
2388
+ return { kind: "batch", name, run: run2 };
2389
+ }
2207
2390
  async function run(parameters) {
2208
- const { items, rules, ctx = {}, chunkSize } = parameters;
2391
+ const { items, rules, chunkSize } = parameters;
2209
2392
  const issues = [];
2210
2393
  let validItems = items.slice();
2211
2394
  for (const rule of rules) {
@@ -2214,7 +2397,7 @@ async function run(parameters) {
2214
2397
  if (rule.kind === "single") {
2215
2398
  for (let i = 0; i < validItems.length; i++) {
2216
2399
  const item = validItems[i];
2217
- const issue = await rule.run(item, ctx);
2400
+ const issue = await rule.run(item);
2218
2401
  if (issue) {
2219
2402
  issues.push({ ...issue, ruleName: rule.name, item });
2220
2403
  indicesToRemove.add(i);
@@ -2222,7 +2405,7 @@ async function run(parameters) {
2222
2405
  }
2223
2406
  } else if (rule.kind === "batch") {
2224
2407
  const exec = async (slice, offset) => {
2225
- const map = await rule.run(slice, ctx);
2408
+ const map = await rule.run(slice);
2226
2409
  for (let i = 0; i < slice.length; i++) {
2227
2410
  const issue = map.get(i);
2228
2411
  if (issue !== void 0) {
@@ -2246,157 +2429,265 @@ async function run(parameters) {
2246
2429
  };
2247
2430
  }
2248
2431
 
2249
- // src/collectors/validations/ValidationRule.ts
2250
- var ValidationRule_exports = {};
2251
- __export(ValidationRule_exports, {
2252
- batch: () => batch2,
2253
- morpho: () => morpho,
2254
- single: () => single
2432
+ // src/gatekeeper/GateConfig.ts
2433
+ var GateConfig_exports = {};
2434
+ __export(GateConfig_exports, {
2435
+ assets: () => assets,
2436
+ configs: () => configs,
2437
+ getCallback: () => getCallback,
2438
+ getCallbackAddresses: () => getCallbackAddresses,
2439
+ getCallbackType: () => getCallbackType,
2440
+ getCallbackTypeAddresses: () => getCallbackTypeAddresses
2255
2441
  });
2256
- function single(name, run2) {
2257
- return { kind: "single", name, run: run2 };
2442
+ function getCallback(chain2, type) {
2443
+ return configs[chain2].callbacks?.find((c) => c.type === type);
2258
2444
  }
2259
- function batch2(name, run2) {
2260
- return { kind: "batch", name, run: run2 };
2445
+ function getCallbackType(chain2, address) {
2446
+ return configs[chain2].callbacks?.find(
2447
+ (c) => c.type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */ && c.addresses.includes(address?.toLowerCase())
2448
+ )?.type;
2261
2449
  }
2262
- function morpho() {
2263
- const chainId = single("chain_id", (offer, { chain }) => {
2264
- if (chain.id !== offer.chainId) {
2265
- return {
2266
- message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
2267
- };
2268
- }
2269
- });
2270
- const loanToken = single("loan_token", (offer, { chain }) => {
2271
- const tokens = new Set(
2272
- Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
2273
- );
2274
- if (!tokens.has(offer.loanToken.toLowerCase())) {
2275
- return {
2276
- message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
2277
- };
2278
- }
2279
- });
2280
- const expiry = single("expiry", (offer, _) => {
2281
- if (offer.expiry < Math.floor(Date.now() / 1e3)) {
2282
- return { message: "Expiry mismatch" };
2283
- }
2284
- });
2285
- const sellEmptyCallback = single(
2286
- "sell_offers_empty_callback",
2287
- (offer, _) => {
2288
- if (!offer.buy && offer.callback.data === "0x") {
2289
- return { message: "Sell offers require a non-empty callback." };
2290
- }
2291
- }
2292
- );
2293
- const buyNonEmptyCallback = single(
2294
- "buy_offers_non_empty_callback",
2295
- (offer, _) => {
2296
- if (offer.buy && offer.callback.data !== "0x") {
2297
- const allowed = new Set(
2298
- Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.BuyVaultV1Callback].map(
2299
- (a) => a.toLowerCase()
2300
- )
2301
- );
2302
- const callbackAddress = offer.callback.address?.toLowerCase();
2303
- if (!callbackAddress || !allowed.has(callbackAddress)) {
2304
- return {
2305
- message: "Buy offers with non-empty callback must use a whitelisted BuyVaultV1Callback."
2306
- };
2307
- }
2308
- }
2450
+ function getCallbackTypeAddresses(chain2, type) {
2451
+ if (type === "buy_with_empty_callback" /* BuyWithEmptyCallback */) {
2452
+ return [];
2453
+ }
2454
+ const match = configs[chain2].callbacks?.find((c) => c.type === type);
2455
+ return match && "addresses" in match ? match.addresses : [];
2456
+ }
2457
+ var getCallbackAddresses = (chain2) => {
2458
+ return configs[chain2].callbacks?.filter((c) => c.type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */).flatMap((c) => c.addresses) ?? [];
2459
+ };
2460
+ var assets = {
2461
+ [ChainId.ETHEREUM.toString()]: [
2462
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
2463
+ // USDC
2464
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F",
2465
+ // DAI
2466
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
2467
+ // WETH
2468
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
2469
+ // WBTC
2470
+ ],
2471
+ [ChainId.BASE.toString()]: [
2472
+ "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2473
+ // USDC
2474
+ "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
2475
+ // DAI
2476
+ "0x4200000000000000000000000000000000000006",
2477
+ // WETH
2478
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
2479
+ // WBTC
2480
+ ],
2481
+ [ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
2482
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
2483
+ // USDC
2484
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F",
2485
+ // DAI
2486
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
2487
+ // WETH
2488
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
2489
+ // WBTC
2490
+ ],
2491
+ [ChainId.ANVIL.toString()]: [
2492
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
2493
+ // USDC
2494
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F",
2495
+ // DAI
2496
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
2497
+ // WETH
2498
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
2499
+ // WBTC
2500
+ ]
2501
+ };
2502
+ var configs = {
2503
+ ethereum: {
2504
+ callbacks: [
2505
+ {
2506
+ type: "buy_vault_v1_callback" /* BuyVaultV1Callback */,
2507
+ addresses: [
2508
+ "0x3333333333333333333333333333333333333333",
2509
+ "0x4444444444444444444444444444444444444444"
2510
+ ],
2511
+ vaultFactories: [
2512
+ "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
2513
+ //v1.0
2514
+ "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
2515
+ //v1.1
2516
+ ]
2517
+ },
2518
+ {
2519
+ type: "sell_erc20_callback" /* SellERC20Callback */,
2520
+ addresses: [
2521
+ "0x1111111111111111111111111111111111111111",
2522
+ "0x2222222222222222222222222222222222222222"
2523
+ ]
2524
+ },
2525
+ { type: "buy_with_empty_callback" /* BuyWithEmptyCallback */ }
2526
+ ],
2527
+ maturities: ["end_of_month" /* EndOfMonth */, "end_of_next_month" /* EndOfNextMonth */]
2528
+ },
2529
+ base: {
2530
+ callbacks: [
2531
+ {
2532
+ type: "buy_vault_v1_callback" /* BuyVaultV1Callback */,
2533
+ addresses: [
2534
+ "0x3333333333333333333333333333333333333333",
2535
+ "0x4444444444444444444444444444444444444444"
2536
+ ],
2537
+ vaultFactories: [
2538
+ "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
2539
+ //v1.0
2540
+ "0xFf62A7c278C62eD665133147129245053Bbf5918"
2541
+ //v1.1
2542
+ ]
2543
+ },
2544
+ {
2545
+ type: "sell_erc20_callback" /* SellERC20Callback */,
2546
+ addresses: [
2547
+ "0x1111111111111111111111111111111111111111",
2548
+ "0x2222222222222222222222222222222222222222"
2549
+ ]
2550
+ },
2551
+ { type: "buy_with_empty_callback" /* BuyWithEmptyCallback */ }
2552
+ ],
2553
+ maturities: ["end_of_month" /* EndOfMonth */, "end_of_next_month" /* EndOfNextMonth */]
2554
+ },
2555
+ "ethereum-virtual-testnet": {
2556
+ callbacks: [
2557
+ {
2558
+ type: "buy_vault_v1_callback" /* BuyVaultV1Callback */,
2559
+ addresses: [
2560
+ "0x3333333333333333333333333333333333333333",
2561
+ "0x4444444444444444444444444444444444444444"
2562
+ ],
2563
+ vaultFactories: [
2564
+ "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
2565
+ //v1.0
2566
+ "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
2567
+ //v1.1
2568
+ ]
2569
+ },
2570
+ {
2571
+ type: "sell_erc20_callback" /* SellERC20Callback */,
2572
+ addresses: [
2573
+ "0x1111111111111111111111111111111111111111",
2574
+ "0x2222222222222222222222222222222222222222"
2575
+ ]
2576
+ },
2577
+ { type: "buy_with_empty_callback" /* BuyWithEmptyCallback */ }
2578
+ ],
2579
+ maturities: ["end_of_month" /* EndOfMonth */, "end_of_next_month" /* EndOfNextMonth */]
2580
+ },
2581
+ anvil: {
2582
+ callbacks: [
2583
+ {
2584
+ type: "buy_vault_v1_callback" /* BuyVaultV1Callback */,
2585
+ addresses: [
2586
+ "0x3333333333333333333333333333333333333333",
2587
+ "0x4444444444444444444444444444444444444444"
2588
+ ],
2589
+ vaultFactories: [
2590
+ "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
2591
+ //v1.0
2592
+ "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
2593
+ //v1.1
2594
+ ]
2595
+ },
2596
+ {
2597
+ type: "sell_erc20_callback" /* SellERC20Callback */,
2598
+ addresses: [
2599
+ "0x1111111111111111111111111111111111111111",
2600
+ "0x2222222222222222222222222222222222222222"
2601
+ ]
2602
+ },
2603
+ { type: "buy_with_empty_callback" /* BuyWithEmptyCallback */ }
2604
+ ],
2605
+ maturities: ["end_of_month" /* EndOfMonth */, "end_of_next_month" /* EndOfNextMonth */]
2606
+ }
2607
+ };
2608
+
2609
+ // src/gatekeeper/Gatekeeper.ts
2610
+ var Gatekeeper_exports = {};
2611
+ __export(Gatekeeper_exports, {
2612
+ create: () => create
2613
+ });
2614
+ function create(parameters) {
2615
+ return {
2616
+ isAllowed: async (offers) => {
2617
+ return await run({
2618
+ items: offers,
2619
+ rules: parameters.rules
2620
+ });
2309
2621
  }
2310
- );
2311
- const sellNonWhitelistedCallback = single(
2312
- "sell_offers_non_whitelisted_callback",
2313
- (offer, _) => {
2314
- if (!offer.buy && offer.callback.data !== "0x") {
2315
- const allowed = new Set(
2316
- Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.SellERC20Callback].map(
2317
- (a) => a.toLowerCase()
2318
- )
2319
- );
2320
- const callbackAddress = offer.callback.address?.toLowerCase();
2321
- if (!callbackAddress || !allowed.has(callbackAddress)) {
2322
- return { message: "Sell offer callback address is not whitelisted." };
2323
- }
2324
- }
2622
+ };
2623
+ }
2624
+
2625
+ // src/gatekeeper/Rules.ts
2626
+ var Rules_exports = {};
2627
+ __export(Rules_exports, {
2628
+ callback: () => callback,
2629
+ chain: () => chain,
2630
+ maturity: () => maturity,
2631
+ token: () => token,
2632
+ validity: () => validity
2633
+ });
2634
+ function validity(parameters) {
2635
+ const { chain: chain2, client } = parameters;
2636
+ const sellErc20CallbackInvalid = single("sell_erc20_callback_invalid", (offer) => {
2637
+ const callbackType = getCallbackType(chain2.name, offer.callback.address);
2638
+ if (callbackType !== Callback_exports.CallbackType.SellERC20Callback) {
2639
+ return;
2325
2640
  }
2326
- );
2327
- const sellCallbackDataInvalid = single(
2328
- "sell_offers_callback_data_invalid",
2329
- (offer, _) => {
2330
- if (!offer.buy && offer.callback.data !== "0x") {
2331
- try {
2332
- const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
2333
- if (decoded.length === 0) {
2334
- return { message: "Sell offer callback data must include at least one collateral." };
2335
- }
2336
- } catch (_2) {
2337
- return { message: "Sell offer callback data cannot be decoded." };
2338
- }
2339
- }
2641
+ const decoded = Callback_exports.decode(callbackType, offer.callback.data);
2642
+ if (decoded.length === 0) {
2643
+ return { message: "Callback data cannot be decoded or is empty." };
2340
2644
  }
2341
- );
2342
- const sellCallbackCollateralInvalid = single(
2343
- "sell_offers_callback_collateral_invalid",
2344
- (offer, _) => {
2345
- if (!offer.buy && offer.callback.data !== "0x") {
2346
- try {
2347
- const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
2348
- const offerCollaterals = new Set(
2349
- offer.collaterals.map((c) => c.asset.toLowerCase())
2350
- );
2351
- for (const { collateral } of decoded) {
2352
- if (!offerCollaterals.has(collateral.toLowerCase())) {
2353
- return { message: "Sell callback collateral is not part of offer collaterals." };
2354
- }
2355
- }
2356
- } catch (_2) {
2357
- }
2645
+ if (callbackType === Callback_exports.CallbackType.SellERC20Callback) {
2646
+ const offerCollaterals = new Set(
2647
+ offer.collaterals.map((c) => c.asset.toLowerCase())
2648
+ );
2649
+ if (decoded.length !== offer.collaterals.length) {
2650
+ return {
2651
+ message: `Sell callback collateral length mismatch. Expected ${offer.collaterals.length}, got ${decoded.length}.`
2652
+ };
2358
2653
  }
2359
- }
2360
- );
2361
- const buyCallbackDataInvalid = single(
2362
- "buy_offers_callback_data_invalid",
2363
- (offer, _) => {
2364
- if (offer.buy && offer.callback.data !== "0x") {
2365
- try {
2366
- const decoded = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2367
- if (decoded.length === 0) {
2368
- return { message: "Buy offer callback data must include at least one vault." };
2369
- }
2370
- } catch (_2) {
2371
- return { message: "Buy offer callback data cannot be decoded." };
2654
+ for (const { collateral } of decoded) {
2655
+ if (!offerCollaterals.has(collateral.toLowerCase())) {
2656
+ return { message: "Sell callback collateral is not part of offer collaterals." };
2372
2657
  }
2373
2658
  }
2374
2659
  }
2375
- );
2660
+ });
2376
2661
  const buyCallbackVaultInvalid = batch2(
2377
2662
  "buy_offers_callback_vault_invalid",
2378
- async (offers, { client, chain }) => {
2663
+ async (offers) => {
2379
2664
  const validationIssues = /* @__PURE__ */ new Map();
2380
2665
  const offersByVaultAddress = /* @__PURE__ */ new Map();
2381
2666
  for (let i = 0; i < offers.length; i++) {
2382
2667
  const offer = offers[i];
2383
- if (offer.buy && offer.callback.data !== "0x") {
2384
- try {
2385
- const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2386
- for (const { vault } of callbackVaults) {
2387
- const normalizedVaultAddress = vault.toLowerCase();
2388
- if (!offersByVaultAddress.has(normalizedVaultAddress)) {
2389
- offersByVaultAddress.set(normalizedVaultAddress, []);
2390
- }
2391
- offersByVaultAddress.get(normalizedVaultAddress).push({ index: i, offer });
2668
+ const callbackType = getCallbackType(chain2.name, offer.callback.address);
2669
+ if (callbackType !== Callback_exports.CallbackType.BuyVaultV1Callback) {
2670
+ continue;
2671
+ }
2672
+ try {
2673
+ const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2674
+ for (const { vault } of callbackVaults) {
2675
+ const normalizedVaultAddress = vault.toLowerCase();
2676
+ if (!offersByVaultAddress.has(normalizedVaultAddress)) {
2677
+ offersByVaultAddress.set(normalizedVaultAddress, []);
2392
2678
  }
2393
- } catch (_) {
2679
+ offersByVaultAddress.get(normalizedVaultAddress).push({ index: i, offer });
2394
2680
  }
2681
+ } catch (_) {
2395
2682
  }
2396
2683
  }
2397
2684
  const uniqueVaultAddresses = Array.from(offersByVaultAddress.keys());
2398
2685
  if (uniqueVaultAddresses.length === 0) return validationIssues;
2399
- const whitelistedFactories = Object.values(chain.vaultV1Factory);
2686
+ const allowedFactories = getCallback(
2687
+ chain2.name,
2688
+ Callback_exports.CallbackType.BuyVaultV1Callback
2689
+ )?.vaultFactories.map((f) => f.toLowerCase());
2690
+ if (!allowedFactories) return validationIssues;
2400
2691
  const multicallContracts = [];
2401
2692
  for (const vaultAddress of uniqueVaultAddresses) {
2402
2693
  multicallContracts.push({
@@ -2404,7 +2695,7 @@ function morpho() {
2404
2695
  abi: Abi_exports.ERC4626,
2405
2696
  functionName: "asset"
2406
2697
  });
2407
- for (const factoryAddress of whitelistedFactories) {
2698
+ for (const factoryAddress of allowedFactories) {
2408
2699
  multicallContracts.push({
2409
2700
  address: factoryAddress,
2410
2701
  abi: Abi_exports.MetaMorphoFactory,
@@ -2419,7 +2710,7 @@ function morpho() {
2419
2710
  });
2420
2711
  const vaultAssetByAddress = /* @__PURE__ */ new Map();
2421
2712
  const registeredVaults = /* @__PURE__ */ new Set();
2422
- const numberOfFactories = whitelistedFactories.length;
2713
+ const numberOfFactories = allowedFactories.length;
2423
2714
  let resultIndex = 0;
2424
2715
  for (const vaultAddress of uniqueVaultAddresses) {
2425
2716
  const assetCallResult = multicallResults[resultIndex++];
@@ -2478,30 +2769,61 @@ function morpho() {
2478
2769
  return validationIssues;
2479
2770
  }
2480
2771
  );
2481
- const maturity = single("maturity", (offer, _) => {
2482
- const allowedMaturities = [Maturity_exports.from("end_of_month"), Maturity_exports.from("end_of_next_month")];
2483
- if (!allowedMaturities.includes(offer.maturity)) {
2484
- return {
2485
- message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
2486
- };
2772
+ const expiry = single("expiry", (offer) => {
2773
+ if (offer.expiry < Math.floor(Date.now() / 1e3)) {
2774
+ return { message: "Expiry mismatch" };
2487
2775
  }
2488
2776
  });
2489
- return [
2490
- chainId,
2491
- loanToken,
2492
- expiry,
2493
- maturity,
2494
- // note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
2495
- // integrators should be able to choose if they want to keep the offer or not
2496
- sellEmptyCallback,
2497
- buyNonEmptyCallback,
2498
- sellNonWhitelistedCallback,
2499
- sellCallbackDataInvalid,
2500
- sellCallbackCollateralInvalid,
2501
- buyCallbackDataInvalid,
2502
- buyCallbackVaultInvalid
2503
- ];
2777
+ return [expiry, sellErc20CallbackInvalid, buyCallbackVaultInvalid];
2504
2778
  }
2779
+ var chain = ({ chain: chain2 }) => single("chain_id", (offer) => {
2780
+ if (chain2.id !== offer.chainId) {
2781
+ return {
2782
+ message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain2.id})`
2783
+ };
2784
+ }
2785
+ });
2786
+ var maturity = ({ maturities }) => single("maturity", (offer) => {
2787
+ const allowedMaturities = maturities.map((m) => Maturity_exports.from(m));
2788
+ if (!allowedMaturities.includes(offer.maturity)) {
2789
+ return {
2790
+ message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
2791
+ };
2792
+ }
2793
+ });
2794
+ var callback = ({
2795
+ callbacks,
2796
+ allowedAddresses
2797
+ }) => single("callback", (offer) => {
2798
+ if (Callback_exports.isEmptyCallback(offer) && offer.buy && !callbacks?.find((c) => c === Callback_exports.CallbackType.BuyWithEmptyCallback)) {
2799
+ return {
2800
+ message: "Buy offers with empty callback not allowed."
2801
+ };
2802
+ }
2803
+ if (Callback_exports.isEmptyCallback(offer) && !offer.buy) {
2804
+ return {
2805
+ message: "Sell offers require a non-empty callback."
2806
+ };
2807
+ }
2808
+ if (!Callback_exports.isEmptyCallback(offer)) {
2809
+ if (!allowedAddresses.includes(offer.callback.address?.toLowerCase())) {
2810
+ return {
2811
+ message: `Callback address ${offer.callback.address} is not allowed.`
2812
+ };
2813
+ }
2814
+ }
2815
+ });
2816
+ var token = ({ assets: assets2 }) => single("token", (offer) => {
2817
+ const allowedAssets = assets2?.map((asset) => asset.toLowerCase());
2818
+ if (!allowedAssets || allowedAssets.length === 0) return { message: "No allowed assets" };
2819
+ if (!allowedAssets.includes(offer.loanToken.toLowerCase()))
2820
+ return { message: "Loan token is not allowed" };
2821
+ if (offer.collaterals.some(
2822
+ (collateral) => !allowedAssets.includes(collateral.asset.toLowerCase())
2823
+ ))
2824
+ return { message: "Collateral is not allowed" };
2825
+ return void 0;
2826
+ });
2505
2827
 
2506
2828
  // src/mempool/MempoolClient.ts
2507
2829
  var MempoolClient_exports = {};
@@ -2510,7 +2832,7 @@ __export(MempoolClient_exports, {
2510
2832
  });
2511
2833
  var DEFAULT_BATCH_SIZE2 = 100;
2512
2834
  var DEFAULT_BLOCK_WINDOW2 = 100;
2513
- function from9(parameters) {
2835
+ function from10(parameters) {
2514
2836
  const config = {
2515
2837
  client: parameters.client,
2516
2838
  mempoolAddress: parameters.mempoolAddress,
@@ -2519,24 +2841,24 @@ function from9(parameters) {
2519
2841
  return {
2520
2842
  add: (parameters2) => add(config, parameters2),
2521
2843
  get: (parameters2) => get(config, parameters2),
2522
- watch: (parameters2) => watch(config, parameters2),
2523
2844
  stream: (parameters2) => streamOffers(config, parameters2)
2524
2845
  };
2525
2846
  }
2526
- async function add(config, parameters) {
2527
- const offer = Offer_exports.from(parameters.offer);
2847
+ async function add(config, offers) {
2528
2848
  if (!config.client.account) throw new WalletAccountNotSetError();
2849
+ const tree = Tree_exports.from(offers.map((o) => Offer_exports.from(o)));
2529
2850
  const chainId = await getChainId(config.client);
2530
- if (BigInt(chainId) !== offer.chainId)
2531
- throw new ChainIdMismatchError(offer.chainId, BigInt(chainId));
2851
+ for (const offer of tree.offers) {
2852
+ if (BigInt(chainId) !== offer.chainId)
2853
+ throw new ChainIdMismatchError(offer.chainId, BigInt(chainId));
2854
+ }
2532
2855
  try {
2533
- const tx = await config.client.sendTransaction({
2856
+ return await config.client.sendTransaction({
2534
2857
  chain: config.client.chain,
2535
2858
  account: config.client.account,
2536
2859
  to: config.mempoolAddress,
2537
- data: Offer_exports.encode(offer)
2860
+ data: Tree_exports.encode(tree)
2538
2861
  });
2539
- return { offer, txHash: tx };
2540
2862
  } catch (error) {
2541
2863
  throw new ViemClientError(error instanceof Error ? error.message : "Unknown error");
2542
2864
  }
@@ -2546,40 +2868,17 @@ async function* get(config, parameters) {
2546
2868
  loanToken,
2547
2869
  blockNumberGte,
2548
2870
  blockNumberLte,
2549
- order = "desc",
2871
+ order: order2 = "desc",
2550
2872
  options: { maxBatchSize = DEFAULT_BATCH_SIZE2 } = {}
2551
2873
  } = parameters || {};
2552
2874
  yield* streamOffers(config, {
2553
2875
  loanToken,
2554
- order,
2876
+ order: order2,
2555
2877
  blockNumberGte,
2556
2878
  blockNumberLte,
2557
2879
  options: { maxBatchSize, blockWindow: config.blockWindow }
2558
2880
  });
2559
2881
  }
2560
- function watch(config, parameters) {
2561
- const {
2562
- loanToken,
2563
- lastSyncedBlock,
2564
- polling: { interval = 3e4, maxBatchSize = DEFAULT_BATCH_SIZE2 } = {},
2565
- onOffers
2566
- } = parameters;
2567
- return poll(
2568
- async () => {
2569
- const blockNumberGte = await lastSyncedBlock();
2570
- const stream = streamOffers(config, {
2571
- loanToken,
2572
- order: "asc",
2573
- blockNumberGte,
2574
- options: { maxBatchSize, blockWindow: config.blockWindow }
2575
- });
2576
- for await (const { offers, blockNumber } of stream) {
2577
- await onOffers(offers, blockNumber);
2578
- }
2579
- },
2580
- { interval }
2581
- );
2582
- }
2583
2882
  var chainIdCache = /* @__PURE__ */ new Map();
2584
2883
  var getChainId = async (client) => {
2585
2884
  if (chainIdCache.has(client.uid)) return chainIdCache.get(client.uid);
@@ -2592,7 +2891,7 @@ async function* streamOffers(config, parameters) {
2592
2891
  loanToken,
2593
2892
  blockNumberGte,
2594
2893
  blockNumberLte,
2595
- order = "desc",
2894
+ order: order2 = "desc",
2596
2895
  options: { maxBatchSize = DEFAULT_BATCH_SIZE2, blockWindow = config.blockWindow } = {}
2597
2896
  } = parameters;
2598
2897
  const stream = Chain_exports.streamLogs({
@@ -2606,28 +2905,29 @@ async function* streamOffers(config, parameters) {
2606
2905
  },
2607
2906
  blockNumberGte,
2608
2907
  blockNumberLte,
2609
- order,
2908
+ order: order2,
2610
2909
  options: { maxBatchSize, blockWindow }
2611
2910
  });
2612
- let blockNumber = order === "asc" ? blockNumberGte : blockNumberLte;
2911
+ let blockNumber = order2 === "asc" ? blockNumberGte : blockNumberLte;
2613
2912
  for await (const { logs, blockNumber: newBlockNumber } of stream) {
2614
2913
  blockNumber = newBlockNumber;
2615
2914
  if (logs.length === 0) break;
2616
- let offersAndBlockNumbers = logs.map((log) => {
2915
+ const offers = [];
2916
+ for (const log of logs) {
2917
+ if (!log) continue;
2617
2918
  const [payload] = viem.decodeAbiParameters([{ type: "bytes" }], log.data);
2618
2919
  try {
2619
- return { offer: Offer_exports.decode(payload, log.blockNumber), blockNumber: log.blockNumber };
2920
+ const tree = Tree_exports.decode(payload);
2921
+ for (const offer of tree.offers) {
2922
+ if (loanToken && offer.loanToken.toLowerCase() !== loanToken.toLowerCase()) continue;
2923
+ offers.push({ ...offer, blockNumber: Number(log.blockNumber) });
2924
+ }
2620
2925
  } catch (_) {
2621
- return null;
2622
2926
  }
2623
- }).filter((item) => item !== null);
2624
- if (loanToken)
2625
- offersAndBlockNumbers = offersAndBlockNumbers.filter(
2626
- (o) => o.offer.loanToken.toLowerCase() === loanToken.toLowerCase()
2627
- );
2628
- if (offersAndBlockNumbers.length === 0) continue;
2927
+ }
2928
+ if (offers.length === 0) continue;
2629
2929
  yield {
2630
- offers: offersAndBlockNumbers.map((item) => item.offer),
2930
+ offers,
2631
2931
  blockNumber
2632
2932
  };
2633
2933
  }
@@ -2655,7 +2955,127 @@ var ChainIdMismatchError = class extends BaseError {
2655
2955
 
2656
2956
  // src/mempool/MempoolClient.ts
2657
2957
  function connect2(parameters) {
2658
- return from9(parameters);
2958
+ return from10(parameters);
2959
+ }
2960
+
2961
+ // src/utils/index.ts
2962
+ var utils_exports = {};
2963
+ __export(utils_exports, {
2964
+ BaseError: () => BaseError,
2965
+ Time: () => time_exports,
2966
+ batch: () => batch,
2967
+ batchMulticall: () => batchMulticall,
2968
+ fromSnakeCase: () => fromSnakeCase,
2969
+ lazy: () => lazy,
2970
+ max: () => max,
2971
+ min: () => min,
2972
+ poll: () => poll,
2973
+ retry: () => retry,
2974
+ stringifyBigint: () => stringifyBigint,
2975
+ toSnakeCase: () => toSnakeCase,
2976
+ wait: () => wait
2977
+ });
2978
+
2979
+ // src/utils/retry.ts
2980
+ var retry = async (fn, attempts = 3, delayMs = 50) => {
2981
+ let lastErr;
2982
+ for (let i = 0; i < attempts; i++) {
2983
+ try {
2984
+ return await fn();
2985
+ } catch (err) {
2986
+ lastErr = err;
2987
+ if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
2988
+ }
2989
+ }
2990
+ throw lastErr;
2991
+ };
2992
+
2993
+ // src/utils/batchMulticall.ts
2994
+ async function batchMulticall(parameters) {
2995
+ const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
2996
+ const results = [];
2997
+ for (const callsBatch of batch(calls, batchSize)) {
2998
+ const batchResults = await retry(
2999
+ () => client.multicall({
3000
+ allowFailure: false,
3001
+ contracts: callsBatch,
3002
+ ...blockNumber ? { blockNumber } : {}
3003
+ }),
3004
+ retryAttempts,
3005
+ retryDelayMs
3006
+ );
3007
+ results.push(...batchResults);
3008
+ }
3009
+ return results;
3010
+ }
3011
+
3012
+ // src/utils/lazy.ts
3013
+ function lazy(pollFn) {
3014
+ return () => async function* () {
3015
+ let active = true;
3016
+ let resolveNext = null;
3017
+ const queue = [];
3018
+ const wait2 = () => new Promise((resolve) => {
3019
+ resolveNext = resolve;
3020
+ });
3021
+ const emit = (item) => {
3022
+ queue.push(item);
3023
+ resolveNext?.();
3024
+ resolveNext = null;
3025
+ };
3026
+ let unpoll = null;
3027
+ const stop = () => {
3028
+ active = false;
3029
+ unpoll?.();
3030
+ resolveNext?.();
3031
+ resolveNext = null;
3032
+ };
3033
+ unpoll = pollFn(emit, { stop });
3034
+ try {
3035
+ while (active) {
3036
+ if (queue.length === 0) await wait2();
3037
+ while (queue.length > 0 && active) yield queue.shift();
3038
+ }
3039
+ } finally {
3040
+ stop();
3041
+ }
3042
+ }();
3043
+ }
3044
+
3045
+ // src/utils/wait.ts
3046
+ async function wait(time) {
3047
+ return new Promise((res) => setTimeout(res, time));
3048
+ }
3049
+
3050
+ // src/utils/poll.ts
3051
+ function poll(fn, { interval }) {
3052
+ let active = true;
3053
+ const unwatch = () => active = false;
3054
+ const watch = async () => {
3055
+ await wait(interval);
3056
+ const poll2 = async () => {
3057
+ if (!active) return;
3058
+ await fn({ unpoll: unwatch });
3059
+ await wait(interval);
3060
+ poll2();
3061
+ };
3062
+ poll2();
3063
+ };
3064
+ watch();
3065
+ return unwatch;
3066
+ }
3067
+
3068
+ // src/utils/time.ts
3069
+ var time_exports = {};
3070
+ __export(time_exports, {
3071
+ max: () => max2,
3072
+ now: () => now
3073
+ });
3074
+ function now() {
3075
+ return Math.floor(Date.now() / 1e3);
3076
+ }
3077
+ function max2() {
3078
+ return 864e16;
2659
3079
  }
2660
3080
 
2661
3081
  exports.Abi = Abi_exports;
@@ -2666,6 +3086,8 @@ exports.Collateral = Collateral_exports;
2666
3086
  exports.Cursor = Cursor_exports;
2667
3087
  exports.Errors = Errors_exports;
2668
3088
  exports.Format = Format_exports;
3089
+ exports.GateConfig = GateConfig_exports;
3090
+ exports.Gatekeeper = Gatekeeper_exports;
2669
3091
  exports.LLTV = LLTV_exports;
2670
3092
  exports.Liquidity = Liquidity_exports;
2671
3093
  exports.Maturity = Maturity_exports;
@@ -2675,9 +3097,10 @@ exports.Offer = Offer_exports;
2675
3097
  exports.Quote = Quote_exports;
2676
3098
  exports.RouterApi = Schema_exports;
2677
3099
  exports.RouterClient = Client_exports;
3100
+ exports.Rules = Rules_exports;
2678
3101
  exports.Time = time_exports;
3102
+ exports.Tree = Tree_exports;
2679
3103
  exports.Utils = utils_exports;
2680
- exports.Validation = Validation_exports;
2681
- exports.ValidationRule = ValidationRule_exports;
3104
+ exports.Validation = Gate_exports;
2682
3105
  //# sourceMappingURL=index.browser.js.map
2683
3106
  //# sourceMappingURL=index.browser.js.map