@morpho-dev/router 0.1.17 → 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 +34 -24
  2. package/dist/cli.js +3140 -2143
  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 +909 -159
  28. package/dist/index.browser.d.ts +909 -159
  29. package/dist/index.browser.js +1529 -1037
  30. package/dist/index.browser.js.map +1 -1
  31. package/dist/index.browser.mjs +1522 -1036
  32. package/dist/index.browser.mjs.map +1 -1
  33. package/dist/index.node.d.cts +2718 -912
  34. package/dist/index.node.d.ts +2718 -912
  35. package/dist/index.node.js +6827 -5521
  36. package/dist/index.node.js.map +1 -1
  37. package/dist/index.node.mjs +6816 -5515
  38. package/dist/index.node.mjs.map +1 -1
  39. package/package.json +21 -17
  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,62 +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
- ].map((address) => address.toLowerCase())
515
- ),
516
1151
  morpho: "0x0000000000000000000000000000000000000000",
517
1152
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
518
1153
  mempool: {
519
1154
  address: "0x0000000000000000000000000000000000000000",
520
1155
  deploymentBlock: 23347674,
521
1156
  reindexBuffer: 10
522
- },
523
- vaultV1Factory: {
524
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
525
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
526
1157
  }
527
1158
  },
528
1159
  base: {
529
1160
  ...chains$1.base,
530
1161
  id: ChainId.BASE,
531
1162
  name: "base",
532
- whitelistedAssets: new Set(
533
- [
534
- "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
535
- // USDC
536
- "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
537
- // DAI
538
- ].map((address) => address.toLowerCase())
539
- ),
540
1163
  morpho: "0x0000000000000000000000000000000000000000",
541
1164
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
542
1165
  mempool: {
543
1166
  address: "0x0000000000000000000000000000000000000000",
544
1167
  deploymentBlock: 35449942,
545
1168
  reindexBuffer: 10
546
- },
547
- vaultV1Factory: {
548
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
549
- "v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
550
1169
  }
551
1170
  },
552
1171
  "ethereum-virtual-testnet": {
553
1172
  ...chains$1.mainnet,
554
1173
  id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
555
1174
  name: "ethereum-virtual-testnet",
556
- whitelistedAssets: new Set(
557
- [
558
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
559
- // USDC
560
- "0x6B175474E89094C44Da98b954EedeAC495271d0F"
561
- // DAI
562
- ].map((address) => address.toLowerCase())
563
- ),
564
1175
  morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
565
1176
  // @TODO: This is mock Consumed contract, update with Terms once stable
566
1177
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -568,24 +1179,12 @@ var chains = {
568
1179
  address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
569
1180
  deploymentBlock: 23224302,
570
1181
  reindexBuffer: 10
571
- },
572
- vaultV1Factory: {
573
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
574
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
575
1182
  }
576
1183
  },
577
1184
  anvil: {
578
1185
  ...chains$1.anvil,
579
1186
  id: ChainId.ANVIL,
580
1187
  name: "anvil",
581
- whitelistedAssets: new Set(
582
- [
583
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
584
- // USDC
585
- "0x6B175474E89094C44Da98b954EedeAC495271d0F"
586
- // DAI
587
- ].map((address) => address.toLowerCase())
588
- ),
589
1188
  morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
590
1189
  morphoBlue: "0x0000000000000000000000000000000000000000",
591
1190
  // Set dynamically in tests
@@ -593,10 +1192,6 @@ var chains = {
593
1192
  address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
594
1193
  deploymentBlock: 23223727,
595
1194
  reindexBuffer: 10
596
- },
597
- vaultV1Factory: {
598
- "v1.0": "0x0000000000000000000000000000000000000000",
599
- "v1.1": "0x0000000000000000000000000000000000000000"
600
1195
  }
601
1196
  }
602
1197
  };
@@ -611,24 +1206,24 @@ async function* streamLogs(parameters) {
611
1206
  event,
612
1207
  blockNumberGte,
613
1208
  blockNumberLte,
614
- order = "desc",
1209
+ order: order2 = "desc",
615
1210
  options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
616
1211
  } = parameters;
617
1212
  if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
618
1213
  if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
619
- if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
1214
+ if (order2 === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
620
1215
  const latestBlock = (await actions.getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
621
1216
  let toBlock = 0n;
622
- if (order === "asc")
1217
+ if (order2 === "asc")
623
1218
  toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
624
- if (order === "desc")
1219
+ if (order2 === "desc")
625
1220
  toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
626
1221
  let fromBlock = 0n;
627
- if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
628
- if (order === "desc")
1222
+ if (order2 === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
1223
+ if (order2 === "desc")
629
1224
  fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
630
- if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
631
- 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));
632
1227
  if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
633
1228
  let streaming = true;
634
1229
  while (streaming) {
@@ -640,10 +1235,10 @@ async function* streamLogs(parameters) {
640
1235
  });
641
1236
  logs.sort((a, b) => {
642
1237
  if (a.blockNumber !== b.blockNumber)
643
- 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);
644
1239
  if (a.transactionIndex !== b.transactionIndex)
645
- return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
646
- 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;
647
1242
  });
648
1243
  for (const logBatch of batch(logs, maxBatchSize)) {
649
1244
  if (logBatch.length === 0) break;
@@ -654,16 +1249,16 @@ async function* streamLogs(parameters) {
654
1249
  Number(logBatch[logBatch.length - 1]?.blockNumber)
655
1250
  ) : (
656
1251
  // if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
657
- order === "asc" ? Number(toBlock) : Number(fromBlock)
1252
+ order2 === "asc" ? Number(toBlock) : Number(fromBlock)
658
1253
  )
659
1254
  };
660
1255
  }
661
- streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
662
- if (order === "asc") {
1256
+ streaming = order2 === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
1257
+ if (order2 === "asc") {
663
1258
  fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
664
1259
  toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
665
1260
  }
666
- if (order === "desc") {
1261
+ if (order2 === "desc") {
667
1262
  const lowerBound = BigInt(blockNumberGte || 0);
668
1263
  const windowSize = BigInt(blockWindow);
669
1264
  const nextToBlock = max(fromBlock - 1n, lowerBound);
@@ -672,7 +1267,7 @@ async function* streamLogs(parameters) {
672
1267
  fromBlock = nextFromBlock;
673
1268
  }
674
1269
  }
675
- yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
1270
+ yield { logs: [], blockNumber: order2 === "asc" ? Number(toBlock) : Number(fromBlock) };
676
1271
  return;
677
1272
  }
678
1273
  var InvalidBlockRangeError = class extends BaseError {
@@ -711,7 +1306,8 @@ var Collateral_exports = {};
711
1306
  __export(Collateral_exports, {
712
1307
  CollateralSchema: () => CollateralSchema,
713
1308
  CollateralsSchema: () => CollateralsSchema,
714
- from: () => from4
1309
+ from: () => from4,
1310
+ random: () => random
715
1311
  });
716
1312
  var transformHex = (val, ctx) => {
717
1313
  if (viem.isHex(val)) return val;
@@ -721,7 +1317,7 @@ var transformHex = (val, ctx) => {
721
1317
  format: "hex",
722
1318
  error: "not a hex"
723
1319
  });
724
- return z7__namespace.NEVER;
1320
+ return z9__namespace.NEVER;
725
1321
  };
726
1322
  var transformAddress = (val, ctx) => {
727
1323
  if (viem.isAddress(val.toLowerCase())) return val.toLowerCase();
@@ -731,7 +1327,7 @@ var transformAddress = (val, ctx) => {
731
1327
  format: "address",
732
1328
  error: "not a valid address"
733
1329
  });
734
- return z7__namespace.NEVER;
1330
+ return z9__namespace.NEVER;
735
1331
  };
736
1332
 
737
1333
  // src/core/LLTV.ts
@@ -771,7 +1367,7 @@ var InvalidLLTVError = class extends BaseError {
771
1367
  __publicField(this, "name", "LLTV.InvalidLLTVError");
772
1368
  }
773
1369
  };
