@morpho-dev/router 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +28 -18
  2. package/dist/cli.js +3020 -2174
  3. package/dist/cli.js.map +1 -1
  4. package/dist/drizzle/VERSION.ts +3 -0
  5. package/dist/drizzle/drizzle.config.ts +18 -0
  6. package/dist/drizzle/index.ts +2 -0
  7. package/dist/drizzle/{router_v1.4/0000_add_obligation_id.sql → router_v1.5/0000_add_block_number_to_liquidity_graph_and_offer_liquidity_pools_relation.sql} +49 -39
  8. package/dist/drizzle/router_v1.5/0001_create_new_relations_to_prepare_new_liquidity_model.sql +55 -0
  9. package/dist/drizzle/router_v1.5/0002_add_new_offer_status_relation.sql +9 -0
  10. package/dist/drizzle/router_v1.5/0003_insert-status-code.sql +1 -0
  11. package/dist/drizzle/router_v1.5/0004_add_index_for_fast_book_lookup.sql +3 -0
  12. package/dist/drizzle/router_v1.5/0005_add_group_consumed_events_table.sql +12 -0
  13. package/dist/drizzle/router_v1.5/0006_add-trigger-for-consumed-events.sql +58 -0
  14. package/dist/drizzle/router_v1.5/0007_update_index_for_fast_book_lookup.sql +5 -0
  15. package/dist/drizzle/router_v1.5/0008_rename_consumed_events_table.sql +8 -0
  16. package/dist/drizzle/{router_v1.4 → router_v1.5}/meta/0000_snapshot.json +83 -27
  17. package/dist/drizzle/{router_v1.4 → router_v1.5}/meta/0001_snapshot.json +459 -27
  18. package/dist/drizzle/router_v1.5/meta/0002_snapshot.json +1463 -0
  19. package/dist/drizzle/router_v1.5/meta/0003_snapshot.json +1463 -0
  20. package/dist/drizzle/router_v1.5/meta/0004_snapshot.json +1569 -0
  21. package/dist/drizzle/router_v1.5/meta/0005_snapshot.json +1664 -0
  22. package/dist/drizzle/router_v1.5/meta/0006_snapshot.json +1664 -0
  23. package/dist/drizzle/router_v1.5/meta/0007_snapshot.json +1752 -0
  24. package/dist/drizzle/router_v1.5/meta/0008_snapshot.json +1752 -0
  25. package/dist/drizzle/router_v1.5/meta/_journal.json +69 -0
  26. package/dist/drizzle/schema.ts +363 -0
  27. package/dist/index.browser.d.cts +884 -156
  28. package/dist/index.browser.d.ts +884 -156
  29. package/dist/index.browser.js +1433 -1010
  30. package/dist/index.browser.js.map +1 -1
  31. package/dist/index.browser.mjs +1426 -1009
  32. package/dist/index.browser.mjs.map +1 -1
  33. package/dist/index.node.d.cts +2578 -930
  34. package/dist/index.node.d.ts +2578 -930
  35. package/dist/index.node.js +6544 -5459
  36. package/dist/index.node.js.map +1 -1
  37. package/dist/index.node.mjs +6769 -5688
  38. package/dist/index.node.mjs.map +1 -1
  39. package/package.json +17 -13
  40. package/dist/drizzle/router_v1.4/0001_update-primary-key-on-link.sql +0 -3
  41. package/dist/drizzle/router_v1.4/meta/_journal.json +0 -20
@@ -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,74 +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
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
493
- // WETH
494
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
495
- // WBTC
496
- ].map((address) => address.toLowerCase())
497
- ),
498
1126
  morpho: "0x0000000000000000000000000000000000000000",
499
1127
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
500
1128
  mempool: {
501
1129
  address: "0x0000000000000000000000000000000000000000",
502
1130
  deploymentBlock: 23347674,
503
1131
  reindexBuffer: 10
504
- },
505
- vaultV1Factory: {
506
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
507
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
508
1132
  }
509
1133
  },
510
1134
  base: {
511
1135
  ...base,
512
1136
  id: ChainId.BASE,
513
1137
  name: "base",
514
- whitelistedAssets: new Set(
515
- [
516
- "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
517
- // USDC
518
- "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
519
- // DAI
520
- "0x4200000000000000000000000000000000000006",
521
- // WETH
522
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
523
- // WBTC
524
- ].map((address) => address.toLowerCase())
525
- ),
526
1138
  morpho: "0x0000000000000000000000000000000000000000",
527
1139
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
528
1140
  mempool: {
529
1141
  address: "0x0000000000000000000000000000000000000000",
530
1142
  deploymentBlock: 35449942,
531
1143
  reindexBuffer: 10
532
- },
533
- vaultV1Factory: {
534
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
535
- "v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
536
1144
  }
537
1145
  },
538
1146
  "ethereum-virtual-testnet": {
539
1147
  ...mainnet,
540
1148
  id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
541
1149
  name: "ethereum-virtual-testnet",
542
- whitelistedAssets: new Set(
543
- [
544
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
545
- // USDC
546
- "0x6B175474E89094C44Da98b954EedeAC495271d0F",
547
- // DAI
548
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
549
- // WETH
550
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
551
- // WBTC
552
- ].map((address) => address.toLowerCase())
553
- ),
554
1150
  morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
555
1151
  // @TODO: This is mock Consumed contract, update with Terms once stable
556
1152
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
@@ -558,28 +1154,12 @@ var chains = {
558
1154
  address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
559
1155
  deploymentBlock: 23224302,
560
1156
  reindexBuffer: 10
561
- },
562
- vaultV1Factory: {
563
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
564
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
565
1157
  }
566
1158
  },
567
1159
  anvil: {
568
1160
  ...anvil,
569
1161
  id: ChainId.ANVIL,
570
1162
  name: "anvil",
571
- whitelistedAssets: new Set(
572
- [
573
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
574
- // USDC
575
- "0x6B175474E89094C44Da98b954EedeAC495271d0F",
576
- // DAI
577
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
578
- // WETH
579
- "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
580
- // WBTC
581
- ].map((address) => address.toLowerCase())
582
- ),
583
1163
  morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
584
1164
  morphoBlue: "0x0000000000000000000000000000000000000000",
585
1165
  // Set dynamically in tests
@@ -587,10 +1167,6 @@ var chains = {
587
1167
  address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
588
1168
  deploymentBlock: 23223727,
589
1169
  reindexBuffer: 10
590
- },
591
- vaultV1Factory: {
592
- "v1.0": "0x0000000000000000000000000000000000000000",
593
- "v1.1": "0x0000000000000000000000000000000000000000"
594
1170
  }
595
1171
  }
596
1172
  };
@@ -605,24 +1181,24 @@ async function* streamLogs(parameters) {
605
1181
  event,
606
1182
  blockNumberGte,
607
1183
  blockNumberLte,
608
- order = "desc",
1184
+ order: order2 = "desc",
609
1185
  options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
610
1186
  } = parameters;
611
1187
  if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
612
1188
  if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
613
- if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
1189
+ if (order2 === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
614
1190
  const latestBlock = (await getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
615
1191
  let toBlock = 0n;
616
- if (order === "asc")
1192
+ if (order2 === "asc")
617
1193
  toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
618
- if (order === "desc")
1194
+ if (order2 === "desc")
619
1195
  toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
620
1196
  let fromBlock = 0n;
621
- if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
622
- if (order === "desc")
1197
+ if (order2 === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
1198
+ if (order2 === "desc")
623
1199
  fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
624
- if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
625
- 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));
626
1202
  if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
627
1203
  let streaming = true;
628
1204
  while (streaming) {
@@ -634,10 +1210,10 @@ async function* streamLogs(parameters) {
634
1210
  });
635
1211
  logs.sort((a, b) => {
636
1212
  if (a.blockNumber !== b.blockNumber)
637
- 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);
638
1214
  if (a.transactionIndex !== b.transactionIndex)
639
- return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
640
- 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;
641
1217
  });
