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