774
- var LLTVSchema = z7__namespace.bigint({ coerce: true }).refine(
1370
+ var LLTVSchema = z9__namespace.bigint({ coerce: true }).refine(
775
1371
  (lltv) => {
776
1372
  try {
777
1373
  from3(lltv);
@@ -788,12 +1384,12 @@ var LLTVSchema = z7__namespace.bigint({ coerce: true }).refine(
788
1384
  ).transform((lltv) => from3(lltv));
789
1385
 
790
1386
  // src/core/Collateral.ts
791
- var CollateralSchema = z7__namespace.object({
792
- asset: z7__namespace.string().transform(transformAddress),
793
- 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),
794
1390
  lltv: LLTVSchema
795
1391
  });
796
- 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(
797
1393
  (collaterals) => {
798
1394
  for (let i = 1; i < collaterals.length; i++) {
799
1395
  if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
@@ -828,6 +1424,13 @@ var from4 = (parameters) => {
828
1424
  oracle: parameters.oracle.toLowerCase()
829
1425
  };
830
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
+ }
831
1434
 
832
1435
  // src/core/Liquidity.ts
833
1436
  var Liquidity_exports = {};
@@ -851,20 +1454,20 @@ function calculateMaxDebt(amount, oraclePrice, lltv) {
851
1454
  return maxDebt;
852
1455
  }
853
1456
  function generateBalancePoolId(parameters) {
854
- const { user, chainId, token } = parameters;
855
- return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
1457
+ const { user, chainId, token: token2 } = parameters;
1458
+ return `${user}-${chainId.toString()}-${token2}-balance`.toLowerCase();
856
1459
  }
857
1460
  function generateAllowancePoolId(parameters) {
858
- const { user, chainId, token } = parameters;
859
- return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
1461
+ const { user, chainId, token: token2 } = parameters;
1462
+ return `${user}-${chainId.toString()}-${token2}-allowance`.toLowerCase();
860
1463
  }
861
1464
  function generateSellERC20CallbackPoolId(parameters) {
862
- const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
863
- 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();
864
1467
  }
865
1468
  function generateObligationCollateralPoolId(parameters) {
866
- const { user, chainId, obligationId: obligationId2, token } = parameters;
867
- 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();
868
1471
  }
869
1472
  function generateBuyVaultCallbackPoolId(parameters) {
870
1473
  const { user, chainId, vault, offerHash } = parameters;
@@ -894,12 +1497,13 @@ __export(Maturity_exports, {
894
1497
  InvalidFormatError: () => InvalidFormatError,
895
1498
  InvalidOptionError: () => InvalidOptionError2,
896
1499
  MaturitySchema: () => MaturitySchema,
1500
+ MaturityType: () => MaturityType,
897
1501
  from: () => from5
898
1502
  });
899
- var MaturitySchema = z7__namespace.number().int().refine(
900
- (maturity) => {
1503
+ var MaturitySchema = z9__namespace.number().int().refine(
1504
+ (maturity2) => {
901
1505
  try {
902
- from5(maturity);
1506
+ from5(maturity2);
903
1507
  return true;
904
1508
  } catch (_e) {
905
1509
  return false;
@@ -915,7 +1519,16 @@ var MaturitySchema = z7__namespace.number().int().refine(
915
1519
  }
916
1520
  }
917
1521
  }
918
- ).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 || {});
919
1532
  var MaturityOptions = {
920
1533
  end_of_week: () => endOfWeek(),
921
1534
  end_of_next_week: () => endOfNextWeek(),
@@ -936,8 +1549,26 @@ function from5(ts) {
936
1549
  }
937
1550
  var endOfWeek = () => fridayOfWeek(0);
938
1551
  var endOfNextWeek = () => fridayOfWeek(1);
939
- var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
940
- 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
+ };
941
1572
  var endOfQuarter = () => lastFridayOfQuarter(0);
942
1573
  var endOfNextQuarter = () => lastFridayOfQuarter(1);
943
1574
  var fridayOfWeek = (weeksAhead = 0) => {
@@ -958,8 +1589,8 @@ var lastFridayOfMonth = (year, month) => {
958
1589
  while (lastDayOfMonth15H.getUTCDay() !== 5) {
959
1590
  lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
960
1591
  }
961
- const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
962
- return maturity;
1592
+ const maturity2 = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
1593
+ return maturity2;
963
1594
  };
964
1595
  var lastFridayOfQuarter = (quartersAhead = 0) => {
965
1596
  const now2 = /* @__PURE__ */ new Date();
@@ -1005,11 +1636,11 @@ __export(Obligation_exports, {
1005
1636
  from: () => from6,
1006
1637
  fromSnakeCase: () => fromSnakeCase2,
1007
1638
  id: () => id,
1008
- random: () => random
1639
+ random: () => random2
1009
1640
  });
1010
- var ObligationSchema = z7__namespace.object({
1011
- chainId: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1012
- 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),
1013
1644
  collaterals: CollateralsSchema,
1014
1645
  maturity: MaturitySchema
1015
1646
  });
@@ -1067,7 +1698,7 @@ function id(obligation) {
1067
1698
  )
1068
1699
  );
1069
1700
  }
1070
- function random() {
1701
+ function random2() {
1071
1702
  return from6({
1072
1703
  chainId: 1n,
1073
1704
  loanToken: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
@@ -1101,170 +1732,138 @@ __export(Offer_exports, {
1101
1732
  InvalidOfferError: () => InvalidOfferError,
1102
1733
  OfferHashSchema: () => OfferHashSchema,
1103
1734
  OfferSchema: () => OfferSchema,
1735
+ StatusCode: () => StatusCode,
1104
1736
  consumedEvent: () => consumedEvent,
1105
- decode: () => decode,
1737
+ decode: () => decode4,
1106
1738
  domain: () => domain,
1107
- encode: () => encode,
1108
- from: () => from7,
1739
+ encode: () => encode4,
1740
+ from: () => from8,
1109
1741
  fromConsumedLog: () => fromConsumedLog,
1110
1742
  fromSnakeCase: () => fromSnakeCase3,
1111
1743
  hash: () => hash,
1112
1744
  obligationId: () => obligationId,
1113
- random: () => random2,
1745
+ random: () => random3,
1114
1746
  sign: () => sign,
1747
+ signatureMsg: () => signatureMsg,
1115
1748
  toSnakeCase: () => toSnakeCase2,
1116
1749
  types: () => types
1117
1750
  });
1118
1751
 
1119
- // src/utils/index.ts
1120
- var utils_exports = {};
1121
- __export(utils_exports, {
1122
- BaseError: () => BaseError,
1123
- Time: () => time_exports,
1124
- batch: () => batch,
1125
- batchMulticall: () => batchMulticall,
1126
- fromSnakeCase: () => fromSnakeCase,
1127
- lazy: () => lazy,
1128
- max: () => max,
1129
- min: () => min,
1130
- poll: () => poll,
1131
- retry: () => retry,
1132
- toSnakeCase: () => toSnakeCase,
1133
- 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
1134
1759
  });
1135
-
1136
- // src/utils/retry.ts
1137
- var retry = async (fn, attempts = 3, delayMs = 50) => {
1138
- let lastErr;
1139
- for (let i = 0; i < attempts; i++) {
1140
- try {
1141
- return await fn();
1142
- } catch (err) {
1143
- lastErr = err;
1144
- if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
1145
- }
1146
- }
1147
- 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 });
1148
1767
  };
1149
-
1150
- // src/utils/batchMulticall.ts
1151
- async function batchMulticall(parameters) {
1152
- const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
1153
- const results = [];
1154
- for (const callsBatch of batch(calls, batchSize)) {
1155
- const batchResults = await retry(
1156
- () => client.multicall({
1157
- allowFailure: false,
1158
- contracts: callsBatch,
1159
- ...blockNumber ? { blockNumber } : {}
1160
- }),
1161
- retryAttempts,
1162
- retryDelayMs
1163
- );
1164
- 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}`);
1165
1808
  }
1166
- return results;
1167
- }
1168
-
1169
- // src/utils/lazy.ts
1170
- function lazy(pollFn) {
1171
- return () => async function* () {
1172
- let active = true;
1173
- let resolveNext = null;
1174
- const queue = [];
1175
- const wait2 = () => new Promise((resolve) => {
1176
- resolveNext = resolve;
1177
- });
1178
- const emit = (item) => {
1179
- queue.push(item);
1180
- resolveNext?.();
1181
- resolveNext = null;
1182
- };
1183
- let unpoll = null;
1184
- const stop = () => {
1185
- active = false;
1186
- unpoll?.();
1187
- resolveNext?.();
1188
- resolveNext = null;
1189
- };
1190
- unpoll = pollFn(emit, { stop });
1191
- try {
1192
- while (active) {
1193
- if (queue.length === 0) await wait2();
1194
- while (queue.length > 0 && active) yield queue.shift();
1195
- }
1196
- } finally {
1197
- stop();
1198
- }
1199
- }();
1200
- }
1201
-
1202
- // src/utils/wait.ts
1203
- async function wait(time) {
1204
- return new Promise((res) => setTimeout(res, time));
1205
- }
1206
-
1207
- // src/utils/poll.ts
1208
- function poll(fn, { interval }) {
1209
- let active = true;
1210
- const unwatch = () => active = false;
1211
- const watch2 = async () => {
1212
- await wait(interval);
1213
- const poll2 = async () => {
1214
- if (!active) return;
1215
- await fn({ unpoll: unwatch });
1216
- await wait(interval);
1217
- poll2();
1218
- };
1219
- poll2();
1220
- };
1221
- watch2();
1222
- return unwatch;
1223
- }
1224
-
1225
- // src/utils/time.ts
1226
- var time_exports = {};
1227
- __export(time_exports, {
1228
- max: () => max2,
1229
- now: () => now
1230
- });
1231
- function now() {
1232
- return Math.floor(Date.now() / 1e3);
1233
- }
1234
- function max2() {
1235
- return 864e16;
1236
- }
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
+ };
1237
1830
 
1238
1831
  // src/core/Offer.ts
1239
- 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}$/, {
1240
1838
  message: "Hash must be a valid 32-byte hex string"
1241
1839
  }).transform(transformHex);
1242
1840
  var OfferSchema = (parameters) => {
1243
- const { omitHash = false } = parameters || {};
1244
- const now2 = time_exports.now();
1245
- let base = z7__namespace.object({
1246
- offering: z7__namespace.string().transform(transformAddress),
1247
- assets: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1248
- 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),
1249
1846
  maturity: MaturitySchema,
1250
- expiry: z7__namespace.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
1251
- start: z7__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1252
- nonce: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1253
- buy: z7__namespace.boolean(),
1254
- chainId: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1255
- 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),
1256
1853
  collaterals: CollateralsSchema,
1257
- callback: z7__namespace.object({
1258
- address: z7__namespace.string().transform(transformAddress),
1259
- data: z7__namespace.string().transform(transformHex),
1260
- 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)
1261
1858
  }),
1262
- consumed: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1263
- blockNumber: z7__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1264
- signature: z7__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1859
+ signature: z9__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1265
1860
  message: "Signature must be a valid 65-byte hex string"
1266
1861
  }).transform(transformHex).optional()
1267
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) });
1268
1867
  if (!omitHash) base = base.extend({ hash: OfferHashSchema });
1269
1868
  return base.refine((data) => data.start < data.expiry, {
1270
1869
  message: "Start must be before expiry",
@@ -1274,7 +1873,7 @@ var OfferSchema = (parameters) => {
1274
1873
  path: ["expiry"]
1275
1874
  });
1276
1875
  };
1277
- function from7(input) {
1876
+ function from8(input) {
1278
1877
  try {
1279
1878
  const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1280
1879
  const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
@@ -1287,44 +1886,94 @@ function from7(input) {
1287
1886
  }
1288
1887
  }
1289
1888
  function fromSnakeCase3(input) {
1290
- return from7(fromSnakeCase(input));
1889
+ return from8(fromSnakeCase(input));
1291
1890
  }
1292
1891
  function toSnakeCase2(offer) {
1293
1892
  return toSnakeCase(offer);
1294
1893
  }
1295
- function random2() {
1296
- const loanToken = accounts.privateKeyToAccount(accounts.generatePrivateKey()).address;
1297
- const maturity = from5("end_of_month");
1298
- const expiry = from5("end_of_week") - 1;
1299
- const lltv = from3(0.965);
1300
- const offer = from7({
1301
- offering: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1302
- assets: BigInt(Math.floor(Math.random() * 1e6)),
1303
- rate: BigInt(Math.floor(Math.random() * 1e6)),
1304
- maturity,
1305
- expiry,
1306
- start: expiry - 10,
1894
+ function random3(config) {
1895
+ const chain2 = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
1896
+ const loanToken = config?.loanTokens ? config.loanTokens[Math.floor(Math.random() * config.loanTokens.length)] : accounts.privateKeyToAccount(accounts.generatePrivateKey()).address;
1897
+ const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [accounts.privateKeyToAccount(accounts.generatePrivateKey()).address];
1898
+ const collateralAsset = collateralCandidates[Math.floor(Math.random() * collateralCandidates.length)];
1899
+ const maturityOption = weightedChoice([
1900
+ ["end_of_month", 1],
1901
+ ["end_of_next_month", 1]
1902
+ ]);
1903
+ const maturity2 = config?.maturity ?? from5(maturityOption);
1904
+ const lltv = from3(
1905
+ weightedChoice([
1906
+ [0.385, 1],
1907
+ [0.5, 1],
1908
+ [0.625, 2],
1909
+ [0.77, 8],
1910
+ [0.86, 10],
1911
+ [0.915, 8],
1912
+ [0.945, 6],
1913
+ [0.965, 4],
1914
+ [0.98, 2]
1915
+ ])
1916
+ );
1917
+ const buy = config?.buy !== void 0 ? config.buy : Math.random() > 0.5;
1918
+ const ONE = 1000000000000000000n;
1919
+ const qMin = buy ? 16 : 4;
1920
+ const qMax = buy ? 32 : 16;
1921
+ const len = qMax - qMin + 1;
1922
+ const ratePairs = Array.from(
1923
+ { length: len },
1924
+ (_, idx) => {
1925
+ const q = qMin + idx;
1926
+ const scaledRate = BigInt(q) * (ONE / 4n);
1927
+ const weight = buy ? 1 + idx : 1 + (len - 1 - idx);
1928
+ return [scaledRate, weight];
1929
+ }
1930
+ );
1931
+ const rate = config?.rate ?? weightedChoice(ratePairs);
1932
+ const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
1933
+ const unit = BigInt(10) ** BigInt(loanTokenDecimals);
1934
+ const amountBase = BigInt(100 + Math.floor(Math.random() * (1e6 - 100 + 1)));
1935
+ const assetsScaled = config?.assets ?? amountBase * unit;
1936
+ const consumed = config?.consumed !== void 0 ? config.consumed : Math.random() < 0.8 ? 0n : assetsScaled * BigInt(1 + Math.floor(Math.random() * 900)) / 1000n;
1937
+ const callbackBySide = (() => {
1938
+ if (buy) return { address: viem.zeroAddress, data: "0x", gasLimit: 0n };
1939
+ const sellCallbackAddress = "0x3333333333333333333333333333333333333333";
1940
+ const amount = assetsScaled * 1000000000000000000000n;
1941
+ const data = encodeSellERC20Callback({
1942
+ collaterals: [collateralAsset],
1943
+ amounts: [amount]
1944
+ });
1945
+ return { address: sellCallbackAddress, data, gasLimit: 0n };
1946
+ })();
1947
+ const offer = from8({
1948
+ offering: config?.offering ?? accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1949
+ assets: assetsScaled,
1950
+ rate,
1951
+ maturity: maturity2,
1952
+ expiry: config?.expiry ?? maturity2 - 1,
1953
+ start: config?.start ?? maturity2 - 10,
1307
1954
  nonce: BigInt(Math.floor(Math.random() * 1e6)),
1308
- buy: Math.random() > 0.5,
1309
- chainId: 1n,
1955
+ buy,
1956
+ chainId: chain2.id,
1310
1957
  loanToken,
1311
- collaterals: [
1312
- from4({
1313
- asset: viem.zeroAddress,
1314
- oracle: viem.zeroAddress,
1315
- lltv
1316
- })
1317
- ],
1318
- callback: {
1319
- address: viem.zeroAddress,
1320
- data: "0x",
1321
- gasLimit: 0n
1322
- },
1323
- consumed: 0n,
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)),
1962
+ callback: config?.callback ?? callbackBySide,
1963
+ consumed,
1324
1964
  blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
1325
1965
  });
1326
1966
  return offer;
1327
1967
  }
1968
+ var weightedChoice = (pairs) => {
1969
+ const total = pairs.reduce((sum, [, weight]) => sum + weight, 0);
1970
+ let roll = Math.random() * total;
1971
+ for (const [value, weight] of pairs) {
1972
+ roll -= weight;
1973
+ if (roll < 0) return value;
1974
+ }
1975
+ return pairs[0][0];
1976
+ };
1328
1977
  var domain = (chainId) => ({
1329
1978
  chainId,
1330
1979
  verifyingContract: viem.zeroAddress
@@ -1357,31 +2006,16 @@ var types = {
1357
2006
  { name: "gasLimit", type: "uint256" }
1358
2007
  ]
1359
2008
  };
1360
- function sign(offer, wallet) {
2009
+ async function sign(offers, wallet) {
1361
2010
  if (!wallet.account) throw new AccountNotSetError();
1362
- return wallet.signTypedData({
2011
+ return wallet.signMessage({
1363
2012
  account: wallet.account,
1364
- domain: domain(offer.chainId),
1365
- types,
1366
- primaryType: "Offer",
1367
- message: {
1368
- offering: offer.offering.toLowerCase(),
1369
- assets: offer.assets,
1370
- rate: offer.rate,
1371
- maturity: BigInt(offer.maturity),
1372
- expiry: BigInt(offer.expiry),
1373
- nonce: offer.nonce,
1374
- buy: offer.buy,
1375
- loanToken: offer.loanToken.toLowerCase(),
1376
- collaterals: offer.collaterals,
1377
- callback: {
1378
- address: offer.callback.address.toLowerCase(),
1379
- data: offer.callback.data,
1380
- gasLimit: offer.callback.gasLimit
1381
- }
1382
- }
2013
+ message: { raw: signatureMsg(offers) }
1383
2014
  });
1384
2015
  }
2016
+ function signatureMsg(offers) {
2017
+ return from7(offers).root;
2018
+ }
1385
2019
  function hash(offer) {
1386
2020
  return viem.hashTypedData({
1387
2021
  domain: domain(offer.chainId),
@@ -1446,7 +2080,7 @@ var OfferAbi = [
1446
2080
  },
1447
2081
  { name: "signature", type: "bytes" }
1448
2082
  ];
1449
- function encode(offer) {
2083
+ function encode4(offer) {
1450
2084
  return viem.encodeAbiParameters(OfferAbi, [
1451
2085
  offer.offering,
1452
2086
  offer.assets,
@@ -1463,14 +2097,14 @@ function encode(offer) {
1463
2097
  offer.signature ?? "0x"
1464
2098
  ]);
1465
2099
  }
1466
- function decode(data, blockNumber) {
2100
+ function decode4(data, blockNumber) {
1467
2101
  let decoded;
1468
2102
  try {
1469
2103
  decoded = viem.decodeAbiParameters(OfferAbi, data);
1470
2104
  } catch (error) {
1471
2105
  throw new InvalidOfferError(error);
1472
2106
  }
1473
- const offer = from7({
2107
+ const offer = from8({
1474
2108
  offering: decoded[0],
1475
2109
  assets: decoded[1],
1476
2110
  rate: decoded[2],
@@ -1538,20 +2172,20 @@ var Quote_exports = {};
1538
2172
  __export(Quote_exports, {
1539
2173
  InvalidQuoteError: () => InvalidQuoteError,
1540
2174
  QuoteSchema: () => QuoteSchema,
1541
- from: () => from8,
2175
+ from: () => from9,
1542
2176
  fromSnakeCase: () => fromSnakeCase4,
1543
- random: () => random3
2177
+ random: () => random4
1544
2178
  });
1545
- var QuoteSchema = z7__namespace.object({
1546
- obligationId: z7__namespace.string().transform(transformHex),
1547
- ask: z7__namespace.object({
1548
- 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)
1549
2183
  }),
1550
- bid: z7__namespace.object({
1551
- 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)
1552
2186
  })
1553
2187
  });
1554
- function from8(parameters) {
2188
+ function from9(parameters) {
1555
2189
  try {
1556
2190
  const parsedQuote = QuoteSchema.parse(parameters);
1557
2191
  return {
@@ -1564,10 +2198,10 @@ function from8(parameters) {
1564
2198
  }
1565
2199
  }
1566
2200
  function fromSnakeCase4(snake) {
1567
- return from8(fromSnakeCase(snake));
2201
+ return from9(fromSnakeCase(snake));
1568
2202
  }
1569
- function random3() {
1570
- return from8({
2203
+ function random4() {
2204
+ return from9({
1571
2205
  obligationId: Obligation_exports.id(Obligation_exports.random()),
1572
2206
  ask: {
1573
2207
  rate: BigInt(Math.floor(Math.random() * 1e6))
@@ -1587,520 +2221,130 @@ var InvalidQuoteError = class extends BaseError {
1587
2221
  // src/core/types.ts
1588
2222
  var BrandTypeId = Symbol.for("mempool/Brand");
1589
2223
 
1590
- // src/stores/utils/Cursor.ts
1591
- var Cursor_exports = {};
1592
- __export(Cursor_exports, {
1593
- decode: () => decode2,
1594
- encode: () => encode2,
1595
- validate: () => validate
1596
- });
1597
- function validate(cursor) {
1598
- if (!cursor || typeof cursor !== "object") {
1599
- throw new Error("Cursor must be an object");
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)
2240
+ };
2241
+ }
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
2250
+ }
2251
+ }
2252
+ });
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();
2261
+ }
2262
+ throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2263
+ details: JSON.stringify(error)
2264
+ });
1600
2265
  }
1601
- const c = cursor;
1602
- if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
1603
- throw new Error(
1604
- `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
1605
- );
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
2282
+ },
2283
+ ...signature !== null ? { signature: item.signature } : void 0
2284
+ });
2285
+ }) ?? [];
2286
+ return {
2287
+ cursor: data?.cursor ?? null,
2288
+ offers
2289
+ };
2290
+ }
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
+ }
2299
+ });
2300
+ if (error !== void 0) {
2301
+ switch (response.status) {
2302
+ case 401:
2303
+ throw new HttpUnauthorizedError();
2304
+ case 403:
2305
+ throw new HttpForbiddenError();
2306
+ case 429:
2307
+ throw new HttpRateLimitError();
2308
+ }
2309
+ throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2310
+ details: JSON.stringify(error)
2311
+ });
1606
2312
  }