642
1218
  for (const logBatch of batch(logs, maxBatchSize)) {
643
1219
  if (logBatch.length === 0) break;
@@ -648,16 +1224,16 @@ async function* streamLogs(parameters) {
648
1224
  Number(logBatch[logBatch.length - 1]?.blockNumber)
649
1225
  ) : (
650
1226
  // if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
651
- order === "asc" ? Number(toBlock) : Number(fromBlock)
1227
+ order2 === "asc" ? Number(toBlock) : Number(fromBlock)
652
1228
  )
653
1229
  };
654
1230
  }
655
- streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
656
- if (order === "asc") {
1231
+ streaming = order2 === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
1232
+ if (order2 === "asc") {
657
1233
  fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
658
1234
  toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
659
1235
  }
660
- if (order === "desc") {
1236
+ if (order2 === "desc") {
661
1237
  const lowerBound = BigInt(blockNumberGte || 0);
662
1238
  const windowSize = BigInt(blockWindow);
663
1239
  const nextToBlock = max(fromBlock - 1n, lowerBound);
@@ -666,7 +1242,7 @@ async function* streamLogs(parameters) {
666
1242
  fromBlock = nextFromBlock;
667
1243
  }
668
1244
  }
669
- yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
1245
+ yield { logs: [], blockNumber: order2 === "asc" ? Number(toBlock) : Number(fromBlock) };
670
1246
  return;
671
1247
  }
672
1248
  var InvalidBlockRangeError = class extends BaseError {
@@ -705,7 +1281,8 @@ var Collateral_exports = {};
705
1281
  __export(Collateral_exports, {
706
1282
  CollateralSchema: () => CollateralSchema,
707
1283
  CollateralsSchema: () => CollateralsSchema,
708
- from: () => from4
1284
+ from: () => from4,
1285
+ random: () => random
709
1286
  });
710
1287
  var transformHex = (val, ctx) => {
711
1288
  if (isHex(val)) return val;
@@ -715,7 +1292,7 @@ var transformHex = (val, ctx) => {
715
1292
  format: "hex",
716
1293
  error: "not a hex"
717
1294
  });
718
- return z7.NEVER;
1295
+ return z9.NEVER;
719
1296
  };
720
1297
  var transformAddress = (val, ctx) => {
721
1298
  if (isAddress(val.toLowerCase())) return val.toLowerCase();
@@ -725,7 +1302,7 @@ var transformAddress = (val, ctx) => {
725
1302
  format: "address",
726
1303
  error: "not a valid address"
727
1304
  });
728
- return z7.NEVER;
1305
+ return z9.NEVER;
729
1306
  };
730
1307
 
731
1308
  // src/core/LLTV.ts
@@ -765,7 +1342,7 @@ var InvalidLLTVError = class extends BaseError {
765
1342
  __publicField(this, "name", "LLTV.InvalidLLTVError");
766
1343
  }
767
1344
  };
768
- var LLTVSchema = z7.bigint({ coerce: true }).refine(
1345
+ var LLTVSchema = z9.bigint({ coerce: true }).refine(
769
1346
  (lltv) => {
770
1347
  try {
771
1348
  from3(lltv);
@@ -782,12 +1359,12 @@ var LLTVSchema = z7.bigint({ coerce: true }).refine(
782
1359
  ).transform((lltv) => from3(lltv));
783
1360
 
784
1361
  // src/core/Collateral.ts
785
- var CollateralSchema = z7.object({
786
- asset: z7.string().transform(transformAddress),
787
- oracle: z7.string().transform(transformAddress),
1362
+ var CollateralSchema = z9.object({
1363
+ asset: z9.string().transform(transformAddress),
1364
+ oracle: z9.string().transform(transformAddress),
788
1365
  lltv: LLTVSchema
789
1366
  });
790
- 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(
791
1368
  (collaterals) => {
792
1369
  for (let i = 1; i < collaterals.length; i++) {
793
1370
  if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
@@ -822,6 +1399,13 @@ var from4 = (parameters) => {
822
1399
  oracle: parameters.oracle.toLowerCase()
823
1400
  };
824
1401
  };
1402
+ function random() {
1403
+ return from4({
1404
+ asset: privateKeyToAccount(generatePrivateKey()).address,
1405
+ oracle: privateKeyToAccount(generatePrivateKey()).address,
1406
+ lltv: 0.965
1407
+ });
1408
+ }
825
1409
 
826
1410
  // src/core/Liquidity.ts
827
1411
  var Liquidity_exports = {};
@@ -845,20 +1429,20 @@ function calculateMaxDebt(amount, oraclePrice, lltv) {
845
1429
  return maxDebt;
846
1430
  }
847
1431
  function generateBalancePoolId(parameters) {
848
- const { user, chainId, token } = parameters;
849
- return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
1432
+ const { user, chainId, token: token2 } = parameters;
1433
+ return `${user}-${chainId.toString()}-${token2}-balance`.toLowerCase();
850
1434
  }
851
1435
  function generateAllowancePoolId(parameters) {
852
- const { user, chainId, token } = parameters;
853
- return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
1436
+ const { user, chainId, token: token2 } = parameters;
1437
+ return `${user}-${chainId.toString()}-${token2}-allowance`.toLowerCase();
854
1438
  }
855
1439
  function generateSellERC20CallbackPoolId(parameters) {
856
- const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
857
- 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();
858
1442
  }
859
1443
  function generateObligationCollateralPoolId(parameters) {
860
- const { user, chainId, obligationId: obligationId2, token } = parameters;
861
- 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();
862
1446
  }
863
1447
  function generateBuyVaultCallbackPoolId(parameters) {
864
1448
  const { user, chainId, vault, offerHash } = parameters;
@@ -888,12 +1472,13 @@ __export(Maturity_exports, {
888
1472
  InvalidFormatError: () => InvalidFormatError,
889
1473
  InvalidOptionError: () => InvalidOptionError2,
890
1474
  MaturitySchema: () => MaturitySchema,
1475
+ MaturityType: () => MaturityType,
891
1476
  from: () => from5
892
1477
  });
893
- var MaturitySchema = z7.number().int().refine(
894
- (maturity) => {
1478
+ var MaturitySchema = z9.number().int().refine(
1479
+ (maturity2) => {
895
1480
  try {
896
- from5(maturity);
1481
+ from5(maturity2);
897
1482
  return true;
898
1483
  } catch (_e) {
899
1484
  return false;
@@ -909,7 +1494,16 @@ var MaturitySchema = z7.number().int().refine(
909
1494
  }
910
1495
  }
911
1496
  }
912
- ).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 || {});
913
1507
  var MaturityOptions = {
914
1508
  end_of_week: () => endOfWeek(),
915
1509
  end_of_next_week: () => endOfNextWeek(),
@@ -930,8 +1524,26 @@ function from5(ts) {
930
1524
  }
931
1525
  var endOfWeek = () => fridayOfWeek(0);
932
1526
  var endOfNextWeek = () => fridayOfWeek(1);
933
- var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
934
- 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
+ };
935
1547
  var endOfQuarter = () => lastFridayOfQuarter(0);
936
1548
  var endOfNextQuarter = () => lastFridayOfQuarter(1);
937
1549
  var fridayOfWeek = (weeksAhead = 0) => {
@@ -952,8 +1564,8 @@ var lastFridayOfMonth = (year, month) => {
952
1564
  while (lastDayOfMonth15H.getUTCDay() !== 5) {
953
1565
  lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
954
1566
  }
955
- const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
956
- return maturity;
1567
+ const maturity2 = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
1568
+ return maturity2;
957
1569
  };
958
1570
  var lastFridayOfQuarter = (quartersAhead = 0) => {
959
1571
  const now2 = /* @__PURE__ */ new Date();
@@ -999,11 +1611,11 @@ __export(Obligation_exports, {
999
1611
  from: () => from6,
1000
1612
  fromSnakeCase: () => fromSnakeCase2,
1001
1613
  id: () => id,
1002
- random: () => random
1614
+ random: () => random2
1003
1615
  });
1004
- var ObligationSchema = z7.object({
1005
- chainId: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
1006
- 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),
1007
1619
  collaterals: CollateralsSchema,
1008
1620
  maturity: MaturitySchema
1009
1621
  });
@@ -1061,7 +1673,7 @@ function id(obligation) {
1061
1673
  )
1062
1674
  );
1063
1675
  }
1064
- function random() {
1676
+ function random2() {
1065
1677
  return from6({
1066
1678
  chainId: 1n,
1067
1679
  loanToken: privateKeyToAccount(generatePrivateKey()).address,
@@ -1095,170 +1707,138 @@ __export(Offer_exports, {
1095
1707
  InvalidOfferError: () => InvalidOfferError,
1096
1708
  OfferHashSchema: () => OfferHashSchema,
1097
1709
  OfferSchema: () => OfferSchema,
1710
+ StatusCode: () => StatusCode,
1098
1711
  consumedEvent: () => consumedEvent,
1099
- decode: () => decode,
1712
+ decode: () => decode4,
1100
1713
  domain: () => domain,
1101
- encode: () => encode,
1102
- from: () => from7,
1714
+ encode: () => encode4,
1715
+ from: () => from8,
1103
1716
  fromConsumedLog: () => fromConsumedLog,
1104
1717
  fromSnakeCase: () => fromSnakeCase3,
1105
1718
  hash: () => hash,
1106
1719
  obligationId: () => obligationId,
1107
- random: () => random2,
1720
+ random: () => random3,
1108
1721
  sign: () => sign,
1722
+ signatureMsg: () => signatureMsg,
1109
1723
  toSnakeCase: () => toSnakeCase2,
1110
1724
  types: () => types
1111
1725
  });
1112
1726
 
1113
- // src/utils/index.ts
1114
- var utils_exports = {};
1115
- __export(utils_exports, {
1116
- BaseError: () => BaseError,
1117
- Time: () => time_exports,
1118
- batch: () => batch,
1119
- batchMulticall: () => batchMulticall,
1120
- fromSnakeCase: () => fromSnakeCase,
1121
- lazy: () => lazy,
1122
- max: () => max,
1123
- min: () => min,
1124
- poll: () => poll,
1125
- retry: () => retry,
1126
- toSnakeCase: () => toSnakeCase,
1127
- 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
1128
1734
  });
1129
-
1130
- // src/utils/retry.ts
1131
- var retry = async (fn, attempts = 3, delayMs = 50) => {
1132
- let lastErr;
1133
- for (let i = 0; i < attempts; i++) {
1134
- try {
1135
- return await fn();
1136
- } catch (err) {
1137
- lastErr = err;
1138
- if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
1139
- }
1140
- }
1141
- 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 });
1142
1742
  };
1143
-
1144
- // src/utils/batchMulticall.ts
1145
- async function batchMulticall(parameters) {
1146
- const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
1147
- const results = [];
1148
- for (const callsBatch of batch(calls, batchSize)) {
1149
- const batchResults = await retry(
1150
- () => client.multicall({
1151
- allowFailure: false,
1152
- contracts: callsBatch,
1153
- ...blockNumber ? { blockNumber } : {}
1154
- }),
1155
- retryAttempts,
1156
- retryDelayMs
1157
- );
1158
- 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}`);
1159
1783
  }
1160
- return results;
1161
- }
1162
-
1163
- // src/utils/lazy.ts
1164
- function lazy(pollFn) {
1165
- return () => async function* () {
1166
- let active = true;
1167
- let resolveNext = null;
1168
- const queue = [];
1169
- const wait2 = () => new Promise((resolve) => {
1170
- resolveNext = resolve;
1171
- });
1172
- const emit = (item) => {
1173
- queue.push(item);
1174
- resolveNext?.();
1175
- resolveNext = null;
1176
- };
1177
- let unpoll = null;
1178
- const stop = () => {
1179
- active = false;
1180
- unpoll?.();
1181
- resolveNext?.();
1182
- resolveNext = null;
1183
- };
1184
- unpoll = pollFn(emit, { stop });
1185
- try {
1186
- while (active) {
1187
- if (queue.length === 0) await wait2();
1188
- while (queue.length > 0 && active) yield queue.shift();
1189
- }
1190
- } finally {
1191
- stop();
1192
- }
1193
- }();
1194
- }
1195
-
1196
- // src/utils/wait.ts
1197
- async function wait(time) {
1198
- return new Promise((res) => setTimeout(res, time));
1199
- }
1200
-
1201
- // src/utils/poll.ts
1202
- function poll(fn, { interval }) {
1203
- let active = true;
1204
- const unwatch = () => active = false;
1205
- const watch2 = async () => {
1206
- await wait(interval);
1207
- const poll2 = async () => {
1208
- if (!active) return;
1209
- await fn({ unpoll: unwatch });
1210
- await wait(interval);
1211
- poll2();
1212
- };
1213
- poll2();
1214
- };
1215
- watch2();
1216
- return unwatch;
1217
- }
1218
-
1219
- // src/utils/time.ts
1220
- var time_exports = {};
1221
- __export(time_exports, {
1222
- max: () => max2,
1223
- now: () => now
1224
- });
1225
- function now() {
1226
- return Math.floor(Date.now() / 1e3);
1227
- }
1228
- function max2() {
1229
- return 864e16;
1230
- }
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
+ };
1231
1805
 
1232
1806
  // src/core/Offer.ts
1233
- 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}$/, {
1234
1813
  message: "Hash must be a valid 32-byte hex string"
1235
1814
  }).transform(transformHex);
1236
1815
  var OfferSchema = (parameters) => {
1237
- const { omitHash = false } = parameters || {};
1238
- const now2 = time_exports.now();
1239
- let base = z7.object({
1240
- offering: z7.string().transform(transformAddress),
1241
- assets: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
1242
- 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),
1243
1821
  maturity: MaturitySchema,
1244
- expiry: z7.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
1245
- start: z7.number().int().max(Number.MAX_SAFE_INTEGER),
1246
- nonce: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
1247
- buy: z7.boolean(),
1248
- chainId: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
1249
- 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),
1250
1828
  collaterals: CollateralsSchema,
1251
- callback: z7.object({
1252
- address: z7.string().transform(transformAddress),
1253
- data: z7.string().transform(transformHex),
1254
- 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)
1255
1833
  }),
1256
- consumed: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
1257
- blockNumber: z7.number().int().max(Number.MAX_SAFE_INTEGER),
1258
- signature: z7.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1834
+ signature: z9.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1259
1835
  message: "Signature must be a valid 65-byte hex string"
1260
1836
  }).transform(transformHex).optional()
1261
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) });
1262
1842
  if (!omitHash) base = base.extend({ hash: OfferHashSchema });
1263
1843
  return base.refine((data) => data.start < data.expiry, {
1264
1844
  message: "Start must be before expiry",
@@ -1268,7 +1848,7 @@ var OfferSchema = (parameters) => {
1268
1848
  path: ["expiry"]
1269
1849
  });
1270
1850
  };
1271
- function from7(input) {
1851
+ function from8(input) {
1272
1852
  try {
1273
1853
  const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1274
1854
  const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
@@ -1281,13 +1861,13 @@ function from7(input) {
1281
1861
  }
1282
1862
  }
1283
1863
  function fromSnakeCase3(input) {
1284
- return from7(fromSnakeCase(input));
1864
+ return from8(fromSnakeCase(input));
1285
1865
  }
1286
1866
  function toSnakeCase2(offer) {
1287
1867
  return toSnakeCase(offer);
1288
1868
  }
1289
- function random2(config) {
1290
- const chain = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
1869
+ function random3(config) {
1870
+ const chain2 = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
1291
1871
  const loanToken = config?.loanTokens ? config.loanTokens[Math.floor(Math.random() * config.loanTokens.length)] : privateKeyToAccount(generatePrivateKey()).address;
1292
1872
  const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [privateKeyToAccount(generatePrivateKey()).address];
1293
1873
  const collateralAsset = collateralCandidates[Math.floor(Math.random() * collateralCandidates.length)];
@@ -1295,7 +1875,7 @@ function random2(config) {
1295
1875
  ["end_of_month", 1],
1296
1876
  ["end_of_next_month", 1]
1297
1877
  ]);
1298
- const maturity = config?.maturity ?? from5(maturityOption);
1878
+ const maturity2 = config?.maturity ?? from5(maturityOption);
1299
1879
  const lltv = from3(
1300
1880
  weightedChoice([
1301
1881
  [0.385, 1],
@@ -1331,32 +1911,29 @@ function random2(config) {
1331
1911
  const consumed = config?.consumed !== void 0 ? config.consumed : Math.random() < 0.8 ? 0n : assetsScaled * BigInt(1 + Math.floor(Math.random() * 900)) / 1000n;
1332
1912
  const callbackBySide = (() => {
1333
1913
  if (buy) return { address: zeroAddress, data: "0x", gasLimit: 0n };
1334
- const sellCallbackAddress = WhitelistedCallbackAddresses["sell_erc20_callback" /* SellERC20Callback */][0].toLowerCase();
1914
+ const sellCallbackAddress = "0x3333333333333333333333333333333333333333";
1335
1915
  const amount = assetsScaled * 1000000000000000000000n;
1336
1916
  const data = encodeSellERC20Callback({
1337
1917
  collaterals: [collateralAsset],
1338
1918
  amounts: [amount]
1339
1919
  });
1340
- return { address: sellCallbackAddress, data, gasLimit: 500000n };
1920
+ return { address: sellCallbackAddress, data, gasLimit: 0n };
1341
1921
  })();
1342
- const offer = from7({
1922
+ const offer = from8({
1343
1923
  offering: config?.offering ?? privateKeyToAccount(generatePrivateKey()).address,
1344
1924
  assets: assetsScaled,
1345
1925
  rate,
1346
- maturity,
1347
- expiry: config?.expiry ?? maturity - 1,
1348
- start: config?.start ?? maturity - 10,
1926
+ maturity: maturity2,
1927
+ expiry: config?.expiry ?? maturity2 - 1,
1928
+ start: config?.start ?? maturity2 - 10,
1349
1929
  nonce: BigInt(Math.floor(Math.random() * 1e6)),
1350
1930
  buy,
1351
- chainId: chain.id,
1931
+ chainId: chain2.id,
1352
1932
  loanToken,
1353
- collaterals: config?.collaterals ?? [
1354
- from4({
1355
- asset: collateralAsset,
1356
- oracle: zeroAddress,
1357
- lltv
1358
- })
1359
- ],
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)),
1360
1937
  callback: config?.callback ?? callbackBySide,
1361
1938
  consumed,
1362
1939
  blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
@@ -1404,31 +1981,16 @@ var types = {
1404
1981
  { name: "gasLimit", type: "uint256" }
1405
1982
  ]
1406
1983
  };
1407
- function sign(offer, wallet) {
1984
+ async function sign(offers, wallet) {
1408
1985
  if (!wallet.account) throw new AccountNotSetError();
1409
- return wallet.signTypedData({
1986
+ return wallet.signMessage({
1410
1987
  account: wallet.account,
1411
- domain: domain(offer.chainId),
1412
- types,
1413
- primaryType: "Offer",
1414
- message: {
1415
- offering: offer.offering.toLowerCase(),
1416
- assets: offer.assets,
1417
- rate: offer.rate,
1418
- maturity: BigInt(offer.maturity),
1419
- expiry: BigInt(offer.expiry),
1420
- nonce: offer.nonce,
1421
- buy: offer.buy,
1422
- loanToken: offer.loanToken.toLowerCase(),
1423
- collaterals: offer.collaterals,
1424
- callback: {
1425
- address: offer.callback.address.toLowerCase(),
1426
- data: offer.callback.data,
1427
- gasLimit: offer.callback.gasLimit
1428
- }
1429
- }
1988
+ message: { raw: signatureMsg(offers) }
1430
1989
  });
1431
1990
  }
1991
+ function signatureMsg(offers) {
1992
+ return from7(offers).root;
1993
+ }
1432
1994
  function hash(offer) {
1433
1995
  return hashTypedData({
1434
1996
  domain: domain(offer.chainId),
@@ -1493,7 +2055,7 @@ var OfferAbi = [
1493
2055
  },
1494
2056
  { name: "signature", type: "bytes" }
1495
2057
  ];
1496
- function encode(offer) {
2058
+ function encode4(offer) {
1497
2059
  return encodeAbiParameters(OfferAbi, [
1498
2060
  offer.offering,
1499
2061
  offer.assets,
@@ -1510,14 +2072,14 @@ function encode(offer) {
1510
2072
  offer.signature ?? "0x"
1511
2073
  ]);
1512
2074
  }
1513
- function decode(data, blockNumber) {
2075
+ function decode4(data, blockNumber) {
1514
2076
  let decoded;
1515
2077
  try {
1516
2078
  decoded = decodeAbiParameters(OfferAbi, data);
1517
2079
  } catch (error) {
1518
2080
  throw new InvalidOfferError(error);
1519
2081
  }
1520
- const offer = from7({
2082
+ const offer = from8({
1521
2083
  offering: decoded[0],
1522
2084
  assets: decoded[1],
1523
2085
  rate: decoded[2],
@@ -1585,20 +2147,20 @@ var Quote_exports = {};
1585
2147
  __export(Quote_exports, {
1586
2148
  InvalidQuoteError: () => InvalidQuoteError,
1587
2149
  QuoteSchema: () => QuoteSchema,
1588
- from: () => from8,
2150
+ from: () => from9,
1589
2151
  fromSnakeCase: () => fromSnakeCase4,
1590
- random: () => random3
2152
+ random: () => random4
1591
2153
  });
1592
- var QuoteSchema = z7.object({
1593
- obligationId: z7.string().transform(transformHex),
1594
- ask: z7.object({
1595
- 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)
1596
2158
  }),
1597
- bid: z7.object({
1598
- rate: z7.bigint({ coerce: true }).min(0n).max(maxUint256)
2159
+ bid: z9.object({
2160
+ rate: z9.bigint({ coerce: true }).min(0n).max(maxUint256)
1599
2161
  })
1600
2162
  });
1601
- function from8(parameters) {
2163
+ function from9(parameters) {
1602
2164
  try {
1603
2165
  const parsedQuote = QuoteSchema.parse(parameters);
1604
2166
  return {
@@ -1611,10 +2173,10 @@ function from8(parameters) {
1611
2173
  }
1612
2174
  }
1613
2175
  function fromSnakeCase4(snake) {
1614
- return from8(fromSnakeCase(snake));
2176
+ return from9(fromSnakeCase(snake));
1615
2177
  }
1616
- function random3() {
1617
- return from8({
2178
+ function random4() {
2179
+ return from9({
1618
2180
  obligationId: Obligation_exports.id(Obligation_exports.random()),
1619
2181
  ask: {
1620
2182
  rate: BigInt(Math.floor(Math.random() * 1e6))
@@ -1634,494 +2196,83 @@ var InvalidQuoteError = class extends BaseError {
1634
2196
  // src/core/types.ts
1635
2197
  var BrandTypeId = Symbol.for("mempool/Brand");
1636
2198
 
1637
- // src/stores/utils/Cursor.ts
1638
- var Cursor_exports = {};
1639
- __export(Cursor_exports, {
1640
- decode: () => decode2,
1641
- encode: () => encode2,
1642
- validate: () => validate
1643
- });
1644
- function validate(cursor) {
1645
- if (!cursor || typeof cursor !== "object") {
1646
- throw new Error("Cursor must be an object");
1647
- }
1648
- const c = cursor;
1649
- if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
1650
- throw new Error(
1651
- `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
1652
- );
1653
- }
1654
- if (!["asc", "desc"].includes(c.dir)) {
1655
- throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
1656
- }
1657
- if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
1658
- throw new Error(
1659
- `Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
1660
- );
1661
- }
1662
- const validations = {
1663
- rate: {
1664
- field: "rate",
1665
- type: "string",
1666
- pattern: /^\d+$/,
1667
- error: "numeric string"
1668
- },
1669
- amount: {
1670
- field: "assets",
1671
- type: "string",
1672
- pattern: /^\d+$/,
1673
- error: "numeric string"
1674
- },
1675
- maturity: {
1676
- field: "maturity",
1677
- type: "number",
1678
- validator: (val) => val > 0,
1679
- error: "positive number"
1680
- },
1681
- expiry: {
1682
- field: "expiry",
1683
- type: "number",
1684
- validator: (val) => val > 0,
1685
- error: "positive number"
1686
- }
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)
1687
2215
  };
1688
- const validation = validations[c.sort];
1689
- if (!validation) {
1690
- throw new Error(`Invalid sort field: ${c.sort}`);
1691
- }
1692
- const fieldValue = c[validation.field];
1693
- if (!fieldValue) {
1694
- throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
1695
- }
1696
- if (typeof fieldValue !== validation.type) {
1697
- throw new Error(
1698
- `${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
1699
- );
1700
- }
1701
- if (validation.pattern && !validation.pattern.test(fieldValue)) {
1702
- throw new Error(
1703
- `Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
1704
- );
1705
- }
1706
- if (validation.validator && !validation.validator(fieldValue)) {
1707
- throw new Error(
1708
- `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
1709
- );
1710
- }
1711
- if (c.page !== void 0) {
1712
- if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
1713
- throw new Error("Invalid page: must be a positive integer");
1714
- }
1715
- }
1716
- return true;
1717
2216
  }
1718
- function encode2(c) {
1719
- return Base64.encodeURL(JSON.stringify(c));
1720
- }
1721
- function decode2(token) {
1722
- if (!token) return null;
1723
- const decoded = JSON.parse(Base64.decode(token));
1724
- validate(decoded);
1725
- return decoded;
1726
- }
1727
-
1728
- // src/api/Schema/requests.ts
1729
- var MAX_LIMIT = 100;
1730
- var DEFAULT_LIMIT = 20;
1731
- var PaginationQueryParams = z7.object({
1732
- cursor: z7.string().optional().refine(
1733
- (val) => {
1734
- if (!val) return true;
1735
- try {
1736
- const decoded = Cursor_exports.decode(val);
1737
- return decoded !== null;
1738
- } catch (_error) {
1739
- return false;
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
1740
2225
  }
1741
- },
1742
- {
1743
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1744
- }
1745
- ).meta({
1746
- description: "Pagination cursor in base64url-encoded format",
1747
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
1748
- }),
1749
- limit: z7.string().regex(/^[1-9]\d*$/, {
1750
- message: "Limit must be a positive integer"
1751
- }).transform((val) => Number.parseInt(val, 10)).pipe(
1752
- z7.number().max(MAX_LIMIT, {
1753
- message: `Limit cannot exceed ${MAX_LIMIT}`
1754
- })
1755
- ).optional().default(DEFAULT_LIMIT).meta({
1756
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
1757
- example: 10
1758
- })
1759
- });
1760
- var GetOffersQueryParams = z7.object({
1761
- ...PaginationQueryParams.shape,
1762
- side: z7.enum(["buy", "sell"]).meta({
1763
- description: "Side of the offer.",
1764
- example: "buy"
1765
- }),
1766
- 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({
1767
- description: "Offers obligation id",
1768
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1769
- })
1770
- });
1771
- var GetObligationsQueryParams = z7.object({
1772
- ...PaginationQueryParams.shape,
1773
- cursor: z7.string().optional().meta({
1774
- description: "Obligation id cursor",
1775
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1776
- })
1777
- });
1778
- var schemas = {
1779
- get_offers: GetOffersQueryParams,
1780
- get_obligations: GetObligationsQueryParams
1781
- };
1782
- function parse(action, query) {
1783
- return schemas[action].parse(query);
1784
- }
1785
- function safeParse(action, query, error) {
1786
- return schemas[action].safeParse(query, {
1787
- error
1788
- });
1789
- }
1790
-
1791
- // src/api/Schema/openapi.ts
1792
- var timestampExample = "2024-01-01T12:00:00.000Z";
1793
- var cursorExample = "eyJvZmZzZXQiOjEwMH0";
1794
- function makeSuccessResponse(parameters) {
1795
- const { dataSchema, dataDescription, dataExample, cursor } = parameters;
1796
- const withDataMeta = dataDescription ? dataSchema.meta({ description: dataDescription }) : dataSchema;
1797
- return z.object({
1798
- status: z.literal("success"),
1799
- cursor: z.string().nullable(),
1800
- data: z.any(),
1801
- meta: z.object({
1802
- timestamp: z.string()
1803
- })
1804
- }).extend({
1805
- data: withDataMeta
1806
- }).meta({
1807
- example: {
1808
- status: "success",
1809
- cursor,
1810
- data: dataExample,
1811
- meta: { timestamp: timestampExample }
1812
2226
  }
1813
2227
  });
1814
- }
1815
- var OffersSuccessResponseSchema = makeSuccessResponse({
1816
- dataSchema: z.array(z.any()),
1817
- dataDescription: "Offers matching the provided filters.",
1818
- dataExample: [toSnakeCase(Offer_exports.random())],
1819
- cursor: cursorExample
1820
- });
1821
- var ObligationsSuccessResponseSchema = makeSuccessResponse({
1822
- dataSchema: z.array(z.any()),
1823
- dataDescription: "Obligations known to the router.",
1824
- dataExample: [toSnakeCase(Obligation_exports.random())],
1825
- cursor: cursorExample
1826
- });
1827
- var RouterStatusSuccessResponseSchema = makeSuccessResponse({
1828
- dataSchema: RouterStatusResponse,
1829
- dataDescription: "Aggregated router status.",
1830
- dataExample: { status: "live" },
1831
- cursor: null
1832
- });
1833
- var CollectorsHealthSuccessResponseSchema = makeSuccessResponse({
1834
- dataSchema: CollectorsHealthResponse,
1835
- dataDescription: "Collectors health details and sync status.",
1836
- dataExample: [
1837
- {
1838
- name: "mempool_offers",
1839
- chain_id: "1",
1840
- block_number: 21345678,
1841
- updated_at: "2024-01-01T12:00:00.000Z",
1842
- lag: 0,
1843
- status: "live"
1844
- }
1845
- ],
1846
- cursor: null
1847
- });
1848
- var ChainsHealthSuccessResponseSchema = makeSuccessResponse({
1849
- dataSchema: ChainsHealthResponse,
1850
- dataDescription: "Latest processed block per chain.",
1851
- dataExample: [
1852
- {
1853
- chain_id: "1",
1854
- block_number: 21345678,
1855
- updated_at: "2024-01-01T12:00:00.000Z"
1856
- }
1857
- ],
1858
- cursor: null
1859
- });
1860
- var errorResponseSchema = z.object({
1861
- status: z.literal("error"),
1862
- error: z.object({
1863
- code: z.string(),
1864
- message: z.string(),
1865
- details: z.any().optional()
1866
- }),
1867
- meta: z.object({
1868
- timestamp: z.string()
1869
- })
1870
- }).meta({
1871
- description: "Error response wrapper.",
1872
- example: {
1873
- status: "error",
1874
- error: {
1875
- code: "VALIDATION_ERROR",
1876
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object",
1877
- details: [
1878
- {
1879
- field: "cursor",
1880
- issue: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1881
- }
1882
- ]
1883
- },
1884
- meta: {
1885
- timestamp: timestampExample
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();
1886
2236
  }
2237
+ throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2238
+ details: JSON.stringify(error)
2239
+ });
1887
2240
  }
1888
- });
1889
- var paths = {
1890
- "/v1/offers": {
1891
- get: {
1892
- summary: "Offers",
1893
- description: "Find offers that match specific criteria",
1894
- tags: ["Offers"],
1895
- requestParams: {
1896
- query: GetOffersQueryParams
1897
- },
1898
- responses: {
1899
- 200: {
1900
- description: "Success",
1901
- content: {
1902
- "application/json": {
1903
- schema: OffersSuccessResponseSchema
1904
- }
1905
- }
1906
- },
1907
- 400: {
1908
- description: "Bad Request",
1909
- content: {
1910
- "application/json": {
1911
- schema: errorResponseSchema
1912
- }
1913
- }
1914
- }
1915
- }
1916
- }
1917
- },
1918
- "/v1/obligations": {
1919
- get: {
1920
- summary: "Obligations",
1921
- description: "List obligations with pagination support",
1922
- tags: ["Obligations"],
1923
- requestParams: {
1924
- query: GetObligationsQueryParams
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
1925
2257
  },
1926
- responses: {
1927
- 200: {
1928
- description: "Success",
1929
- content: {
1930
- "application/json": {
1931
- schema: ObligationsSuccessResponseSchema
1932
- }
1933
- }
1934
- },
1935
- 400: {
1936
- description: "Bad Request",
1937
- content: {
1938
- "application/json": {
1939
- schema: errorResponseSchema
1940
- }
1941
- }
1942
- }
1943
- }
1944
- }
1945
- },
1946
- "/v1/health": {
1947
- get: {
1948
- summary: "Router status",
1949
- description: "Retrieve the aggregated status of the router.",
1950
- tags: ["Health"],
1951
- responses: {
1952
- 200: {
1953
- description: "Success",
1954
- content: {
1955
- "application/json": {
1956
- schema: RouterStatusSuccessResponseSchema
1957
- }
1958
- }
1959
- }
1960
- }
1961
- }
1962
- },
1963
- "/v1/health/collectors": {
1964
- get: {
1965
- summary: "Collectors health",
1966
- description: "Retrieve the block numbers processed by collectors and their sync status.",
1967
- tags: ["Health"],
1968
- responses: {
1969
- 200: {
1970
- description: "Success",
1971
- content: {
1972
- "application/json": {
1973
- schema: CollectorsHealthSuccessResponseSchema
1974
- }
1975
- }
1976
- }
1977
- }
1978
- }
1979
- },
1980
- "/v1/health/chains": {
1981
- get: {
1982
- summary: "Chains health",
1983
- description: "Retrieve the latest block processed for each chain.",
1984
- tags: ["Health"],
1985
- responses: {
1986
- 200: {
1987
- description: "Success",
1988
- content: {
1989
- "application/json": {
1990
- schema: ChainsHealthSuccessResponseSchema
1991
- }
1992
- }
1993
- }
1994
- }
1995
- }
1996
- }
1997
- };
1998
- var OpenApi = createDocument({
1999
- openapi: "3.1.0",
2000
- info: {
2001
- title: "Router API",
2002
- version: "1.0.0",
2003
- description: "API for the Morpho Router"
2004
- },
2005
- tags: [
2006
- {
2007
- name: "Offers"
2008
- },
2009
- {
2010
- name: "Obligations"
2011
- },
2012
- {
2013
- name: "Health"
2014
- }
2015
- ],
2016
- servers: [
2017
- {
2018
- url: "https://router.morpho.dev",
2019
- description: "Production server"
2020
- },
2021
- {
2022
- url: "http://localhost:7891",
2023
- description: "Local development server"
2024
- }
2025
- ],
2026
- paths
2027
- });
2028
-
2029
- // src/client/Client.ts
2030
- var Client_exports = {};
2031
- __export(Client_exports, {
2032
- HttpForbiddenError: () => HttpForbiddenError,
2033
- HttpGetApiFailedError: () => HttpGetApiFailedError,
2034
- HttpRateLimitError: () => HttpRateLimitError,
2035
- HttpUnauthorizedError: () => HttpUnauthorizedError,
2036
- InvalidUrlError: () => InvalidUrlError,
2037
- connect: () => connect,
2038
- getObligations: () => getObligations,
2039
- getOffers: () => getOffers
2040
- });
2041
- function connect(options) {
2042
- const u = new URL(options?.url || "https://router.morpho.dev");
2043
- if (u.protocol !== "http:" && u.protocol !== "https:") {
2044
- throw new InvalidUrlError(u.toString());
2045
- }
2046
- const headers = options?.headers ?? new Headers();
2047
- headers.set("Content-Type", "application/json");
2048
- options?.apiKey !== void 0 ? headers.set("X-API-Key", options.apiKey) : null;
2049
- const config = {
2050
- url: u,
2051
- headers
2052
- };
2053
- return {
2054
- ...config,
2055
- getOffers: (parameters) => getOffers(config, parameters),
2056
- getObligations: (parameters) => getObligations(config, parameters)
2057
- };
2058
- }
2059
- async function getOffers(config, parameters) {
2060
- const url = new URL(`${config.url.toString()}v1/offers`);
2061
- url.searchParams.set("side", parameters.side);
2062
- url.searchParams.set("obligation_id", parameters.obligationId.toString());
2063
- if (parameters.cursor) {
2064
- url.searchParams.set("cursor", parameters.cursor);
2065
- }
2066
- if (parameters.limit !== void 0) {
2067
- url.searchParams.set("limit", parameters.limit.toString());
2068
- }
2069
- const { cursor: returnedCursor, data: offers } = await getApi(config, url);
2070
- const routerOffers = offers.map(Offer_exports.fromSnakeCase);
2071
- return {
2072
- cursor: returnedCursor,
2073
- offers: routerOffers
2074
- };
2075
- }
2076
- async function getObligations(config, parameters) {
2077
- const url = new URL(`${config.url.toString()}v1/obligations`);
2078
- if (parameters?.cursor !== void 0) {
2079
- url.searchParams.set("cursor", parameters.cursor);
2080
- }
2081
- if (parameters?.limit !== void 0) {
2082
- url.searchParams.set("limit", parameters.limit.toString());
2083
- }
2084
- const { cursor: returnedCursor, data: items } = await getApi(config, url);
2085
- const obligations = items.map((item) => {
2086
- const obligation = Obligation_exports.fromSnakeCase(item);
2087
- const { obligationId: _, ...returned } = {
2088
- id: () => Obligation_exports.id(obligation),
2089
- ...obligation,
2090
- ...Quote_exports.fromSnakeCase({ obligation_id: item.id, ask: item.ask, bid: item.bid })
2091
- };
2092
- return returned;
2093
- });
2258
+ ...signature !== null ? { signature: item.signature } : void 0
2259
+ });
2260
+ }) ?? [];
2094
2261
  return {
2095
- cursor: returnedCursor,
2096
- obligations
2262
+ cursor: data?.cursor ?? null,
2263
+ offers
2097
2264
  };
2098
2265
  }
2099
- async function getApi(config, url) {
2100
- const pathname = url.pathname;
2101
- let action;
2102
- switch (true) {
2103
- case pathname.includes("/v1/offers"):
2104
- action = "get_offers";
2105
- break;
2106
- case pathname.includes("/v1/obligations"):
2107
- action = "get_obligations";
2108
- break;
2109
- default:
2110
- throw new HttpGetApiFailedError("Unknown endpoint", {
2111
- details: `Unsupported path: ${pathname}`
2112
- });
2113
- }
2114
- const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
2115
- if (!schemaParseResult.success) {
2116
- throw new HttpGetApiFailedError(`Invalid URL parameters`, {
2117
- details: schemaParseResult.error.issues[0]?.message
2118
- });
2119
- }
2120
- const response = await fetch(url.toString(), {
2121
- method: "GET",
2122
- headers: config.headers
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
+ }
2123
2274
  });
2124
- if (!response.ok) {
2275
+ if (error !== void 0) {
2125
2276
  switch (response.status) {
2126
2277
  case 401:
2127
2278
  throw new HttpUnauthorizedError();
@@ -2131,10 +2282,31 @@ async function getApi(config, url) {
2131
2282
  throw new HttpRateLimitError();
2132
2283
  }
2133
2284
  throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
2134
- details: await response.text()
2285
+ details: JSON.stringify(error)
2135
2286
  });
2136
2287
  }
2137
- return response.json();
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
+ };
2138
2310
  }