1607
- if (!["asc", "desc"].includes(c.dir)) {
1608
- throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
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
+ };
2335
+ }
2336
+ var InvalidUrlError = class extends BaseError {
2337
+ constructor(url) {
2338
+ super(`URL "${url}" is not http/https.`);
2339
+ __publicField(this, "name", "Router.InvalidUrlError");
1609
2340
  }
1610
- if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
1611
- throw new Error(
1612
- `Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
1613
- );
1614
- }
1615
- const validations = {
1616
- rate: {
1617
- field: "rate",
1618
- type: "string",
1619
- pattern: /^\d+$/,
1620
- error: "numeric string"
1621
- },
1622
- amount: {
1623
- field: "assets",
1624
- type: "string",
1625
- pattern: /^\d+$/,
1626
- error: "numeric string"
1627
- },
1628
- maturity: {
1629
- field: "maturity",
1630
- type: "number",
1631
- validator: (val) => val > 0,
1632
- error: "positive number"
1633
- },
1634
- expiry: {
1635
- field: "expiry",
1636
- type: "number",
1637
- validator: (val) => val > 0,
1638
- error: "positive number"
1639
- }
1640
- };
1641
- const validation = validations[c.sort];
1642
- if (!validation) {
1643
- throw new Error(`Invalid sort field: ${c.sort}`);
1644
- }
1645
- const fieldValue = c[validation.field];
1646
- if (!fieldValue) {
1647
- throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
1648
- }
1649
- if (typeof fieldValue !== validation.type) {
1650
- throw new Error(
1651
- `${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
1652
- );
1653
- }
1654
- if (validation.pattern && !validation.pattern.test(fieldValue)) {
1655
- throw new Error(
1656
- `Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
1657
- );
1658
- }
1659
- if (validation.validator && !validation.validator(fieldValue)) {
1660
- throw new Error(
1661
- `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
1662
- );
1663
- }
1664
- if (c.page !== void 0) {
1665
- if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
1666
- throw new Error("Invalid page: must be a positive integer");
1667
- }
1668
- }
1669
- return true;
1670
- }
1671
- function encode2(c) {
1672
- return jsBase64.Base64.encodeURL(JSON.stringify(c));
1673
- }
1674
- function decode2(token) {
1675
- if (!token) return null;
1676
- const decoded = JSON.parse(jsBase64.Base64.decode(token));
1677
- validate(decoded);
1678
- return decoded;
1679
- }
1680
-
1681
- // src/api/Schema/requests.ts
1682
- var MAX_LIMIT = 100;
1683
- var DEFAULT_LIMIT = 20;
1684
- var PaginationQueryParams = z7__namespace.object({
1685
- cursor: z7__namespace.string().optional().refine(
1686
- (val) => {
1687
- if (!val) return true;
1688
- try {
1689
- const decoded = Cursor_exports.decode(val);
1690
- return decoded !== null;
1691
- } catch (_error) {
1692
- return false;
1693
- }
1694
- },
1695
- {
1696
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1697
- }
1698
- ).meta({
1699
- description: "Pagination cursor in base64url-encoded format",
1700
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
1701
- }),
1702
- limit: z7__namespace.string().regex(/^[1-9]\d*$/, {
1703
- message: "Limit must be a positive integer"
1704
- }).transform((val) => Number.parseInt(val, 10)).pipe(
1705
- z7__namespace.number().max(MAX_LIMIT, {
1706
- message: `Limit cannot exceed ${MAX_LIMIT}`
1707
- })
1708
- ).optional().default(DEFAULT_LIMIT).meta({
1709
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
1710
- example: 10
1711
- })
1712
- });
1713
- var GetOffersQueryParams = z7__namespace.object({
1714
- ...PaginationQueryParams.shape,
1715
- side: z7__namespace.enum(["buy", "sell"]).meta({
1716
- description: "Side of the offer.",
1717
- example: "buy"
1718
- }),
1719
- 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({
1720
- description: "Offers obligation id",
1721
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1722
- })
1723
- });
1724
- var GetObligationsQueryParams = z7__namespace.object({
1725
- ...PaginationQueryParams.shape,
1726
- cursor: z7__namespace.string().optional().meta({
1727
- description: "Obligation id cursor",
1728
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1729
- })
1730
- });
1731
- var schemas = {
1732
- get_offers: GetOffersQueryParams,
1733
- get_obligations: GetObligationsQueryParams
1734
- };
1735
- function parse(action, query) {
1736
- return schemas[action].parse(query);
1737
- }
1738
- function safeParse(action, query, error) {
1739
- return schemas[action].safeParse(query, {
1740
- error
1741
- });
1742
- }
1743
-
1744
- // src/api/Schema/openapi.ts
1745
- var timestampExample = "2024-01-01T12:00:00.000Z";
1746
- var cursorExample = "eyJvZmZzZXQiOjEwMH0";
1747
- function makeSuccessResponse(parameters) {
1748
- const { dataSchema, dataDescription, dataExample, cursor } = parameters;
1749
- const withDataMeta = dataDescription ? dataSchema.meta({ description: dataDescription }) : dataSchema;
1750
- return v4.z.object({
1751
- status: v4.z.literal("success"),
1752
- cursor: v4.z.string().nullable(),
1753
- data: v4.z.any(),
1754
- meta: v4.z.object({
1755
- timestamp: v4.z.string()
1756
- })
1757
- }).extend({
1758
- data: withDataMeta
1759
- }).meta({
1760
- example: {
1761
- status: "success",
1762
- cursor,
1763
- data: dataExample,
1764
- meta: { timestamp: timestampExample }
1765
- }
1766
- });
1767
- }
1768
- var OffersSuccessResponseSchema = makeSuccessResponse({
1769
- dataSchema: v4.z.array(v4.z.any()),
1770
- dataDescription: "Offers matching the provided filters.",
1771
- dataExample: [toSnakeCase(Offer_exports.random())],
1772
- cursor: cursorExample
1773
- });
1774
- var ObligationsSuccessResponseSchema = makeSuccessResponse({
1775
- dataSchema: v4.z.array(v4.z.any()),
1776
- dataDescription: "Obligations known to the router.",
1777
- dataExample: [toSnakeCase(Obligation_exports.random())],
1778
- cursor: cursorExample
1779
- });
1780
- var RouterStatusSuccessResponseSchema = makeSuccessResponse({
1781
- dataSchema: RouterStatusResponse,
1782
- dataDescription: "Aggregated router status.",
1783
- dataExample: { status: "live" },
1784
- cursor: null
1785
- });
1786
- var CollectorsHealthSuccessResponseSchema = makeSuccessResponse({
1787
- dataSchema: CollectorsHealthResponse,
1788
- dataDescription: "Collectors health details and sync status.",
1789
- dataExample: [
1790
- {
1791
- name: "mempool_offers",
1792
- chain_id: "1",
1793
- block_number: 21345678,
1794
- updated_at: "2024-01-01T12:00:00.000Z",
1795
- lag: 0,
1796
- status: "live"
1797
- }
1798
- ],
1799
- cursor: null
1800
- });
1801
- var ChainsHealthSuccessResponseSchema = makeSuccessResponse({
1802
- dataSchema: ChainsHealthResponse,
1803
- dataDescription: "Latest processed block per chain.",
1804
- dataExample: [
1805
- {
1806
- chain_id: "1",
1807
- block_number: 21345678,
1808
- updated_at: "2024-01-01T12:00:00.000Z"
1809
- }
1810
- ],
1811
- cursor: null
1812
- });
1813
- var errorResponseSchema = v4.z.object({
1814
- status: v4.z.literal("error"),
1815
- error: v4.z.object({
1816
- code: v4.z.string(),
1817
- message: v4.z.string(),
1818
- details: v4.z.any().optional()
1819
- }),
1820
- meta: v4.z.object({
1821
- timestamp: v4.z.string()
1822
- })
1823
- }).meta({
1824
- description: "Error response wrapper.",
1825
- example: {
1826
- status: "error",
1827
- error: {
1828
- code: "VALIDATION_ERROR",
1829
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object",
1830
- details: [
1831
- {
1832
- field: "cursor",
1833
- issue: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1834
- }
1835
- ]
1836
- },
1837
- meta: {
1838
- timestamp: timestampExample
1839
- }
1840
- }
1841
- });
1842
- var paths = {
1843
- "/v1/offers": {
1844
- get: {
1845
- summary: "Offers",
1846
- description: "Find offers that match specific criteria",
1847
- tags: ["Offers"],
1848
- requestParams: {
1849
- query: GetOffersQueryParams
1850
- },
1851
- responses: {
1852
- 200: {
1853
- description: "Success",
1854
- content: {
1855
- "application/json": {
1856
- schema: OffersSuccessResponseSchema
1857
- }
1858
- }
1859
- },
1860
- 400: {
1861
- description: "Bad Request",
1862
- content: {
1863
- "application/json": {
1864
- schema: errorResponseSchema
1865
- }
1866
- }
1867
- }
1868
- }
1869
- }
1870
- },
1871
- "/v1/obligations": {
1872
- get: {
1873
- summary: "Obligations",
1874
- description: "List obligations with pagination support",
1875
- tags: ["Obligations"],
1876
- requestParams: {
1877
- query: GetObligationsQueryParams
1878
- },
1879
- responses: {
1880
- 200: {
1881
- description: "Success",
1882
- content: {
1883
- "application/json": {
1884
- schema: ObligationsSuccessResponseSchema
1885
- }
1886
- }
1887
- },
1888
- 400: {
1889
- description: "Bad Request",
1890
- content: {
1891
- "application/json": {
1892
- schema: errorResponseSchema
1893
- }
1894
- }
1895
- }
1896
- }
1897
- }
1898
- },
1899
- "/v1/health": {
1900
- get: {
1901
- summary: "Router status",
1902
- description: "Retrieve the aggregated status of the router.",
1903
- tags: ["Health"],
1904
- responses: {
1905
- 200: {
1906
- description: "Success",
1907
- content: {
1908
- "application/json": {
1909
- schema: RouterStatusSuccessResponseSchema
1910
- }
1911
- }
1912
- }
1913
- }
1914
- }
1915
- },
1916
- "/v1/health/collectors": {
1917
- get: {
1918
- summary: "Collectors health",
1919
- description: "Retrieve the block numbers processed by collectors and their sync status.",
1920
- tags: ["Health"],
1921
- responses: {
1922
- 200: {
1923
- description: "Success",
1924
- content: {
1925
- "application/json": {
1926
- schema: CollectorsHealthSuccessResponseSchema
1927
- }
1928
- }
1929
- }
1930
- }
1931
- }
1932
- },
1933
- "/v1/health/chains": {
1934
- get: {
1935
- summary: "Chains health",
1936
- description: "Retrieve the latest block processed for each chain.",
1937
- tags: ["Health"],
1938
- responses: {
1939
- 200: {
1940
- description: "Success",
1941
- content: {
1942
- "application/json": {
1943
- schema: ChainsHealthSuccessResponseSchema
1944
- }
1945
- }
1946
- }
1947
- }
1948
- }
1949
- }
1950
- };
1951
- var OpenApi = zodOpenapi.createDocument({
1952
- openapi: "3.1.0",
1953
- info: {
1954
- title: "Router API",
1955
- version: "1.0.0",
1956
- description: "API for the Morpho Router"
1957
- },
1958
- tags: [
1959
- {
1960
- name: "Offers"
1961
- },
1962
- {
1963
- name: "Obligations"
1964
- },
1965
- {
1966
- name: "Health"
1967
- }
1968
- ],
1969
- servers: [
1970
- {
1971
- url: "https://router.morpho.dev",
1972
- description: "Production server"
1973
- },
1974
- {
1975
- url: "http://localhost:7891",
1976
- description: "Local development server"
1977
- }
1978
- ],
1979
- paths
1980
- });
1981
-
1982
- // src/client/Client.ts
1983
- var Client_exports = {};
1984
- __export(Client_exports, {
1985
- HttpForbiddenError: () => HttpForbiddenError,
1986
- HttpGetApiFailedError: () => HttpGetApiFailedError,
1987
- HttpRateLimitError: () => HttpRateLimitError,
1988
- HttpUnauthorizedError: () => HttpUnauthorizedError,
1989
- InvalidUrlError: () => InvalidUrlError,
1990
- connect: () => connect,
1991
- getObligations: () => getObligations,
1992
- getOffers: () => getOffers
1993
- });
1994
- function connect(options) {
1995
- const u = new URL(options?.url || "https://router.morpho.dev");
1996
- if (u.protocol !== "http:" && u.protocol !== "https:") {
1997
- throw new InvalidUrlError(u.toString());
1998
- }
1999
- const headers = options?.headers ?? new Headers();
2000
- headers.set("Content-Type", "application/json");
2001
- options?.apiKey !== void 0 ? headers.set("X-API-Key", options.apiKey) : null;
2002
- const config = {
2003
- url: u,
2004
- headers
2005
- };
2006
- return {
2007
- ...config,
2008
- getOffers: (parameters) => getOffers(config, parameters),
2009
- getObligations: (parameters) => getObligations(config, parameters)
2010
- };
2011
- }
2012
- async function getOffers(config, parameters) {
2013
- const url = new URL(`${config.url.toString()}v1/offers`);
2014
- url.searchParams.set("side", parameters.side);
2015
- url.searchParams.set("obligation_id", parameters.obligationId.toString());
2016
- if (parameters.cursor) {
2017
- url.searchParams.set("cursor", parameters.cursor);
2018
- }
2019
- if (parameters.limit !== void 0) {
2020
- url.searchParams.set("limit", parameters.limit.toString());
2021
- }
2022
- const { cursor: returnedCursor, data: offers } = await getApi(config, url);
2023
- const routerOffers = offers.map(Offer_exports.fromSnakeCase);
2024
- return {
2025
- cursor: returnedCursor,
2026
- offers: routerOffers
2027
- };
2028
- }
2029
- async function getObligations(config, parameters) {
2030
- const url = new URL(`${config.url.toString()}v1/obligations`);
2031
- if (parameters?.cursor !== void 0) {
2032
- url.searchParams.set("cursor", parameters.cursor);
2033
- }
2034
- if (parameters?.limit !== void 0) {
2035
- url.searchParams.set("limit", parameters.limit.toString());
2036
- }
2037
- const { cursor: returnedCursor, data: items } = await getApi(config, url);
2038
- const obligations = items.map((item) => {
2039
- const obligation = Obligation_exports.fromSnakeCase(item);
2040
- const { obligationId: _, ...returned } = {
2041
- id: () => Obligation_exports.id(obligation),
2042
- ...obligation,
2043
- ...Quote_exports.fromSnakeCase({ obligation_id: item.id, ask: item.ask, bid: item.bid })
2044
- };
2045
- return returned;
2046
- });
2047
- return {
2048
- cursor: returnedCursor,
2049
- obligations
2050
- };
2051
- }
2052
- async function getApi(config, url) {
2053
- const pathname = url.pathname;
2054
- let action;
2055
- switch (true) {
2056
- case pathname.includes("/v1/offers"):
2057
- action = "get_offers";
2058
- break;
2059
- case pathname.includes("/v1/obligations"):
2060
- action = "get_obligations";
2061
- break;
2062
- default:
2063
- throw new HttpGetApiFailedError("Unknown endpoint", {
2064
- details: `Unsupported path: ${pathname}`
2065
- });
2066
- }
2067
- const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
2068
- if (!schemaParseResult.success) {
2069
- throw new HttpGetApiFailedError(`Invalid URL parameters`, {
2070
- details: schemaParseResult.error.issues[0]?.message
2071
- });
2072
- }
2073
- const response = await fetch(url.toString(), {
2074
- method: "GET",
2075
- headers: config.headers
2076
- });
2077
- if (!response.ok) {
2078
- switch (response.status) {
2079
- case 401:
2080
- throw new HttpUnauthorizedError();
2081
- case 403:
2082
- throw new HttpForbiddenError();
2083
- case 429:
2084
- throw new HttpRateLimitError();
2085
- }
2086
- throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2087
- details: await response.text()
2088
- });
2089
- }
2090
- return response.json();
2091
- }
2092
- var InvalidUrlError = class extends BaseError {
2093
- constructor(url) {
2094
- super(`URL "${url}" is not http/https.`);
2095
- __publicField(this, "name", "Router.InvalidUrlError");
2096
- }
2097
- };
2098
- var HttpUnauthorizedError = class extends BaseError {
2099
- constructor() {
2100
- super("Unauthorized.", {
2101
- metaMessages: ["Ensure that an API key is provided."]
2102
- });
2103
- __publicField(this, "name", "Router.HttpUnauthorizedError");
2341
+ };
2342
+ var HttpUnauthorizedError = class extends BaseError {
2343
+ constructor() {
2344
+ super("Unauthorized.", {
2345
+ metaMessages: ["Ensure that an API key is provided."]
2346
+ });
2347
+ __publicField(this, "name", "Router.HttpUnauthorizedError");
2104
2348
  }
2105
2349
  };
2106
2350
  var HttpForbiddenError = class extends BaseError {
@@ -2130,13 +2374,21 @@ var HttpGetApiFailedError = class extends BaseError {
2130
2374
  }
2131
2375
  };
2132
2376
 
2133
- // src/collectors/validations/Validation.ts
2134
- var Validation_exports = {};
2135
- __export(Validation_exports, {
2136
- run: () => run
2377
+ // src/gatekeeper/Gate.ts
2378
+ var Gate_exports = {};
2379
+ __export(Gate_exports, {
2380
+ batch: () => batch2,
2381
+ run: () => run,
2382
+ single: () => single
2137
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
+ }
2138
2390
  async function run(parameters) {
2139
- const { items, rules, ctx = {}, chunkSize } = parameters;
2391
+ const { items, rules, chunkSize } = parameters;
2140
2392
  const issues = [];
2141
2393
  let validItems = items.slice();
2142
2394
  for (const rule of rules) {
@@ -2145,7 +2397,7 @@ async function run(parameters) {
2145
2397
  if (rule.kind === "single") {
2146
2398
  for (let i = 0; i < validItems.length; i++) {
2147
2399
  const item = validItems[i];
2148
- const issue = await rule.run(item, ctx);
2400
+ const issue = await rule.run(item);
2149
2401
  if (issue) {
2150
2402
  issues.push({ ...issue, ruleName: rule.name, item });
2151
2403
  indicesToRemove.add(i);
@@ -2153,7 +2405,7 @@ async function run(parameters) {
2153
2405
  }
2154
2406
  } else if (rule.kind === "batch") {
2155
2407
  const exec = async (slice, offset) => {
2156
- const map = await rule.run(slice, ctx);
2408
+ const map = await rule.run(slice);
2157
2409
  for (let i = 0; i < slice.length; i++) {
2158
2410
  const issue = map.get(i);
2159
2411
  if (issue !== void 0) {
@@ -2177,157 +2429,265 @@ async function run(parameters) {
2177
2429
  };
2178
2430
  }
2179
2431
 
2180
- // src/collectors/validations/ValidationRule.ts
2181
- var ValidationRule_exports = {};
2182
- __export(ValidationRule_exports, {
2183
- batch: () => batch2,
2184
- morpho: () => morpho,
2185
- 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
2186
2441
  });
2187
- function single(name, run2) {
2188
- return { kind: "single", name, run: run2 };
2442
+ function getCallback(chain2, type) {
2443
+ return configs[chain2].callbacks?.find((c) => c.type === type);
2189
2444
  }
2190
- function batch2(name, run2) {
2191
- 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;
2192
2449
  }
2193
- function morpho() {
2194
- const chainId = single("chain_id", (offer, { chain }) => {
2195
- if (chain.id !== offer.chainId) {
2196
- return {
2197
- message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
2198
- };
2199
- }
2200
- });
2201
- const loanToken = single("loan_token", (offer, { chain }) => {
2202
- const tokens = new Set(
2203
- Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
2204
- );
2205
- if (!tokens.has(offer.loanToken.toLowerCase())) {
2206
- return {
2207
- message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
2208
- };
2209
- }
2210
- });
2211
- const expiry = single("expiry", (offer, _) => {
2212
- if (offer.expiry < Math.floor(Date.now() / 1e3)) {
2213
- return { message: "Expiry mismatch" };
2214
- }
2215
- });
2216
- const sellEmptyCallback = single(
2217
- "sell_offers_empty_callback",
2218
- (offer, _) => {
2219
- if (!offer.buy && offer.callback.data === "0x") {
2220
- return { message: "Sell offers require a non-empty callback." };
2221
- }
2222
- }
2223
- );
2224
- const buyNonEmptyCallback = single(
2225
- "buy_offers_non_empty_callback",
2226
- (offer, _) => {
2227
- if (offer.buy && offer.callback.data !== "0x") {
2228
- const allowed = new Set(
2229
- Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.BuyVaultV1Callback].map(
2230
- (a) => a.toLowerCase()
2231
- )
2232
- );
2233
- const callbackAddress = offer.callback.address?.toLowerCase();
2234
- if (!callbackAddress || !allowed.has(callbackAddress)) {
2235
- return {
2236
- message: "Buy offers with non-empty callback must use a whitelisted BuyVaultV1Callback."
2237
- };
2238
- }
2239
- }
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
+ });
2240
2621
  }
2241
- );
2242
- const sellNonWhitelistedCallback = single(
2243
- "sell_offers_non_whitelisted_callback",
2244
- (offer, _) => {
2245
- if (!offer.buy && offer.callback.data !== "0x") {
2246
- const allowed = new Set(
2247
- Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.SellERC20Callback].map(
2248
- (a) => a.toLowerCase()
2249
- )
2250
- );
2251
- const callbackAddress = offer.callback.address?.toLowerCase();
2252
- if (!callbackAddress || !allowed.has(callbackAddress)) {
2253
- return { message: "Sell offer callback address is not whitelisted." };
2254
- }
2255
- }
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;
2256
2640
  }
2257
- );
2258
- const sellCallbackDataInvalid = single(
2259
- "sell_offers_callback_data_invalid",
2260
- (offer, _) => {
2261
- if (!offer.buy && offer.callback.data !== "0x") {
2262
- try {
2263
- const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
2264
- if (decoded.length === 0) {
2265
- return { message: "Sell offer callback data must include at least one collateral." };
2266
- }
2267
- } catch (_2) {
2268
- return { message: "Sell offer callback data cannot be decoded." };
2269
- }
2270
- }
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." };
2271
2644
  }
2272
- );
2273
- const sellCallbackCollateralInvalid = single(
2274
- "sell_offers_callback_collateral_invalid",
2275
- (offer, _) => {
2276
- if (!offer.buy && offer.callback.data !== "0x") {
2277
- try {
2278
- const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
2279
- const offerCollaterals = new Set(
2280
- offer.collaterals.map((c) => c.asset.toLowerCase())
2281
- );
2282
- for (const { collateral } of decoded) {
2283
- if (!offerCollaterals.has(collateral.toLowerCase())) {
2284
- return { message: "Sell callback collateral is not part of offer collaterals." };
2285
- }
2286
- }
2287
- } catch (_2) {
2288
- }
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
+ };
2289
2653
  }
2290
- }
2291
- );
2292
- const buyCallbackDataInvalid = single(
2293
- "buy_offers_callback_data_invalid",
2294
- (offer, _) => {
2295
- if (offer.buy && offer.callback.data !== "0x") {
2296
- try {
2297
- const decoded = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2298
- if (decoded.length === 0) {
2299
- return { message: "Buy offer callback data must include at least one vault." };
2300
- }
2301
- } catch (_2) {
2302
- 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." };
2303
2657
  }
2304
2658
  }
2305
2659
  }
2306
- );
2660
+ });
2307
2661
  const buyCallbackVaultInvalid = batch2(
2308
2662
  "buy_offers_callback_vault_invalid",
2309
- async (offers, { client, chain }) => {
2663
+ async (offers) => {
2310
2664
  const validationIssues = /* @__PURE__ */ new Map();
2311
2665
  const offersByVaultAddress = /* @__PURE__ */ new Map();
2312
2666
  for (let i = 0; i < offers.length; i++) {
2313
2667
  const offer = offers[i];
2314
- if (offer.buy && offer.callback.data !== "0x") {
2315
- try {
2316
- const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2317
- for (const { vault } of callbackVaults) {
2318
- const normalizedVaultAddress = vault.toLowerCase();
2319
- if (!offersByVaultAddress.has(normalizedVaultAddress)) {
2320
- offersByVaultAddress.set(normalizedVaultAddress, []);
2321
- }
2322
- 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, []);
2323
2678
  }
2324
- } catch (_) {
2679
+ offersByVaultAddress.get(normalizedVaultAddress).push({ index: i, offer });
2325
2680
  }
2681
+ } catch (_) {
2326
2682
  }
2327
2683
  }
2328
2684
  const uniqueVaultAddresses = Array.from(offersByVaultAddress.keys());
2329
2685
  if (uniqueVaultAddresses.length === 0) return validationIssues;
2330
- 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;
2331
2691
  const multicallContracts = [];
2332
2692
  for (const vaultAddress of uniqueVaultAddresses) {
2333
2693
  multicallContracts.push({
@@ -2335,7 +2695,7 @@ function morpho() {
2335
2695
  abi: Abi_exports.ERC4626,
2336
2696
  functionName: "asset"
2337
2697
  });
2338
- for (const factoryAddress of whitelistedFactories) {
2698
+ for (const factoryAddress of allowedFactories) {
2339
2699
  multicallContracts.push({
2340
2700
  address: factoryAddress,
2341
2701
  abi: Abi_exports.MetaMorphoFactory,
@@ -2350,7 +2710,7 @@ function morpho() {
2350
2710
  });
2351
2711
  const vaultAssetByAddress = /* @__PURE__ */ new Map();
2352
2712
  const registeredVaults = /* @__PURE__ */ new Set();
2353
- const numberOfFactories = whitelistedFactories.length;
2713
+ const numberOfFactories = allowedFactories.length;
2354
2714
  let resultIndex = 0;
2355
2715
  for (const vaultAddress of uniqueVaultAddresses) {
2356
2716
  const assetCallResult = multicallResults[resultIndex++];
@@ -2409,30 +2769,61 @@ function morpho() {
2409
2769
  return validationIssues;
2410
2770
  }
2411
2771
  );
2412
- const maturity = single("maturity", (offer, _) => {
2413
- const allowedMaturities = [Maturity_exports.from("end_of_month"), Maturity_exports.from("end_of_next_month")];
2414
- if (!allowedMaturities.includes(offer.maturity)) {
2415
- return {
2416
- message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
2417
- };
2772
+ const expiry = single("expiry", (offer) => {
2773
+ if (offer.expiry < Math.floor(Date.now() / 1e3)) {
2774
+ return { message: "Expiry mismatch" };
2418
2775
  }
2419
2776
  });
2420
- return [
2421
- chainId,
2422
- loanToken,
2423
- expiry,
2424
- maturity,
2425
- // note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
2426
- // integrators should be able to choose if they want to keep the offer or not
2427
- sellEmptyCallback,
2428
- buyNonEmptyCallback,
2429
- sellNonWhitelistedCallback,
2430
- sellCallbackDataInvalid,
2431
- sellCallbackCollateralInvalid,
2432
- buyCallbackDataInvalid,
2433
- buyCallbackVaultInvalid
2434
- ];
2777
+ return [expiry, sellErc20CallbackInvalid, buyCallbackVaultInvalid];
2435
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
+ });
2436
2827
 
2437
2828
  // src/mempool/MempoolClient.ts
2438
2829
  var MempoolClient_exports = {};
@@ -2441,7 +2832,7 @@ __export(MempoolClient_exports, {
2441
2832
  });
2442
2833
  var DEFAULT_BATCH_SIZE2 = 100;
2443
2834
  var DEFAULT_BLOCK_WINDOW2 = 100;
2444
- function from9(parameters) {
2835
+ function from10(parameters) {
2445
2836
  const config = {
2446
2837
  client: parameters.client,
2447
2838
  mempoolAddress: parameters.mempoolAddress,
@@ -2450,24 +2841,24 @@ function from9(parameters) {
2450
2841
  return {
2451
2842
  add: (parameters2) => add(config, parameters2),
2452
2843
  get: (parameters2) => get(config, parameters2),
2453
- watch: (parameters2) => watch(config, parameters2),
2454
2844
  stream: (parameters2) => streamOffers(config, parameters2)
2455
2845
  };
2456
2846
  }
2457
- async function add(config, parameters) {
2458
- const offer = Offer_exports.from(parameters.offer);
2847
+ async function add(config, offers) {
2459
2848
  if (!config.client.account) throw new WalletAccountNotSetError();
2849
+ const tree = Tree_exports.from(offers.map((o) => Offer_exports.from(o)));
2460
2850
  const chainId = await getChainId(config.client);
2461
- if (BigInt(chainId) !== offer.chainId)
2462
- 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
+ }
2463
2855
  try {
2464
- const tx = await config.client.sendTransaction({
2856
+ return await config.client.sendTransaction({
2465
2857
  chain: config.client.chain,
2466
2858
  account: config.client.account,
2467
2859
  to: config.mempoolAddress,
2468
- data: Offer_exports.encode(offer)
2860
+ data: Tree_exports.encode(tree)
2469
2861
  });
2470
- return { offer, txHash: tx };
2471
2862
  } catch (error) {
2472
2863
  throw new ViemClientError(error instanceof Error ? error.message : "Unknown error");
2473
2864
  }
@@ -2477,40 +2868,17 @@ async function* get(config, parameters) {
2477
2868
  loanToken,
2478
2869
  blockNumberGte,
2479
2870
  blockNumberLte,
2480
- order = "desc",
2871
+ order: order2 = "desc",
2481
2872
  options: { maxBatchSize = DEFAULT_BATCH_SIZE2 } = {}
2482
2873
  } = parameters || {};
2483
2874
  yield* streamOffers(config, {
2484
2875
  loanToken,
2485
- order,
2876
+ order: order2,
2486
2877
  blockNumberGte,
2487
2878
  blockNumberLte,
2488
2879
  options: { maxBatchSize, blockWindow: config.blockWindow }
2489
2880
  });
2490
2881
  }
2491
- function watch(config, parameters) {
2492
- const {
2493
- loanToken,
2494
- lastSyncedBlock,
2495
- polling: { interval = 3e4, maxBatchSize = DEFAULT_BATCH_SIZE2 } = {},
2496
- onOffers
2497
- } = parameters;
2498
- return poll(
2499
- async () => {
2500
- const blockNumberGte = await lastSyncedBlock();
2501
- const stream = streamOffers(config, {
2502
- loanToken,
2503
- order: "asc",
2504
- blockNumberGte,
2505
- options: { maxBatchSize, blockWindow: config.blockWindow }
2506
- });
2507
- for await (const { offers, blockNumber } of stream) {
2508
- await onOffers(offers, blockNumber);
2509
- }
2510
- },
2511
- { interval }
2512
- );
2513
- }
2514
2882
  var chainIdCache = /* @__PURE__ */ new Map();
2515
2883
  var getChainId = async (client) => {
2516
2884
  if (chainIdCache.has(client.uid)) return chainIdCache.get(client.uid);
@@ -2523,7 +2891,7 @@ async function* streamOffers(config, parameters) {
2523
2891
  loanToken,
2524
2892
  blockNumberGte,
2525
2893
  blockNumberLte,
2526
- order = "desc",
2894
+ order: order2 = "desc",
2527
2895
  options: { maxBatchSize = DEFAULT_BATCH_SIZE2, blockWindow = config.blockWindow } = {}
2528
2896
  } = parameters;
2529
2897
  const stream = Chain_exports.streamLogs({
@@ -2537,28 +2905,29 @@ async function* streamOffers(config, parameters) {
2537
2905
  },
2538
2906
  blockNumberGte,
2539
2907
  blockNumberLte,
2540
- order,
2908
+ order: order2,
2541
2909
  options: { maxBatchSize, blockWindow }
2542
2910
  });
2543
- let blockNumber = order === "asc" ? blockNumberGte : blockNumberLte;
2911
+ let blockNumber = order2 === "asc" ? blockNumberGte : blockNumberLte;
2544
2912
  for await (const { logs, blockNumber: newBlockNumber } of stream) {
2545
2913
  blockNumber = newBlockNumber;
2546
2914
  if (logs.length === 0) break;
2547
- let offersAndBlockNumbers = logs.map((log) => {
2915
+ const offers = [];
2916
+ for (const log of logs) {
2917
+ if (!log) continue;
2548
2918
  const [payload] = viem.decodeAbiParameters([{ type: "bytes" }], log.data);
2549
2919
  try {
2550
- 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
+ }
2551
2925
  } catch (_) {
2552
- return null;
2553
2926
  }
2554
- }).filter((item) => item !== null);
2555
- if (loanToken)
2556
- offersAndBlockNumbers = offersAndBlockNumbers.filter(
2557
- (o) => o.offer.loanToken.toLowerCase() === loanToken.toLowerCase()
2558
- );
2559
- if (offersAndBlockNumbers.length === 0) continue;
2927
+ }
2928
+ if (offers.length === 0) continue;
2560
2929
  yield {
2561
- offers: offersAndBlockNumbers.map((item) => item.offer),
2930
+ offers,
2562
2931
  blockNumber
2563
2932
  };
2564
2933
  }
@@ -2586,7 +2955,127 @@ var ChainIdMismatchError = class extends BaseError {
2586
2955
 
2587
2956
  // src/mempool/MempoolClient.ts
2588
2957
  function connect2(parameters) {
2589
- 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;
2590
3079
  }
2591
3080
 
2592
3081
  exports.Abi = Abi_exports;
@@ -2597,6 +3086,8 @@ exports.Collateral = Collateral_exports;
2597
3086
  exports.Cursor = Cursor_exports;
2598
3087
  exports.Errors = Errors_exports;
2599
3088
  exports.Format = Format_exports;
3089
+ exports.GateConfig = GateConfig_exports;
3090
+ exports.Gatekeeper = Gatekeeper_exports;
2600
3091
  exports.LLTV = LLTV_exports;
2601
3092
  exports.Liquidity = Liquidity_exports;
2602
3093
  exports.Maturity = Maturity_exports;
@@ -2606,9 +3097,10 @@ exports.Offer = Offer_exports;
2606
3097
  exports.Quote = Quote_exports;
2607
3098
  exports.RouterApi = Schema_exports;
2608
3099
  exports.RouterClient = Client_exports;
3100
+ exports.Rules = Rules_exports;
2609
3101
  exports.Time = time_exports;
3102
+ exports.Tree = Tree_exports;
2610
3103
  exports.Utils = utils_exports;
2611
- exports.Validation = Validation_exports;
2612
- exports.ValidationRule = ValidationRule_exports;
3104
+ exports.Validation = Gate_exports;
2613
3105
  //# sourceMappingURL=index.browser.js.map
2614
3106
  //# sourceMappingURL=index.browser.js.map