2139
2311
  var InvalidUrlError = class extends BaseError {
2140
2312
  constructor(url) {
@@ -2177,13 +2349,21 @@ var HttpGetApiFailedError = class extends BaseError {
2177
2349
  }
2178
2350
  };
2179
2351
 
2180
- // src/collectors/validations/Validation.ts
2181
- var Validation_exports = {};
2182
- __export(Validation_exports, {
2183
- run: () => run
2352
+ // src/gatekeeper/Gate.ts
2353
+ var Gate_exports = {};
2354
+ __export(Gate_exports, {
2355
+ batch: () => batch2,
2356
+ run: () => run,
2357
+ single: () => single
2184
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
+ }
2185
2365
  async function run(parameters) {
2186
- const { items, rules, ctx = {}, chunkSize } = parameters;
2366
+ const { items, rules, chunkSize } = parameters;
2187
2367
  const issues = [];
2188
2368
  let validItems = items.slice();
2189
2369
  for (const rule of rules) {
@@ -2192,7 +2372,7 @@ async function run(parameters) {
2192
2372
  if (rule.kind === "single") {
2193
2373
  for (let i = 0; i < validItems.length; i++) {
2194
2374
  const item = validItems[i];
2195
- const issue = await rule.run(item, ctx);
2375
+ const issue = await rule.run(item);
2196
2376
  if (issue) {
2197
2377
  issues.push({ ...issue, ruleName: rule.name, item });
2198
2378
  indicesToRemove.add(i);
@@ -2200,7 +2380,7 @@ async function run(parameters) {
2200
2380
  }
2201
2381
  } else if (rule.kind === "batch") {
2202
2382
  const exec = async (slice, offset) => {
2203
- const map = await rule.run(slice, ctx);
2383
+ const map = await rule.run(slice);
2204
2384
  for (let i = 0; i < slice.length; i++) {
2205
2385
  const issue = map.get(i);
2206
2386
  if (issue !== void 0) {
@@ -2224,157 +2404,265 @@ async function run(parameters) {
2224
2404
  };
2225
2405
  }
2226
2406
 
2227
- // src/collectors/validations/ValidationRule.ts
2228
- var ValidationRule_exports = {};
2229
- __export(ValidationRule_exports, {
2230
- batch: () => batch2,
2231
- morpho: () => morpho,
2232
- 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
2233
2416
  });
2234
- function single(name, run2) {
2235
- return { kind: "single", name, run: run2 };
2417
+ function getCallback(chain2, type) {
2418
+ return configs[chain2].callbacks?.find((c) => c.type === type);
2236
2419
  }
2237
- function batch2(name, run2) {
2238
- 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;
2239
2424
  }
2240
- function morpho() {
2241
- const chainId = single("chain_id", (offer, { chain }) => {
2242
- if (chain.id !== offer.chainId) {
2243
- return {
2244
- message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
2245
- };
2246
- }
2247
- });
2248
- const loanToken = single("loan_token", (offer, { chain }) => {
2249
- const tokens = new Set(
2250
- Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
2251
- );
2252
- if (!tokens.has(offer.loanToken.toLowerCase())) {
2253
- return {
2254
- message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
2255
- };
2256
- }
2257
- });
2258
- const expiry = single("expiry", (offer, _) => {
2259
- if (offer.expiry < Math.floor(Date.now() / 1e3)) {
2260
- return { message: "Expiry mismatch" };
2261
- }
2262
- });
2263
- const sellEmptyCallback = single(
2264
- "sell_offers_empty_callback",
2265
- (offer, _) => {
2266
- if (!offer.buy && offer.callback.data === "0x") {
2267
- return { message: "Sell offers require a non-empty callback." };
2268
- }
2269
- }
2270
- );
2271
- const buyNonEmptyCallback = single(
2272
- "buy_offers_non_empty_callback",
2273
- (offer, _) => {
2274
- if (offer.buy && offer.callback.data !== "0x") {
2275
- const allowed = new Set(
2276
- Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.BuyVaultV1Callback].map(
2277
- (a) => a.toLowerCase()
2278
- )
2279
- );
2280
- const callbackAddress = offer.callback.address?.toLowerCase();
2281
- if (!callbackAddress || !allowed.has(callbackAddress)) {
2282
- return {
2283
- message: "Buy offers with non-empty callback must use a whitelisted BuyVaultV1Callback."
2284
- };
2285
- }
2286
- }
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
+ });
2287
2596
  }
2288
- );
2289
- const sellNonWhitelistedCallback = single(
2290
- "sell_offers_non_whitelisted_callback",
2291
- (offer, _) => {
2292
- if (!offer.buy && offer.callback.data !== "0x") {
2293
- const allowed = new Set(
2294
- Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.SellERC20Callback].map(
2295
- (a) => a.toLowerCase()
2296
- )
2297
- );
2298
- const callbackAddress = offer.callback.address?.toLowerCase();
2299
- if (!callbackAddress || !allowed.has(callbackAddress)) {
2300
- return { message: "Sell offer callback address is not whitelisted." };
2301
- }
2302
- }
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;
2303
2615
  }
2304
- );
2305
- const sellCallbackDataInvalid = single(
2306
- "sell_offers_callback_data_invalid",
2307
- (offer, _) => {
2308
- if (!offer.buy && offer.callback.data !== "0x") {
2309
- try {
2310
- const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
2311
- if (decoded.length === 0) {
2312
- return { message: "Sell offer callback data must include at least one collateral." };
2313
- }
2314
- } catch (_2) {
2315
- return { message: "Sell offer callback data cannot be decoded." };
2316
- }
2317
- }
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." };
2318
2619
  }
2319
- );
2320
- const sellCallbackCollateralInvalid = single(
2321
- "sell_offers_callback_collateral_invalid",
2322
- (offer, _) => {
2323
- if (!offer.buy && offer.callback.data !== "0x") {
2324
- try {
2325
- const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
2326
- const offerCollaterals = new Set(
2327
- offer.collaterals.map((c) => c.asset.toLowerCase())
2328
- );
2329
- for (const { collateral } of decoded) {
2330
- if (!offerCollaterals.has(collateral.toLowerCase())) {
2331
- return { message: "Sell callback collateral is not part of offer collaterals." };
2332
- }
2333
- }
2334
- } catch (_2) {
2335
- }
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
+ };
2336
2628
  }
2337
- }
2338
- );
2339
- const buyCallbackDataInvalid = single(
2340
- "buy_offers_callback_data_invalid",
2341
- (offer, _) => {
2342
- if (offer.buy && offer.callback.data !== "0x") {
2343
- try {
2344
- const decoded = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2345
- if (decoded.length === 0) {
2346
- return { message: "Buy offer callback data must include at least one vault." };
2347
- }
2348
- } catch (_2) {
2349
- 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." };
2350
2632
  }
2351
2633
  }
2352
2634
  }
2353
- );
2635
+ });
2354
2636
  const buyCallbackVaultInvalid = batch2(
2355
2637
  "buy_offers_callback_vault_invalid",
2356
- async (offers, { client, chain }) => {
2638
+ async (offers) => {
2357
2639
  const validationIssues = /* @__PURE__ */ new Map();
2358
2640
  const offersByVaultAddress = /* @__PURE__ */ new Map();
2359
2641
  for (let i = 0; i < offers.length; i++) {
2360
2642
  const offer = offers[i];
2361
- if (offer.buy && offer.callback.data !== "0x") {
2362
- try {
2363
- const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2364
- for (const { vault } of callbackVaults) {
2365
- const normalizedVaultAddress = vault.toLowerCase();
2366
- if (!offersByVaultAddress.has(normalizedVaultAddress)) {
2367
- offersByVaultAddress.set(normalizedVaultAddress, []);
2368
- }
2369
- 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, []);
2370
2653
  }
2371
- } catch (_) {
2654
+ offersByVaultAddress.get(normalizedVaultAddress).push({ index: i, offer });
2372
2655
  }
2656
+ } catch (_) {
2373
2657
  }
2374
2658
  }
2375
2659
  const uniqueVaultAddresses = Array.from(offersByVaultAddress.keys());
2376
2660
  if (uniqueVaultAddresses.length === 0) return validationIssues;
2377
- 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;
2378
2666
  const multicallContracts = [];
2379
2667
  for (const vaultAddress of uniqueVaultAddresses) {
2380
2668
  multicallContracts.push({
@@ -2382,7 +2670,7 @@ function morpho() {
2382
2670
  abi: Abi_exports.ERC4626,
2383
2671
  functionName: "asset"
2384
2672
  });
2385
- for (const factoryAddress of whitelistedFactories) {
2673
+ for (const factoryAddress of allowedFactories) {
2386
2674
  multicallContracts.push({
2387
2675
  address: factoryAddress,
2388
2676
  abi: Abi_exports.MetaMorphoFactory,
@@ -2397,7 +2685,7 @@ function morpho() {
2397
2685
  });
2398
2686
  const vaultAssetByAddress = /* @__PURE__ */ new Map();
2399
2687
  const registeredVaults = /* @__PURE__ */ new Set();
2400
- const numberOfFactories = whitelistedFactories.length;
2688
+ const numberOfFactories = allowedFactories.length;
2401
2689
  let resultIndex = 0;
2402
2690
  for (const vaultAddress of uniqueVaultAddresses) {
2403
2691
  const assetCallResult = multicallResults[resultIndex++];
@@ -2456,30 +2744,61 @@ function morpho() {
2456
2744
  return validationIssues;
2457
2745
  }
2458
2746
  );
2459
- const maturity = single("maturity", (offer, _) => {
2460
- const allowedMaturities = [Maturity_exports.from("end_of_month"), Maturity_exports.from("end_of_next_month")];
2461
- if (!allowedMaturities.includes(offer.maturity)) {
2462
- return {
2463
- message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
2464
- };
2747
+ const expiry = single("expiry", (offer) => {
2748
+ if (offer.expiry < Math.floor(Date.now() / 1e3)) {
2749
+ return { message: "Expiry mismatch" };
2465
2750
  }
2466
2751
  });
2467
- return [
2468
- chainId,
2469
- loanToken,
2470
- expiry,
2471
- maturity,
2472
- // note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
2473
- // integrators should be able to choose if they want to keep the offer or not
2474
- sellEmptyCallback,
2475
- buyNonEmptyCallback,
2476
- sellNonWhitelistedCallback,
2477
- sellCallbackDataInvalid,
2478
- sellCallbackCollateralInvalid,
2479
- buyCallbackDataInvalid,
2480
- buyCallbackVaultInvalid
2481
- ];
2752
+ return [expiry, sellErc20CallbackInvalid, buyCallbackVaultInvalid];
2482
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
+ });
2483
2802
 
2484
2803
  // src/mempool/MempoolClient.ts
2485
2804
  var MempoolClient_exports = {};
@@ -2488,7 +2807,7 @@ __export(MempoolClient_exports, {
2488
2807
  });
2489
2808
  var DEFAULT_BATCH_SIZE2 = 100;
2490
2809
  var DEFAULT_BLOCK_WINDOW2 = 100;
2491
- function from9(parameters) {
2810
+ function from10(parameters) {
2492
2811
  const config = {
2493
2812
  client: parameters.client,
2494
2813
  mempoolAddress: parameters.mempoolAddress,
@@ -2497,24 +2816,24 @@ function from9(parameters) {
2497
2816
  return {
2498
2817
  add: (parameters2) => add(config, parameters2),
2499
2818
  get: (parameters2) => get(config, parameters2),
2500
- watch: (parameters2) => watch(config, parameters2),
2501
2819
  stream: (parameters2) => streamOffers(config, parameters2)
2502
2820
  };
2503
2821
  }
2504
- async function add(config, parameters) {
2505
- const offer = Offer_exports.from(parameters.offer);
2822
+ async function add(config, offers) {
2506
2823
  if (!config.client.account) throw new WalletAccountNotSetError();
2824
+ const tree = Tree_exports.from(offers.map((o) => Offer_exports.from(o)));
2507
2825
  const chainId = await getChainId(config.client);
2508
- if (BigInt(chainId) !== offer.chainId)
2509
- 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
+ }
2510
2830
  try {
2511
- const tx = await config.client.sendTransaction({
2831
+ return await config.client.sendTransaction({
2512
2832
  chain: config.client.chain,
2513
2833
  account: config.client.account,
2514
2834
  to: config.mempoolAddress,
2515
- data: Offer_exports.encode(offer)
2835
+ data: Tree_exports.encode(tree)
2516
2836
  });
2517
- return { offer, txHash: tx };
2518
2837
  } catch (error) {
2519
2838
  throw new ViemClientError(error instanceof Error ? error.message : "Unknown error");
2520
2839
  }
@@ -2524,40 +2843,17 @@ async function* get(config, parameters) {
2524
2843
  loanToken,
2525
2844
  blockNumberGte,
2526
2845
  blockNumberLte,
2527
- order = "desc",
2846
+ order: order2 = "desc",
2528
2847
  options: { maxBatchSize = DEFAULT_BATCH_SIZE2 } = {}
2529
2848
  } = parameters || {};
2530
2849
  yield* streamOffers(config, {
2531
2850
  loanToken,
2532
- order,
2851
+ order: order2,
2533
2852
  blockNumberGte,
2534
2853
  blockNumberLte,
2535
2854
  options: { maxBatchSize, blockWindow: config.blockWindow }
2536
2855
  });
2537
2856
  }
2538
- function watch(config, parameters) {
2539
- const {
2540
- loanToken,
2541
- lastSyncedBlock,
2542
- polling: { interval = 3e4, maxBatchSize = DEFAULT_BATCH_SIZE2 } = {},
2543
- onOffers
2544
- } = parameters;
2545
- return poll(
2546
- async () => {
2547
- const blockNumberGte = await lastSyncedBlock();
2548
- const stream = streamOffers(config, {
2549
- loanToken,
2550
- order: "asc",
2551
- blockNumberGte,
2552
- options: { maxBatchSize, blockWindow: config.blockWindow }
2553
- });
2554
- for await (const { offers, blockNumber } of stream) {
2555
- await onOffers(offers, blockNumber);
2556
- }
2557
- },
2558
- { interval }
2559
- );
2560
- }
2561
2857
  var chainIdCache = /* @__PURE__ */ new Map();
2562
2858
  var getChainId = async (client) => {
2563
2859
  if (chainIdCache.has(client.uid)) return chainIdCache.get(client.uid);
@@ -2570,7 +2866,7 @@ async function* streamOffers(config, parameters) {
2570
2866
  loanToken,
2571
2867
  blockNumberGte,
2572
2868
  blockNumberLte,
2573
- order = "desc",
2869
+ order: order2 = "desc",
2574
2870
  options: { maxBatchSize = DEFAULT_BATCH_SIZE2, blockWindow = config.blockWindow } = {}
2575
2871
  } = parameters;
2576
2872
  const stream = Chain_exports.streamLogs({
@@ -2584,28 +2880,29 @@ async function* streamOffers(config, parameters) {
2584
2880
  },
2585
2881
  blockNumberGte,
2586
2882
  blockNumberLte,
2587
- order,
2883
+ order: order2,
2588
2884
  options: { maxBatchSize, blockWindow }
2589
2885
  });
2590
- let blockNumber = order === "asc" ? blockNumberGte : blockNumberLte;
2886
+ let blockNumber = order2 === "asc" ? blockNumberGte : blockNumberLte;
2591
2887
  for await (const { logs, blockNumber: newBlockNumber } of stream) {
2592
2888
  blockNumber = newBlockNumber;
2593
2889
  if (logs.length === 0) break;
2594
- let offersAndBlockNumbers = logs.map((log) => {
2890
+ const offers = [];
2891
+ for (const log of logs) {
2892
+ if (!log) continue;
2595
2893
  const [payload] = decodeAbiParameters([{ type: "bytes" }], log.data);
2596
2894
  try {
2597
- 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
+ }
2598
2900
  } catch (_) {
2599
- return null;
2600
2901
  }
2601
- }).filter((item) => item !== null);
2602
- if (loanToken)
2603
- offersAndBlockNumbers = offersAndBlockNumbers.filter(
2604
- (o) => o.offer.loanToken.toLowerCase() === loanToken.toLowerCase()
2605
- );
2606
- if (offersAndBlockNumbers.length === 0) continue;
2902
+ }
2903
+ if (offers.length === 0) continue;
2607
2904
  yield {
2608
- offers: offersAndBlockNumbers.map((item) => item.offer),
2905
+ offers,
2609
2906
  blockNumber
2610
2907
  };
2611
2908
  }
@@ -2633,9 +2930,129 @@ var ChainIdMismatchError = class extends BaseError {
2633
2930
 
2634
2931
  // src/mempool/MempoolClient.ts
2635
2932
  function connect2(parameters) {
2636
- 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;
2637
3054
  }
2638
3055
 
2639
- 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 };
2640
3057
  //# sourceMappingURL=index.browser.mjs.map
2641
3058
  //# sourceMappingURL=index.browser.mjs.map