@morpho-dev/router 0.1.12 → 0.1.16

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.
@@ -1,10 +1,33 @@
1
1
  'use strict';
2
2
 
3
- var mempool = require('@morpho-dev/mempool');
4
- var viem = require('viem');
5
3
  var v4 = require('zod/v4');
6
- var zodOpenapi = require('zod-openapi');
4
+ var viem = require('viem');
5
+ var actions = require('viem/actions');
6
+ var chains$1 = require('viem/chains');
7
+ var z7 = require('zod');
7
8
  var jsBase64 = require('js-base64');
9
+ var accounts = require('viem/accounts');
10
+ var zodOpenapi = require('zod-openapi');
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var z7__namespace = /*#__PURE__*/_interopNamespace(z7);
8
31
 
9
32
  var __defProp = Object.defineProperty;
10
33
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -12,117 +35,754 @@ var __export = (target, all) => {
12
35
  for (var name in all)
13
36
  __defProp(target, name, { get: all[name], enumerable: true });
14
37
  };
15
- var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
38
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
16
39
 
17
- // src/api/Client.ts
18
- var Client_exports = {};
19
- __export(Client_exports, {
20
- HttpForbiddenError: () => HttpForbiddenError,
21
- HttpGetOffersFailedError: () => HttpGetOffersFailedError,
22
- HttpRateLimitError: () => HttpRateLimitError,
23
- HttpUnauthorizedError: () => HttpUnauthorizedError,
24
- InvalidUrlError: () => InvalidUrlError,
25
- connect: () => connect,
26
- get: () => get,
27
- match: () => match
40
+ // src/api/Api/Schema/index.ts
41
+ var Schema_exports = {};
42
+ __export(Schema_exports, {
43
+ ChainHealth: () => ChainHealth,
44
+ ChainsHealthResponse: () => ChainsHealthResponse,
45
+ CollectorHealth: () => CollectorHealth,
46
+ CollectorsHealthResponse: () => CollectorsHealthResponse,
47
+ ObligationResponse: () => ObligationResponse_exports,
48
+ OfferResponse: () => OfferResponse_exports,
49
+ OpenApi: () => OpenApi,
50
+ RouterStatusResponse: () => RouterStatusResponse,
51
+ parse: () => parse,
52
+ safeParse: () => safeParse
53
+ });
54
+ var CollectorHealth = v4.z.object({
55
+ name: v4.z.string(),
56
+ chain_id: v4.z.number(),
57
+ block_number: v4.z.number().nullable(),
58
+ updated_at: v4.z.string().nullable(),
59
+ lag: v4.z.number().nullable(),
60
+ status: v4.z.enum(["live", "lagging", "unknown"])
61
+ });
62
+ var CollectorsHealthResponse = v4.z.object({
63
+ collectors: v4.z.array(CollectorHealth)
64
+ });
65
+ var ChainHealth = v4.z.object({
66
+ chain_id: v4.z.number(),
67
+ block_number: v4.z.number(),
68
+ updated_at: v4.z.string()
69
+ });
70
+ var ChainsHealthResponse = v4.z.object({
71
+ chains: v4.z.array(ChainHealth)
72
+ });
73
+ var RouterStatusResponse = v4.z.object({
74
+ status: v4.z.enum(["live", "syncing"])
28
75
  });
29
76
 
30
- // src/core/RouterOffer.ts
31
- var RouterOffer_exports = {};
32
- __export(RouterOffer_exports, {
33
- InvalidRouterOfferError: () => InvalidRouterOfferError,
34
- OfferStatusValues: () => OfferStatusValues,
35
- RouterOfferSchema: () => RouterOfferSchema,
36
- consumedEvent: () => consumedEvent,
37
- from: () => from,
38
- fromConsumedLog: () => fromConsumedLog,
39
- fromSnakeCase: () => fromSnakeCase,
40
- random: () => random,
41
- toSnakeCase: () => toSnakeCase
77
+ // src/api/Api/Schema/ObligationResponse.ts
78
+ var ObligationResponse_exports = {};
79
+ __export(ObligationResponse_exports, {
80
+ from: () => from6
42
81
  });
43
- var OfferStatusValues = [
44
- "valid",
45
- "callback_not_supported",
46
- "callback_error",
47
- "unverified"
82
+
83
+ // src/core/Abi.ts
84
+ var Abi_exports = {};
85
+ __export(Abi_exports, {
86
+ ERC4626: () => ERC4626,
87
+ MetaMorpho: () => MetaMorpho,
88
+ MetaMorphoFactory: () => MetaMorphoFactory,
89
+ Morpho: () => Morpho,
90
+ Oracle: () => Oracle
91
+ });
92
+ var Oracle = [
93
+ {
94
+ type: "function",
95
+ name: "price",
96
+ inputs: [],
97
+ outputs: [{ name: "", type: "uint256" }],
98
+ stateMutability: "view"
99
+ }
100
+ ];
101
+ var ERC4626 = [
102
+ {
103
+ type: "function",
104
+ name: "asset",
105
+ inputs: [],
106
+ outputs: [{ name: "", type: "address" }],
107
+ stateMutability: "view"
108
+ }
109
+ ];
110
+ var MetaMorphoFactory = [
111
+ {
112
+ type: "function",
113
+ name: "isMetaMorpho",
114
+ inputs: [{ name: "target", type: "address" }],
115
+ outputs: [{ name: "", type: "bool" }],
116
+ stateMutability: "view"
117
+ }
118
+ ];
119
+ var MetaMorpho = [
120
+ {
121
+ type: "function",
122
+ name: "withdrawQueue",
123
+ inputs: [{ name: "index", type: "uint256" }],
124
+ outputs: [{ name: "", type: "bytes32" }],
125
+ stateMutability: "view"
126
+ },
127
+ {
128
+ type: "function",
129
+ name: "withdrawQueueLength",
130
+ inputs: [],
131
+ outputs: [{ name: "", type: "uint256" }],
132
+ stateMutability: "view"
133
+ },
134
+ {
135
+ type: "function",
136
+ name: "maxWithdraw",
137
+ inputs: [{ name: "owner", type: "address" }],
138
+ outputs: [{ name: "", type: "uint256" }],
139
+ stateMutability: "view"
140
+ }
141
+ ];
142
+ var Morpho = [
143
+ {
144
+ type: "function",
145
+ name: "collateralOf",
146
+ inputs: [
147
+ {
148
+ name: "",
149
+ type: "address",
150
+ internalType: "address"
151
+ },
152
+ {
153
+ name: "",
154
+ type: "bytes32",
155
+ internalType: "bytes32"
156
+ },
157
+ {
158
+ name: "",
159
+ type: "address",
160
+ internalType: "address"
161
+ }
162
+ ],
163
+ outputs: [
164
+ {
165
+ name: "",
166
+ type: "uint256",
167
+ internalType: "uint256"
168
+ }
169
+ ],
170
+ stateMutability: "view"
171
+ },
172
+ {
173
+ type: "function",
174
+ name: "debtOf",
175
+ inputs: [
176
+ {
177
+ name: "",
178
+ type: "address",
179
+ internalType: "address"
180
+ },
181
+ {
182
+ name: "",
183
+ type: "bytes32",
184
+ internalType: "bytes32"
185
+ }
186
+ ],
187
+ outputs: [
188
+ {
189
+ name: "",
190
+ type: "uint256",
191
+ internalType: "uint256"
192
+ }
193
+ ],
194
+ stateMutability: "view"
195
+ },
196
+ {
197
+ type: "function",
198
+ name: "market",
199
+ inputs: [
200
+ {
201
+ name: "id",
202
+ type: "bytes32",
203
+ internalType: "Id"
204
+ }
205
+ ],
206
+ outputs: [
207
+ {
208
+ name: "totalSupplyAssets",
209
+ type: "uint128",
210
+ internalType: "uint128"
211
+ },
212
+ {
213
+ name: "totalSupplyShares",
214
+ type: "uint128",
215
+ internalType: "uint128"
216
+ },
217
+ {
218
+ name: "totalBorrowAssets",
219
+ type: "uint128",
220
+ internalType: "uint128"
221
+ },
222
+ {
223
+ name: "totalBorrowShares",
224
+ type: "uint128",
225
+ internalType: "uint128"
226
+ },
227
+ {
228
+ name: "lastUpdate",
229
+ type: "uint128",
230
+ internalType: "uint128"
231
+ },
232
+ {
233
+ name: "fee",
234
+ type: "uint128",
235
+ internalType: "uint128"
236
+ }
237
+ ],
238
+ stateMutability: "view"
239
+ },
240
+ {
241
+ type: "function",
242
+ name: "position",
243
+ inputs: [
244
+ {
245
+ name: "id",
246
+ type: "bytes32",
247
+ internalType: "Id"
248
+ },
249
+ {
250
+ name: "user",
251
+ type: "address",
252
+ internalType: "address"
253
+ }
254
+ ],
255
+ outputs: [
256
+ {
257
+ name: "supplyShares",
258
+ type: "uint256",
259
+ internalType: "uint256"
260
+ },
261
+ {
262
+ name: "borrowShares",
263
+ type: "uint128",
264
+ internalType: "uint128"
265
+ },
266
+ {
267
+ name: "collateral",
268
+ type: "uint128",
269
+ internalType: "uint128"
270
+ }
271
+ ],
272
+ stateMutability: "view"
273
+ }
48
274
  ];
49
- var RouterOfferSchema = (parameters) => mempool.Offer.OfferSchema(parameters).extend({
50
- consumed: v4.z.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
51
- status: v4.z.enum(OfferStatusValues),
52
- metadata: v4.z.object({
53
- issue: v4.z.string()
54
- }).optional()
275
+
276
+ // src/core/Callback.ts
277
+ var Callback_exports = {};
278
+ __export(Callback_exports, {
279
+ CallbackType: () => CallbackType,
280
+ WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
281
+ decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
282
+ decodeSellERC20Callback: () => decodeSellERC20Callback,
283
+ encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
284
+ encodeSellERC20Callback: () => encodeSellERC20Callback
55
285
  });
56
- var consumedEvent = {
57
- type: "event",
58
- name: "Consumed",
59
- inputs: [
60
- { name: "user", type: "address", indexed: true, internalType: "address" },
61
- { name: "nonce", type: "uint256", indexed: true, internalType: "uint256" },
62
- { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }
63
- ],
64
- anonymous: false
286
+ var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
287
+ CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
288
+ CallbackType2["BuyVaultV1Callback"] = "buy_vault_v1_callback";
289
+ CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
290
+ return CallbackType2;
291
+ })(CallbackType || {});
292
+ var WhitelistedCallbackAddresses = {
293
+ ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
294
+ ["buy_vault_v1_callback" /* BuyVaultV1Callback */]: [
295
+ "0x3333333333333333333333333333333333333333",
296
+ "0x4444444444444444444444444444444444444444"
297
+ // @TODO: update once deployed and add mapping per chain if needed
298
+ ].map((address) => address.toLowerCase()),
299
+ ["sell_erc20_callback" /* SellERC20Callback */]: [
300
+ "0x1111111111111111111111111111111111111111",
301
+ "0x2222222222222222222222222222222222222222"
302
+ // @TODO: update once deployed and add mapping per chain if needed
303
+ ].map((address) => address.toLowerCase())
65
304
  };
66
- function from(input) {
305
+ function decodeBuyVaultV1Callback(data) {
306
+ if (!data || data === "0x") throw new Error("Empty callback data");
67
307
  try {
68
- const parsedOffer = RouterOfferSchema({ omitHash: true }).parse(input);
69
- const parsedHash = mempool.Offer.OfferHashSchema.parse(mempool.Offer.hash(parsedOffer));
70
- return {
71
- ...parsedOffer,
72
- hash: parsedHash
73
- };
74
- } catch (error) {
75
- throw new InvalidRouterOfferError(error);
308
+ const [vaults, amounts] = viem.decodeAbiParameters(
309
+ [{ type: "address[]" }, { type: "uint256[]" }],
310
+ data
311
+ );
312
+ if (vaults.length !== amounts.length) {
313
+ throw new Error("Mismatched array lengths");
314
+ }
315
+ return vaults.map((v, i) => ({ vault: v, amount: amounts[i] }));
316
+ } catch (_) {
317
+ throw new Error("Invalid BuyVaultV1Callback callback data");
76
318
  }
77
319
  }
78
- function fromSnakeCase(input) {
79
- return from(mempool.Format.fromSnakeCase(input));
320
+ function decodeSellERC20Callback(data) {
321
+ if (!data || data === "0x") throw new Error("Empty callback data");
322
+ try {
323
+ const [collaterals, amounts] = viem.decodeAbiParameters(
324
+ [{ type: "address[]" }, { type: "uint256[]" }],
325
+ data
326
+ );
327
+ if (collaterals.length !== amounts.length) {
328
+ throw new Error("Mismatched array lengths");
329
+ }
330
+ return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
331
+ } catch (_) {
332
+ throw new Error("Invalid SellERC20Callback callback data");
333
+ }
80
334
  }
81
- function toSnakeCase(offer) {
82
- return mempool.Format.toSnakeCase(offer);
335
+ function encodeBuyVaultV1Callback(parameters) {
336
+ return viem.encodeAbiParameters(
337
+ [{ type: "address[]" }, { type: "uint256[]" }],
338
+ [parameters.vaults, parameters.amounts]
339
+ );
83
340
  }
84
- function random() {
85
- const baseOffer = mempool.Offer.random();
86
- return from({
87
- ...baseOffer,
88
- status: "valid",
89
- metadata: void 0,
90
- consumed: 0n
91
- });
341
+ function encodeSellERC20Callback(parameters) {
342
+ return viem.encodeAbiParameters(
343
+ [{ type: "address[]" }, { type: "uint256[]" }],
344
+ [parameters.collaterals, parameters.amounts]
345
+ );
92
346
  }
93
- function fromConsumedLog(parameters) {
94
- const { blockNumber, logIndex, chainId, transactionHash, user, nonce, amount } = parameters;
95
- return {
96
- id: `${blockNumber.toString()}-${logIndex.toString()}-${chainId}-${transactionHash}`,
97
- chainId: BigInt(chainId),
98
- offering: user,
99
- nonce,
100
- amount
101
- };
347
+
348
+ // src/core/Chain.ts
349
+ var Chain_exports = {};
350
+ __export(Chain_exports, {
351
+ ChainId: () => ChainId,
352
+ InvalidBatchSizeError: () => InvalidBatchSizeError,
353
+ InvalidBlockRangeError: () => InvalidBlockRangeError,
354
+ InvalidBlockWindowError: () => InvalidBlockWindowError,
355
+ MissingBlockNumberError: () => MissingBlockNumberError,
356
+ chainIds: () => chainIds,
357
+ chainNames: () => chainNames,
358
+ chains: () => chains,
359
+ getChain: () => getChain,
360
+ getWhitelistedChains: () => getWhitelistedChains,
361
+ streamLogs: () => streamLogs
362
+ });
363
+
364
+ // src/utils/BigMath.ts
365
+ function max(a, b) {
366
+ return a > b ? a : b;
102
367
  }
103
- var InvalidRouterOfferError = class extends mempool.Errors.BaseError {
104
- constructor(error) {
105
- super("Invalid router offer.", { cause: error });
106
- __publicField(this, "name", "RouterOffer.InvalidRouterOfferError");
368
+ function min(a, b) {
369
+ return a < b ? a : b;
370
+ }
371
+
372
+ // src/utils/batch.ts
373
+ function* batch(array2, batchSize) {
374
+ for (let i = 0; i < array2.length; i += batchSize) {
375
+ yield array2.slice(i, i + batchSize);
107
376
  }
108
- };
377
+ }
109
378
 
110
- // src/core/Cursor.ts
111
- var Cursor_exports = {};
112
- __export(Cursor_exports, {
113
- decode: () => decode,
114
- encode: () => encode,
115
- validate: () => validate
379
+ // src/utils/Errors.ts
380
+ var Errors_exports = {};
381
+ __export(Errors_exports, {
382
+ BaseError: () => BaseError
116
383
  });
117
- function validate(cursor) {
118
- if (!cursor || typeof cursor !== "object") {
119
- throw new Error("Cursor must be an object");
120
- }
121
- const c = cursor;
122
- if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
123
- throw new Error(
124
- `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
125
- );
384
+ var BaseError = class _BaseError extends Error {
385
+ constructor(shortMessage, options = {}) {
386
+ const details = (() => {
387
+ if (options.cause instanceof _BaseError) {
388
+ if (options.cause.details) return options.cause.details;
389
+ if (options.cause.shortMessage) return options.cause.shortMessage;
390
+ }
391
+ if (options.cause && "details" in options.cause && typeof options.cause.details === "string")
392
+ return options.cause.details;
393
+ if (options.cause?.message) return options.cause.message;
394
+ return options.details;
395
+ })();
396
+ const message = [
397
+ shortMessage || "An error occurred.",
398
+ ...options.metaMessages ? ["", ...options.metaMessages] : [],
399
+ ...details ? ["", details ? `Details: ${details}` : void 0] : []
400
+ ].filter((x) => typeof x === "string").join("\n");
401
+ super(message, options.cause ? { cause: options.cause } : void 0);
402
+ __publicField(this, "details");
403
+ __publicField(this, "shortMessage");
404
+ __publicField(this, "cause");
405
+ __publicField(this, "name", "BaseError");
406
+ this.cause = options.cause;
407
+ this.details = details;
408
+ this.shortMessage = shortMessage;
409
+ }
410
+ walk(fn) {
411
+ return walk(this, fn);
412
+ }
413
+ };
414
+ function walk(err, fn) {
415
+ if (fn?.(err)) return err;
416
+ if (err && typeof err === "object" && "cause" in err && err.cause) return walk(err.cause, fn);
417
+ return fn ? null : err;
418
+ }
419
+
420
+ // src/core/Chain.ts
421
+ var chainNames = ["ethereum", "base", "ethereum-virtual-testnet", "anvil"];
422
+ var ChainId = {
423
+ ETHEREUM: BigInt(chains$1.mainnet.id),
424
+ BASE: BigInt(chains$1.base.id),
425
+ "ETHEREUM-VIRTUAL-TESTNET": 109111114n,
426
+ ANVIL: 505050505n
427
+ // random id to not clash with other chains
428
+ };
429
+ var chainIds = new Set(Object.values(ChainId));
430
+ var chainNameLookup = new Map(Object.entries(ChainId).map(([key, value]) => [value, key]));
431
+ function getChain(chainId) {
432
+ const chainName = chainNameLookup.get(chainId)?.toLowerCase();
433
+ if (!chainName) {
434
+ return void 0;
435
+ }
436
+ return chains[chainName];
437
+ }
438
+ var getWhitelistedChains = () => {
439
+ return [chains.ethereum, chains.base, chains["ethereum-virtual-testnet"], chains.anvil];
440
+ };
441
+ var chains = {
442
+ ethereum: {
443
+ ...chains$1.mainnet,
444
+ id: ChainId.ETHEREUM,
445
+ name: "ethereum",
446
+ whitelistedAssets: new Set(
447
+ [
448
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
449
+ // USDC
450
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F"
451
+ // DAI
452
+ ].map((address) => address.toLowerCase())
453
+ ),
454
+ morpho: "0x0000000000000000000000000000000000000000",
455
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
456
+ mempool: {
457
+ address: "0x0000000000000000000000000000000000000000",
458
+ deploymentBlock: 23347674,
459
+ reindexBuffer: 10
460
+ },
461
+ vaultV1Factory: {
462
+ "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
463
+ "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
464
+ }
465
+ },
466
+ base: {
467
+ ...chains$1.base,
468
+ id: ChainId.BASE,
469
+ name: "base",
470
+ whitelistedAssets: new Set(
471
+ [
472
+ "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
473
+ // USDC
474
+ "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
475
+ // DAI
476
+ ].map((address) => address.toLowerCase())
477
+ ),
478
+ morpho: "0x0000000000000000000000000000000000000000",
479
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
480
+ mempool: {
481
+ address: "0x0000000000000000000000000000000000000000",
482
+ deploymentBlock: 35449942,
483
+ reindexBuffer: 10
484
+ },
485
+ vaultV1Factory: {
486
+ "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
487
+ "v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
488
+ }
489
+ },
490
+ "ethereum-virtual-testnet": {
491
+ ...chains$1.mainnet,
492
+ id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
493
+ name: "ethereum-virtual-testnet",
494
+ whitelistedAssets: new Set(
495
+ [
496
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
497
+ // USDC
498
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F"
499
+ // DAI
500
+ ].map((address) => address.toLowerCase())
501
+ ),
502
+ morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
503
+ // @TODO: This is mock Consumed contract, update with Terms once stable
504
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
505
+ mempool: {
506
+ address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
507
+ deploymentBlock: 23224302,
508
+ reindexBuffer: 10
509
+ },
510
+ vaultV1Factory: {
511
+ "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
512
+ "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
513
+ }
514
+ },
515
+ anvil: {
516
+ ...chains$1.anvil,
517
+ id: ChainId.ANVIL,
518
+ name: "anvil",
519
+ whitelistedAssets: new Set(
520
+ [
521
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
522
+ // USDC
523
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F"
524
+ // DAI
525
+ ].map((address) => address.toLowerCase())
526
+ ),
527
+ morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
528
+ morphoBlue: "0x0000000000000000000000000000000000000000",
529
+ // Set dynamically in tests
530
+ mempool: {
531
+ address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
532
+ deploymentBlock: 23223727,
533
+ reindexBuffer: 10
534
+ },
535
+ vaultV1Factory: {
536
+ "v1.0": "0x0000000000000000000000000000000000000000",
537
+ "v1.1": "0x0000000000000000000000000000000000000000"
538
+ }
539
+ }
540
+ };
541
+ var MAX_BATCH_SIZE = 1e4;
542
+ var DEFAULT_BATCH_SIZE = 2500;
543
+ var MAX_BLOCK_WINDOW = 2e3;
544
+ var DEFAULT_BLOCK_WINDOW = 500;
545
+ async function* streamLogs(parameters) {
546
+ const {
547
+ client,
548
+ contractAddress,
549
+ event,
550
+ blockNumberGte,
551
+ blockNumberLte,
552
+ order = "desc",
553
+ options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
554
+ } = parameters;
555
+ if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
556
+ if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
557
+ if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
558
+ const latestBlock = (await actions.getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
559
+ let toBlock = 0n;
560
+ if (order === "asc")
561
+ toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
562
+ if (order === "desc")
563
+ toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
564
+ let fromBlock = 0n;
565
+ if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
566
+ if (order === "desc")
567
+ fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
568
+ if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
569
+ if (order === "desc") fromBlock = max(fromBlock, toBlock - BigInt(blockWindow));
570
+ if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
571
+ let streaming = true;
572
+ while (streaming) {
573
+ const logs = await actions.getLogs(client, {
574
+ address: contractAddress,
575
+ event,
576
+ fromBlock,
577
+ toBlock
578
+ });
579
+ logs.sort((a, b) => {
580
+ if (a.blockNumber !== b.blockNumber)
581
+ return order === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
582
+ if (a.transactionIndex !== b.transactionIndex)
583
+ return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
584
+ return order === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
585
+ });
586
+ for (const logBatch of batch(logs, maxBatchSize)) {
587
+ if (logBatch.length === 0) break;
588
+ yield {
589
+ logs: logBatch,
590
+ blockNumber: logBatch.length === maxBatchSize ? (
591
+ // if the batch is full, return the last block number, block numbers are always sorted
592
+ Number(logBatch[logBatch.length - 1]?.blockNumber)
593
+ ) : (
594
+ // if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
595
+ order === "asc" ? Number(toBlock) : Number(fromBlock)
596
+ )
597
+ };
598
+ }
599
+ streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
600
+ if (order === "asc") {
601
+ fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
602
+ toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
603
+ }
604
+ if (order === "desc") {
605
+ const lowerBound = BigInt(blockNumberGte || 0);
606
+ const windowSize = BigInt(blockWindow);
607
+ const nextToBlock = max(fromBlock - 1n, lowerBound);
608
+ const nextFromBlock = max(nextToBlock - windowSize, lowerBound);
609
+ toBlock = nextToBlock;
610
+ fromBlock = nextFromBlock;
611
+ }
612
+ }
613
+ yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
614
+ return;
615
+ }
616
+ var InvalidBlockRangeError = class extends BaseError {
617
+ constructor(fromBlock, toBlock) {
618
+ super(
619
+ `Invalid block range while streaming data from chain. From block ${fromBlock} to block ${toBlock}.`
620
+ );
621
+ __publicField(this, "name", "Chain.InvalidBlockRangeError");
622
+ }
623
+ };
624
+ var InvalidBlockWindowError = class extends BaseError {
625
+ constructor(blockWindow) {
626
+ super(
627
+ `Invalid block window while streaming data from chain. Maximum is ${MAX_BLOCK_WINDOW}. Got ${blockWindow}.`
628
+ );
629
+ __publicField(this, "name", "Chain.InvalidBlockWindowError");
630
+ }
631
+ };
632
+ var InvalidBatchSizeError = class extends BaseError {
633
+ constructor(maxBatchSize) {
634
+ super(
635
+ `Invalid batch size while streaming data from chain. Maximum is ${MAX_BATCH_SIZE}. Got ${maxBatchSize}.`
636
+ );
637
+ __publicField(this, "name", "Chain.InvalidBatchSizeError");
638
+ }
639
+ };
640
+ var MissingBlockNumberError = class extends BaseError {
641
+ constructor() {
642
+ super("Missing block number when streaming data from chain in ascending order.");
643
+ __publicField(this, "name", "Chain.MissingBlockNumberError");
644
+ }
645
+ };
646
+
647
+ // src/core/Collateral.ts
648
+ var Collateral_exports = {};
649
+ __export(Collateral_exports, {
650
+ CollateralSchema: () => CollateralSchema,
651
+ CollateralsSchema: () => CollateralsSchema,
652
+ from: () => from2
653
+ });
654
+ var transformHex = (val, ctx) => {
655
+ if (viem.isHex(val)) return val;
656
+ ctx.addIssue({
657
+ code: "invalid_format",
658
+ input: val,
659
+ format: "hex",
660
+ error: "not a hex"
661
+ });
662
+ return z7__namespace.NEVER;
663
+ };
664
+ var transformAddress = (val, ctx) => {
665
+ if (viem.isAddress(val.toLowerCase())) return val.toLowerCase();
666
+ ctx.addIssue({
667
+ code: "invalid_format",
668
+ input: val,
669
+ format: "address",
670
+ error: "not a valid address"
671
+ });
672
+ return z7__namespace.NEVER;
673
+ };
674
+
675
+ // src/core/LLTV.ts
676
+ var LLTV_exports = {};
677
+ __export(LLTV_exports, {
678
+ InvalidLLTVError: () => InvalidLLTVError,
679
+ InvalidOptionError: () => InvalidOptionError,
680
+ LLTVSchema: () => LLTVSchema,
681
+ Options: () => Options,
682
+ from: () => from
683
+ });
684
+ var Options = [0.385, 0.5, 0.625, 0.77, 0.86, 0.915, 0.945, 0.965, 0.98];
685
+ var LLTV_SCALED = Options.map((lltv) => BigInt(lltv * 10 ** 18));
686
+ function from(lltv) {
687
+ if (typeof lltv === "bigint" && !LLTV_SCALED.includes(lltv)) throw new InvalidLLTVError(lltv);
688
+ if (typeof lltv === "bigint") return lltv;
689
+ if (typeof lltv === "number" && !Options.includes(lltv)) throw new InvalidOptionError(lltv);
690
+ return BigInt(lltv * 10 ** 18);
691
+ }
692
+ var InvalidOptionError = class extends BaseError {
693
+ constructor(input) {
694
+ super(
695
+ `Invalid LLTV option. Input: "${input}". Accepted values are: ${Options.map(
696
+ (option) => `"${option}"`
697
+ ).join(", ")}.`
698
+ );
699
+ __publicField(this, "name", "LLTV.InvalidOptionError");
700
+ }
701
+ };
702
+ var InvalidLLTVError = class extends BaseError {
703
+ constructor(input) {
704
+ super(
705
+ `Invalid LLTV. Input: "${input}". Accepted values are: ${LLTV_SCALED.map(
706
+ (option) => `"${option}"`
707
+ ).join(", ")}.`
708
+ );
709
+ __publicField(this, "name", "LLTV.InvalidLLTVError");
710
+ }
711
+ };
712
+ var LLTVSchema = z7__namespace.bigint({ coerce: true }).refine(
713
+ (lltv) => {
714
+ try {
715
+ from(lltv);
716
+ return true;
717
+ } catch (_) {
718
+ return false;
719
+ }
720
+ },
721
+ {
722
+ error: () => {
723
+ return "Invalid LLTV: must be one of 0.385, 0.625, 0.77, 0.86, 0.915, 0.945, 0.965 or 0.98 (scaled by 1e18)";
724
+ }
725
+ }
726
+ ).transform((lltv) => from(lltv));
727
+
728
+ // src/core/Collateral.ts
729
+ var CollateralSchema = z7__namespace.object({
730
+ asset: z7__namespace.string().transform(transformAddress),
731
+ oracle: z7__namespace.string().transform(transformAddress),
732
+ lltv: LLTVSchema
733
+ });
734
+ var CollateralsSchema = z7__namespace.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine(
735
+ (collaterals) => {
736
+ for (let i = 1; i < collaterals.length; i++) {
737
+ if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
738
+ return false;
739
+ }
740
+ }
741
+ return true;
742
+ },
743
+ {
744
+ message: "Collaterals must be sorted alphabetically by address"
745
+ }
746
+ ).refine(
747
+ (collaterals) => {
748
+ const uniqueAssets = /* @__PURE__ */ new Set();
749
+ for (const collateral of collaterals) {
750
+ const assetAddress = collateral.asset.toLowerCase();
751
+ if (uniqueAssets.has(assetAddress)) {
752
+ return false;
753
+ }
754
+ uniqueAssets.add(assetAddress);
755
+ }
756
+ return true;
757
+ },
758
+ {
759
+ message: "Collaterals must not contain duplicate assets"
760
+ }
761
+ );
762
+ var from2 = (parameters) => {
763
+ return {
764
+ asset: parameters.asset.toLowerCase(),
765
+ lltv: from(parameters.lltv),
766
+ oracle: parameters.oracle.toLowerCase()
767
+ };
768
+ };
769
+
770
+ // src/core/Cursor.ts
771
+ var Cursor_exports = {};
772
+ __export(Cursor_exports, {
773
+ decode: () => decode,
774
+ encode: () => encode,
775
+ validate: () => validate
776
+ });
777
+ function validate(cursor) {
778
+ if (!cursor || typeof cursor !== "object") {
779
+ throw new Error("Cursor must be an object");
780
+ }
781
+ const c = cursor;
782
+ if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
783
+ throw new Error(
784
+ `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
785
+ );
126
786
  }
127
787
  if (!["asc", "desc"].includes(c.dir)) {
128
788
  throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
@@ -181,6 +841,11 @@ function validate(cursor) {
181
841
  `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
182
842
  );
183
843
  }
844
+ if (c.page !== void 0) {
845
+ if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
846
+ throw new Error("Invalid page: must be a positive integer");
847
+ }
848
+ }
184
849
  return true;
185
850
  }
186
851
  function encode(c) {
@@ -193,397 +858,839 @@ function decode(token) {
193
858
  return decoded;
194
859
  }
195
860
 
196
- // src/api/Schema/requests.ts
197
- var MAX_LIMIT = 100;
198
- var DEFAULT_LIMIT = 20;
199
- var MAX_LLTV = 100;
200
- var MIN_LLTV = 0;
201
- var GetOffersQueryParams = v4.z.object({
202
- // Core filtering parameters
203
- creators: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
204
- message: "Creators must be comma-separated Ethereum addresses"
205
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
206
- description: "Filter by multiple creator addresses (comma-separated)",
207
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
208
- }),
209
- side: v4.z.enum(["buy", "sell"]).optional().meta({
210
- description: "Filter by offer type: buy offers or sell offers",
211
- example: "buy"
212
- }),
213
- chains: v4.z.string().regex(/^\d+(,\d+)*$/, {
214
- message: "Chains must be comma-separated chain IDs"
215
- }).transform((val) => val.split(",").map(Number)).optional().meta({
216
- description: "Filter by multiple blockchain networks (comma-separated chain IDs)",
217
- example: "1,137,10"
218
- }),
219
- loan_tokens: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
220
- message: "Loan assets must be comma-separated Ethereum addresses"
221
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
222
- description: "Filter by multiple loan assets (comma-separated)",
223
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
224
- }),
225
- status: v4.z.string().regex(/^[a-zA-Z_]+(,[a-zA-Z_]+)*$/, {
226
- message: "Status must be comma-separated status values"
227
- }).transform((val) => val.split(",")).refine((statuses) => statuses.every((status) => OfferStatusValues.includes(status)), {
228
- message: `Invalid status value. Must be one of: ${OfferStatusValues.join(", ")}`
229
- }).optional().meta({
230
- description: `Filter by multiple statuses (comma-separated). Valid values: ${OfferStatusValues.join(", ")}. By default, only offers with 'valid' status are returned.`,
231
- example: "valid,callback_error"
232
- }),
233
- callback_addresses: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
234
- message: "Callback addresses must be comma-separated Ethereum addresses"
235
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
236
- description: "Filter by multiple callback addresses (comma-separated)",
237
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
238
- }),
239
- // Asset range
240
- min_amount: v4.z.bigint({ coerce: true }).positive({
241
- message: "Min amount must be a positive number"
242
- }).optional().meta({
243
- description: "Minimum amount of assets in the offer",
244
- example: "1000"
245
- }),
246
- max_amount: v4.z.bigint({ coerce: true }).positive({
247
- message: "Max amount must be a positive number"
248
- }).optional().meta({
249
- description: "Maximum amount of assets in the offer",
250
- example: "10000"
251
- }),
252
- // Rate range
253
- min_rate: v4.z.bigint({ coerce: true }).positive({
254
- message: "Min rate must be a positive number"
255
- }).optional().meta({
256
- description: "Minimum rate per asset (in wei)",
257
- example: "500000000000000000"
258
- }),
259
- max_rate: v4.z.bigint({ coerce: true }).positive({
260
- message: "Max rate must be a positive number"
261
- }).optional().meta({
262
- description: "Maximum rate per asset (in wei)",
263
- example: "1500000000000000000"
264
- }),
265
- // Time range
266
- min_maturity: v4.z.coerce.number().int().min(0).optional().meta({
267
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
268
- example: "1700000000"
269
- }),
270
- max_maturity: v4.z.coerce.number().int().min(0).optional().meta({
271
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
272
- example: "1800000000"
273
- }),
274
- min_expiry: v4.z.coerce.number().int().optional().meta({
275
- description: "Minimum expiry timestamp (Unix timestamp in seconds)",
276
- example: "1700000000"
277
- }),
278
- max_expiry: v4.z.coerce.number().int().optional().meta({
279
- description: "Maximum expiry timestamp (Unix timestamp in seconds)",
280
- example: "1800000000"
281
- }),
282
- // Collateral filtering
283
- collateral_assets: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
284
- message: "Collateral assets must be comma-separated Ethereum addresses"
285
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
286
- description: "Filter by multiple collateral assets (comma-separated)",
287
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
288
- }),
289
- collateral_oracles: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
290
- message: "Collateral oracles must be comma-separated Ethereum addresses"
291
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
292
- description: "Filter by multiple rate oracles (comma-separated)",
293
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
294
- }),
295
- collateral_tuple: v4.z.string().transform((val, ctx) => {
296
- const pattern = /^(0x[a-fA-F0-9]{40}(:0x[a-fA-F0-9]{40})?(:[0-9]+(\.[0-9]+)?)?)(#0x[a-fA-F0-9]{40}(:0x[a-fA-F0-9]{40})?(:[0-9]+(\.[0-9]+)?)?)*$/;
297
- if (!pattern.test(val)) {
298
- ctx.addIssue({
299
- code: "custom",
300
- message: "collateral_tuple has an invalid format",
301
- input: val
302
- });
861
+ // src/core/Liquidity.ts
862
+ var Liquidity_exports = {};
863
+ __export(Liquidity_exports, {
864
+ calculateMaxDebt: () => calculateMaxDebt,
865
+ generateAllowancePoolId: () => generateAllowancePoolId,
866
+ generateBalancePoolId: () => generateBalancePoolId,
867
+ generateBuyVaultCallbackPoolId: () => generateBuyVaultCallbackPoolId,
868
+ generateDebtPoolId: () => generateDebtPoolId,
869
+ generateMarketLiquidityPoolId: () => generateMarketLiquidityPoolId,
870
+ generateObligationCollateralPoolId: () => generateObligationCollateralPoolId,
871
+ generateSellERC20CallbackPoolId: () => generateSellERC20CallbackPoolId,
872
+ generateUserVaultPositionPoolId: () => generateUserVaultPositionPoolId,
873
+ generateVaultPositionPoolId: () => generateVaultPositionPoolId
874
+ });
875
+ function calculateMaxDebt(amount, oraclePrice, lltv) {
876
+ const ORACLE_PRICE_SCALE = 10n ** 36n;
877
+ const PRECISION = 10n ** 18n;
878
+ const collateralQuoted = amount * oraclePrice / ORACLE_PRICE_SCALE;
879
+ const maxDebt = collateralQuoted * lltv / PRECISION;
880
+ return maxDebt;
881
+ }
882
+ function generateBalancePoolId(parameters) {
883
+ const { user, chainId, token } = parameters;
884
+ return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
885
+ }
886
+ function generateAllowancePoolId(parameters) {
887
+ const { user, chainId, token } = parameters;
888
+ return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
889
+ }
890
+ function generateSellERC20CallbackPoolId(parameters) {
891
+ const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
892
+ return `${user}-${chainId.toString()}-${obligationId2}-${token}-${offerHash}-sell_erc20_callback`.toLowerCase();
893
+ }
894
+ function generateObligationCollateralPoolId(parameters) {
895
+ const { user, chainId, obligationId: obligationId2, token } = parameters;
896
+ return `${user}-${chainId.toString()}-${obligationId2}-${token}-obligation-collateral`.toLowerCase();
897
+ }
898
+ function generateBuyVaultCallbackPoolId(parameters) {
899
+ const { user, chainId, vault, offerHash } = parameters;
900
+ return `${user}-${chainId.toString()}-${vault}-${offerHash}-${"buy_vault_v1_callback" /* BuyVaultV1Callback */}`.toLowerCase();
901
+ }
902
+ function generateDebtPoolId(parameters) {
903
+ const { user, chainId, obligationId: obligationId2 } = parameters;
904
+ return `${user}-${chainId.toString()}-${obligationId2}-debt`.toLowerCase();
905
+ }
906
+ function generateUserVaultPositionPoolId(parameters) {
907
+ const { user, chainId, vault } = parameters;
908
+ return `${user}-${chainId.toString()}-${vault}-user-vault-position`.toLowerCase();
909
+ }
910
+ function generateVaultPositionPoolId(parameters) {
911
+ const { vault, chainId, marketId } = parameters;
912
+ return `${vault}-${chainId.toString()}-${marketId}-vault-position`.toLowerCase();
913
+ }
914
+ function generateMarketLiquidityPoolId(parameters) {
915
+ const { chainId, marketId } = parameters;
916
+ return `${chainId.toString()}-${marketId}-market-liquidity`.toLowerCase();
917
+ }
918
+
919
+ // src/core/Maturity.ts
920
+ var Maturity_exports = {};
921
+ __export(Maturity_exports, {
922
+ InvalidDateError: () => InvalidDateError,
923
+ InvalidFormatError: () => InvalidFormatError,
924
+ InvalidOptionError: () => InvalidOptionError2,
925
+ MaturitySchema: () => MaturitySchema,
926
+ from: () => from3
927
+ });
928
+ var MaturitySchema = z7__namespace.number().int().refine(
929
+ (maturity) => {
930
+ try {
931
+ from3(maturity);
932
+ return true;
933
+ } catch (_e) {
934
+ return false;
303
935
  }
304
- return val.split("#").map((tuple) => {
305
- const parts = tuple.split(":");
306
- if (parts.length === 0 || !parts[0]) {
307
- ctx.addIssue({
308
- code: "custom",
309
- message: "Asset address is required for each collateral tuple",
310
- path: ["asset"],
311
- input: val
312
- });
313
- return v4.z.NEVER;
314
- }
315
- const asset = parts[0]?.toLowerCase();
316
- const oracle = parts[1]?.toLowerCase();
317
- const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
318
- if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
319
- ctx.addIssue({
320
- code: "custom",
321
- message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
322
- path: ["lltv"],
323
- input: val
324
- });
325
- return v4.z.NEVER;
326
- }
327
- let lltvValue;
328
- if (lltv !== void 0) {
329
- try {
330
- lltvValue = mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
331
- } catch (e) {
332
- ctx.issues.push({
333
- code: "custom",
334
- message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
335
- input: lltv,
336
- path: ["lltv"]
337
- });
338
- return v4.z.NEVER;
339
- }
340
- }
341
- return {
342
- asset,
343
- oracle,
344
- lltv: lltvValue
345
- };
346
- });
347
- }).optional().meta({
348
- description: "Filter by collateral combinations in format: asset:oracle:lltv#asset2:oracle2:lltv2. Oracle and lltv are optional. Use # to separate multiple combinations.",
349
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:94.5"
350
- }),
351
- min_lltv: v4.z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).optional().meta({
352
- description: "Minimum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 80.5 = 80.5%)",
353
- example: "80.5"
354
- }),
355
- max_lltv: v4.z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).optional().meta({
356
- description: "Maximum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 95.5 = 95.5%)",
357
- example: "95.5"
358
- }),
359
- // Sorting parameters
360
- sort_by: v4.z.enum(["rate", "maturity", "expiry", "amount"]).optional().meta({
361
- description: "Field to sort results by",
362
- example: "rate"
363
- }),
364
- sort_order: v4.z.enum(["asc", "desc"]).optional().default("desc").meta({
365
- description: "Sort direction: asc (ascending) or desc (descending, default)",
366
- example: "desc"
367
- }),
368
- // Pagination
369
- cursor: v4.z.string().optional().refine(
370
- (val) => {
371
- if (!val) return true;
936
+ },
937
+ {
938
+ error: (issue) => {
372
939
  try {
373
- const decoded = decode(val);
374
- return decoded !== null;
375
- } catch (_error) {
376
- return false;
940
+ const maturityDate = new Date(issue.input * 1e3);
941
+ return `The maturity is set to ${maturityDate}. It must fall on the allowed settlement cycles (Friday 15:00 UTC at the end of week/month/quarter).`;
942
+ } catch (_) {
943
+ return `The maturity is set to ${issue.input}. It must fall on the allowed settlement cycles (Friday 15:00 UTC at the end of week/month/quarter).`;
377
944
  }
378
- },
379
- {
380
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
381
945
  }
382
- ).meta({
383
- description: "Pagination cursor in base64url-encoded format",
384
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
385
- }),
386
- limit: v4.z.string().regex(/^[1-9]\d*$/, {
387
- message: "Limit must be a positive integer"
388
- }).transform((val) => Number.parseInt(val, 10)).pipe(
389
- v4.z.number().max(MAX_LIMIT, {
390
- message: `Limit cannot exceed ${MAX_LIMIT}`
391
- })
392
- ).optional().default(DEFAULT_LIMIT).meta({
393
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
394
- example: 10
395
- })
396
- }).refine(
397
- (data) => data.min_maturity === void 0 || data.max_maturity === void 0 || data.max_maturity >= data.min_maturity,
398
- {
399
- message: "max_maturity must be greater than or equal to min_maturity",
400
- path: ["max_maturity"]
401
946
  }
402
- ).refine(
403
- (data) => data.min_expiry === void 0 || data.max_expiry === void 0 || data.max_expiry >= data.min_expiry,
404
- {
405
- message: "max_expiry must be greater than or equal to min_expiry",
406
- path: ["max_expiry"]
947
+ ).transform((maturity) => maturity);
948
+ var MaturityOptions = {
949
+ end_of_week: () => endOfWeek(),
950
+ end_of_next_week: () => endOfNextWeek(),
951
+ end_of_month: () => endOfMonth(),
952
+ end_of_next_month: () => endOfNextMonth(),
953
+ end_of_quarter: () => endOfQuarter(),
954
+ end_of_next_quarter: () => endOfNextQuarter()
955
+ };
956
+ function from3(ts) {
957
+ if (typeof ts === "string") {
958
+ if (ts in MaturityOptions) return MaturityOptions[ts]();
959
+ throw new InvalidOptionError2(ts);
960
+ }
961
+ if (typeof ts === "number" && ts > 1e12) throw new InvalidFormatError();
962
+ if (!Object.values(MaturityOptions).some((option) => option() === ts))
963
+ throw new InvalidDateError(ts);
964
+ return ts;
965
+ }
966
+ var endOfWeek = () => fridayOfWeek(0);
967
+ var endOfNextWeek = () => fridayOfWeek(1);
968
+ var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
969
+ var endOfNextMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth() + 1);
970
+ var endOfQuarter = () => lastFridayOfQuarter(0);
971
+ var endOfNextQuarter = () => lastFridayOfQuarter(1);
972
+ var fridayOfWeek = (weeksAhead = 0) => {
973
+ const now2 = /* @__PURE__ */ new Date();
974
+ const today15H = new Date(
975
+ Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth(), now2.getUTCDate(), 15)
976
+ );
977
+ let daysUntilFriday = (5 - today15H.getUTCDay() + 7) % 7;
978
+ if (daysUntilFriday === 0 && now2.getTime() >= today15H.getTime()) {
979
+ daysUntilFriday = 7;
407
980
  }
408
- ).refine(
409
- (data) => data.min_amount === void 0 || data.max_amount === void 0 || data.max_amount >= data.min_amount,
410
- {
411
- message: "max_amount must be greater than or equal to min_amount",
412
- path: ["max_amount"]
981
+ const friday = new Date(today15H);
982
+ friday.setUTCDate(friday.getUTCDate() + daysUntilFriday + weeksAhead * 7);
983
+ return friday.getTime() / 1e3;
984
+ };
985
+ var lastFridayOfMonth = (year, month) => {
986
+ const lastDayOfMonth15H = new Date(Date.UTC(year, month + 1, 0, 15));
987
+ while (lastDayOfMonth15H.getUTCDay() !== 5) {
988
+ lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
413
989
  }
414
- ).refine(
415
- (data) => data.min_rate === void 0 || data.max_rate === void 0 || data.max_rate >= data.min_rate,
416
- {
417
- message: "max_rate must be greater than or equal to min_rate",
418
- path: ["max_rate"]
990
+ const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
991
+ return maturity;
992
+ };
993
+ var lastFridayOfQuarter = (quartersAhead = 0) => {
994
+ const now2 = /* @__PURE__ */ new Date();
995
+ const quarterIndex = Math.floor(now2.getUTCMonth() / 3) + quartersAhead;
996
+ const year = now2.getUTCFullYear() + Math.floor(quarterIndex / 4);
997
+ const quarter = quarterIndex % 4;
998
+ const lastMonth = quarter * 3 + 2;
999
+ return lastFridayOfMonth(year, lastMonth);
1000
+ };
1001
+ var InvalidFormatError = class extends BaseError {
1002
+ constructor() {
1003
+ super("Invalid maturity format. Maturity should be expressed in seconds.");
1004
+ __publicField(this, "name", "Maturity.InvalidFormatError");
419
1005
  }
420
- ).refine(
421
- (data) => data.min_lltv === void 0 || data.max_lltv === void 0 || data.max_lltv >= data.min_lltv,
422
- {
423
- message: "max_lltv must be greater than or equal to min_lltv",
424
- path: ["max_lltv"]
1006
+ };
1007
+ var InvalidDateError = class extends BaseError {
1008
+ constructor(input) {
1009
+ super(
1010
+ `Invalid maturity date. Input: "${input}". Accepted values are: ${Object.values(
1011
+ MaturityOptions
1012
+ ).map((option) => `"${option()}"`).join(", ")}.`
1013
+ );
1014
+ __publicField(this, "name", "Maturity.InvalidDateError");
425
1015
  }
426
- );
427
- var MatchOffersQueryParams = v4.z.object({
428
- // Required parameters
429
- side: v4.z.enum(["buy", "sell"]).meta({
430
- description: "The desired side of the match: 'buy' if you want to buy, 'sell' if you want to sell. If your intent is to sell, buy offers will be returned, and vice versa.",
431
- example: "buy"
432
- }),
433
- chain_id: v4.z.string().transform((val, ctx) => {
434
- const numericLike = /^-?\d+$/.test(val);
435
- if (!numericLike) {
436
- ctx.addIssue({
437
- code: "custom",
438
- message: "chain_id has an invalid format",
439
- input: val
440
- });
441
- ctx.addIssue({
442
- code: "custom",
443
- message: "Invalid input: expected number, received NaN",
444
- input: val
445
- });
446
- return v4.z.NEVER;
447
- }
448
- return Number.parseInt(val, 10);
449
- }).pipe(v4.z.number().positive()).meta({
450
- description: "The blockchain network chain ID",
451
- example: "1"
452
- }),
453
- // Optional parameters
454
- rate: v4.z.bigint({ coerce: true }).positive({
455
- message: "Rate must be a positive number"
456
- }).optional().meta({
457
- description: "Rate per asset (in wei) for matching offers",
458
- example: "1000000000000000000"
459
- }),
460
- // Collateral filtering
461
- collaterals: v4.z.string().transform((val, ctx) => {
462
- const pattern = /^(0x[a-fA-F0-9]{40}:0x[a-fA-F0-9]{40}:[0-9]+(\.[0-9]+)?)(#0x[a-fA-F0-9]{40}:0x[a-fA-F0-9]{40}:[0-9]+(\.[0-9]+)?)*$/;
463
- if (!pattern.test(val)) {
464
- ctx.addIssue({
465
- code: "custom",
466
- message: "Collaterals must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. All fields are required for each collateral.",
467
- input: val
468
- });
469
- }
470
- return val.split("#").map((collateral) => {
471
- const parts = collateral.split(":");
472
- if (parts.length !== 3) {
473
- ctx.addIssue({
474
- code: "custom",
475
- message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
476
- path: ["collaterals"],
477
- input: val
478
- });
479
- return v4.z.NEVER;
480
- }
481
- const [asset, oracle, lltvStr] = parts;
482
- if (!asset || !oracle || !lltvStr) {
483
- ctx.addIssue({
484
- code: "custom",
485
- message: "Asset, oracle, and lltv are all required for each collateral",
486
- path: ["collaterals"],
487
- input: val
488
- });
489
- }
490
- let lltvValue;
491
- if (lltvStr !== void 0) {
492
- try {
493
- lltvValue = mempool.LLTV.from(viem.parseUnits(lltvStr, 16));
494
- } catch (e) {
495
- ctx.issues.push({
496
- code: "custom",
497
- message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
498
- input: lltvStr,
499
- path: ["lltv"]
500
- });
501
- return v4.z.NEVER;
502
- }
503
- }
504
- return {
505
- asset: asset.toLowerCase(),
506
- oracle: oracle.toLowerCase(),
507
- lltv: lltvValue
508
- };
509
- });
510
- }).optional().meta({
511
- description: "Collateral requirements in format: asset:oracle:lltv#asset2:oracle2:lltv2. Use # to separate multiple collaterals.",
512
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:94.5"
513
- }),
514
- // Maturity filtering
515
- maturity: v4.z.coerce.number().int().min(0).optional().meta({
516
- description: "Maturity timestamp (Unix timestamp in seconds)",
517
- example: "1700000000"
518
- }),
519
- min_maturity: v4.z.coerce.number().int().min(0).optional().meta({
520
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
521
- example: "1700000000"
522
- }),
523
- max_maturity: v4.z.coerce.number().int().min(0).optional().meta({
524
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
525
- example: "1800000000"
526
- }),
527
- // Asset and creator filtering
528
- loan_token: v4.z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
529
- message: "Loan asset must be a valid Ethereum address"
530
- }).transform((val) => val.toLowerCase()).optional().meta({
531
- description: "The loan asset address to match against",
532
- example: "0x1234567890123456789012345678901234567890"
533
- }),
534
- creator: v4.z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
535
- message: "Creator must be a valid Ethereum address"
536
- }).transform((val) => val.toLowerCase()).optional().meta({
537
- description: "Filter by a specific offer creator address",
538
- example: "0x1234567890123456789012345678901234567890"
539
- }),
540
- // Pagination
541
- cursor: v4.z.string().optional().refine(
542
- (val) => {
543
- if (!val) return true;
544
- try {
545
- const decoded = decode(val);
546
- return decoded !== null;
547
- } catch (_error) {
548
- return false;
549
- }
550
- },
551
- {
552
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
553
- }
554
- ).meta({
555
- description: "Pagination cursor in base64url-encoded format",
556
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
557
- }),
558
- limit: v4.z.string().regex(/^[1-9]\d*$/, {
559
- message: "Limit must be a positive integer"
560
- }).transform((val) => Number.parseInt(val, 10)).pipe(
561
- v4.z.number().max(MAX_LIMIT, {
562
- message: `Limit cannot exceed ${MAX_LIMIT}`
563
- })
564
- ).optional().default(DEFAULT_LIMIT).meta({
565
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
566
- example: 10
567
- })
568
- }).refine(
569
- (data) => data.min_maturity === void 0 || data.max_maturity === void 0 || data.max_maturity >= data.min_maturity,
570
- {
571
- message: "max_maturity must be greater than or equal to min_maturity",
572
- path: ["max_maturity"]
1016
+ };
1017
+ var InvalidOptionError2 = class extends BaseError {
1018
+ constructor(input) {
1019
+ super(
1020
+ `Invalid maturity option. Input: "${input}". Accepted values are: ${Object.keys(
1021
+ MaturityOptions
1022
+ ).map((option) => `"${option}"`).join(", ")}.`
1023
+ );
1024
+ __publicField(this, "name", "Maturity.InvalidOptionError");
573
1025
  }
574
- );
575
- var schemas = {
576
- get_offers: GetOffersQueryParams,
577
- match_offers: MatchOffersQueryParams
578
1026
  };
579
- function safeParse(action, query, error) {
580
- return schemas[action].safeParse(query, {
581
- error
582
- });
583
- }
584
1027
 
585
- // src/api/Schema/openapi.ts
586
- var successResponseSchema = v4.z.object({
1028
+ // src/core/Obligation.ts
1029
+ var Obligation_exports = {};
1030
+ __export(Obligation_exports, {
1031
+ CollateralsAreNotSortedError: () => CollateralsAreNotSortedError,
1032
+ InvalidObligationError: () => InvalidObligationError,
1033
+ ObligationSchema: () => ObligationSchema,
1034
+ from: () => from4,
1035
+ fromSnakeCase: () => fromSnakeCase2,
1036
+ id: () => id,
1037
+ random: () => random
1038
+ });
1039
+
1040
+ // src/utils/Format.ts
1041
+ var Format_exports = {};
1042
+ __export(Format_exports, {
1043
+ fromSnakeCase: () => fromSnakeCase,
1044
+ toSnakeCase: () => toSnakeCase
1045
+ });
1046
+ function toSnakeCase(obj) {
1047
+ return stringifyBigint(
1048
+ processObject(
1049
+ obj,
1050
+ (s) => s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`),
1051
+ (value) => typeof value === "string" && viem.isAddress(value.toLowerCase()) ? viem.getAddress(value.toLowerCase()) : value
1052
+ )
1053
+ );
1054
+ }
1055
+ function fromSnakeCase(obj) {
1056
+ return processObject(
1057
+ obj,
1058
+ (s) => viem.isAddress(s.toLowerCase()) ? s : s.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
1059
+ (value) => typeof value === "string" && viem.isAddress(value.toLowerCase()) ? value.toLowerCase() : value
1060
+ );
1061
+ }
1062
+ function processObject(obj, fnKey, fnValue) {
1063
+ if (typeof obj !== "object" || obj === null) return obj;
1064
+ if (Array.isArray(obj)) return obj.map((item) => processObject(item, fnKey, fnValue));
1065
+ return Object.entries(obj).reduce(
1066
+ (acc, [key, value]) => {
1067
+ const newKey = fnKey(key);
1068
+ acc[newKey] = typeof value === "object" && value !== null ? processObject(value, fnKey, fnValue) : fnValue(value);
1069
+ return acc;
1070
+ },
1071
+ {}
1072
+ );
1073
+ }
1074
+ function stringifyBigint(value) {
1075
+ if (typeof value === "bigint") return value.toString();
1076
+ if (Array.isArray(value)) return value.map(stringifyBigint);
1077
+ if (value && typeof value === "object") {
1078
+ const out = {};
1079
+ for (const [k, v] of Object.entries(value)) {
1080
+ out[k] = stringifyBigint(v);
1081
+ }
1082
+ return out;
1083
+ }
1084
+ return value;
1085
+ }
1086
+
1087
+ // src/core/Obligation.ts
1088
+ var ObligationSchema = z7__namespace.object({
1089
+ chainId: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1090
+ loanToken: z7__namespace.string().transform(transformAddress),
1091
+ collaterals: CollateralsSchema,
1092
+ maturity: MaturitySchema
1093
+ });
1094
+ function from4(parameters) {
1095
+ try {
1096
+ const parsedObligation = ObligationSchema.parse({
1097
+ ...parameters,
1098
+ maturity: from3(parameters.maturity)
1099
+ });
1100
+ return {
1101
+ chainId: parsedObligation.chainId,
1102
+ loanToken: parsedObligation.loanToken.toLowerCase(),
1103
+ collaterals: parsedObligation.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)),
1104
+ maturity: parsedObligation.maturity
1105
+ };
1106
+ } catch (error) {
1107
+ throw new InvalidObligationError(error);
1108
+ }
1109
+ }
1110
+ function fromSnakeCase2(input) {
1111
+ return from4(fromSnakeCase(input));
1112
+ }
1113
+ function id(obligation) {
1114
+ let lastAsset = "";
1115
+ for (const collateral of obligation.collaterals) {
1116
+ const newAsset = collateral.asset.toLowerCase();
1117
+ if (newAsset.localeCompare(lastAsset) < 0) throw new CollateralsAreNotSortedError();
1118
+ lastAsset = newAsset;
1119
+ }
1120
+ return viem.keccak256(
1121
+ viem.encodeAbiParameters(
1122
+ [
1123
+ { type: "uint256" },
1124
+ { type: "address" },
1125
+ {
1126
+ type: "tuple[]",
1127
+ components: [
1128
+ { type: "address", name: "token" },
1129
+ { type: "uint256", name: "lltv" },
1130
+ { type: "address", name: "oracle" }
1131
+ ]
1132
+ },
1133
+ { type: "uint256" }
1134
+ ],
1135
+ [
1136
+ obligation.chainId,
1137
+ obligation.loanToken.toLowerCase(),
1138
+ obligation.collaterals.map((c) => ({
1139
+ token: c.asset.toLowerCase(),
1140
+ lltv: c.lltv,
1141
+ oracle: c.oracle.toLowerCase()
1142
+ })),
1143
+ BigInt(obligation.maturity)
1144
+ ]
1145
+ )
1146
+ );
1147
+ }
1148
+ function random() {
1149
+ return from4({
1150
+ chainId: 1n,
1151
+ loanToken: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1152
+ collaterals: [
1153
+ from2({
1154
+ asset: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1155
+ oracle: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1156
+ lltv: 0.965
1157
+ })
1158
+ ],
1159
+ maturity: from3("end_of_next_quarter")
1160
+ });
1161
+ }
1162
+ var InvalidObligationError = class extends BaseError {
1163
+ constructor(error) {
1164
+ super("Invalid obligation.", { cause: error });
1165
+ __publicField(this, "name", "Obligation.InvalidObligationError");
1166
+ }
1167
+ };
1168
+ var CollateralsAreNotSortedError = class extends BaseError {
1169
+ constructor() {
1170
+ super("Collaterals are not sorted alphabetically by address.");
1171
+ __publicField(this, "name", "Obligation.CollateralsAreNotSortedError");
1172
+ }
1173
+ };
1174
+
1175
+ // src/core/Offer.ts
1176
+ var Offer_exports = {};
1177
+ __export(Offer_exports, {
1178
+ AccountNotSetError: () => AccountNotSetError,
1179
+ InvalidOfferError: () => InvalidOfferError,
1180
+ OfferHashSchema: () => OfferHashSchema,
1181
+ OfferSchema: () => OfferSchema,
1182
+ consumedEvent: () => consumedEvent,
1183
+ decode: () => decode2,
1184
+ domain: () => domain,
1185
+ encode: () => encode2,
1186
+ from: () => from5,
1187
+ fromConsumedLog: () => fromConsumedLog,
1188
+ fromSnakeCase: () => fromSnakeCase3,
1189
+ hash: () => hash,
1190
+ obligationId: () => obligationId,
1191
+ random: () => random2,
1192
+ sign: () => sign,
1193
+ toSnakeCase: () => toSnakeCase2,
1194
+ types: () => types
1195
+ });
1196
+
1197
+ // src/utils/index.ts
1198
+ var utils_exports = {};
1199
+ __export(utils_exports, {
1200
+ BaseError: () => BaseError,
1201
+ Time: () => time_exports,
1202
+ batch: () => batch,
1203
+ batchMulticall: () => batchMulticall,
1204
+ fromSnakeCase: () => fromSnakeCase,
1205
+ lazy: () => lazy,
1206
+ max: () => max,
1207
+ min: () => min,
1208
+ poll: () => poll,
1209
+ retry: () => retry,
1210
+ toSnakeCase: () => toSnakeCase,
1211
+ wait: () => wait
1212
+ });
1213
+
1214
+ // src/utils/retry.ts
1215
+ var retry = async (fn, attempts = 3, delayMs = 50) => {
1216
+ let lastErr;
1217
+ for (let i = 0; i < attempts; i++) {
1218
+ try {
1219
+ return await fn();
1220
+ } catch (err) {
1221
+ lastErr = err;
1222
+ if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
1223
+ }
1224
+ }
1225
+ throw lastErr;
1226
+ };
1227
+
1228
+ // src/utils/batchMulticall.ts
1229
+ async function batchMulticall(parameters) {
1230
+ const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
1231
+ const results = [];
1232
+ for (const callsBatch of batch(calls, batchSize)) {
1233
+ const batchResults = await retry(
1234
+ () => client.multicall({
1235
+ allowFailure: false,
1236
+ contracts: callsBatch,
1237
+ ...blockNumber ? { blockNumber } : {}
1238
+ }),
1239
+ retryAttempts,
1240
+ retryDelayMs
1241
+ );
1242
+ results.push(...batchResults);
1243
+ }
1244
+ return results;
1245
+ }
1246
+
1247
+ // src/utils/lazy.ts
1248
+ function lazy(pollFn) {
1249
+ return () => async function* () {
1250
+ let active = true;
1251
+ let resolveNext = null;
1252
+ const queue = [];
1253
+ const wait2 = () => new Promise((resolve) => {
1254
+ resolveNext = resolve;
1255
+ });
1256
+ const emit = (item) => {
1257
+ queue.push(item);
1258
+ resolveNext?.();
1259
+ resolveNext = null;
1260
+ };
1261
+ let unpoll = null;
1262
+ const stop = () => {
1263
+ active = false;
1264
+ unpoll?.();
1265
+ resolveNext?.();
1266
+ resolveNext = null;
1267
+ };
1268
+ unpoll = pollFn(emit, { stop });
1269
+ try {
1270
+ while (active) {
1271
+ if (queue.length === 0) await wait2();
1272
+ while (queue.length > 0 && active) yield queue.shift();
1273
+ }
1274
+ } finally {
1275
+ stop();
1276
+ }
1277
+ }();
1278
+ }
1279
+
1280
+ // src/utils/wait.ts
1281
+ async function wait(time) {
1282
+ return new Promise((res) => setTimeout(res, time));
1283
+ }
1284
+
1285
+ // src/utils/poll.ts
1286
+ function poll(fn, { interval }) {
1287
+ let active = true;
1288
+ const unwatch = () => active = false;
1289
+ const watch2 = async () => {
1290
+ await wait(interval);
1291
+ const poll2 = async () => {
1292
+ if (!active) return;
1293
+ await fn({ unpoll: unwatch });
1294
+ await wait(interval);
1295
+ poll2();
1296
+ };
1297
+ poll2();
1298
+ };
1299
+ watch2();
1300
+ return unwatch;
1301
+ }
1302
+
1303
+ // src/utils/time.ts
1304
+ var time_exports = {};
1305
+ __export(time_exports, {
1306
+ max: () => max2,
1307
+ now: () => now
1308
+ });
1309
+ function now() {
1310
+ return Math.floor(Date.now() / 1e3);
1311
+ }
1312
+ function max2() {
1313
+ return 864e16;
1314
+ }
1315
+
1316
+ // src/core/Offer.ts
1317
+ var OfferHashSchema = z7__namespace.string().regex(/^0x[0-9a-fA-F]{64}$/, {
1318
+ message: "Hash must be a valid 32-byte hex string"
1319
+ }).transform(transformHex);
1320
+ var OfferSchema = (parameters) => {
1321
+ const { omitHash = false } = parameters || {};
1322
+ const now2 = time_exports.now();
1323
+ let base = z7__namespace.object({
1324
+ offering: z7__namespace.string().transform(transformAddress),
1325
+ assets: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1326
+ rate: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1327
+ maturity: MaturitySchema,
1328
+ expiry: z7__namespace.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
1329
+ start: z7__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1330
+ nonce: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1331
+ buy: z7__namespace.boolean(),
1332
+ chainId: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1333
+ loanToken: z7__namespace.string().transform(transformAddress),
1334
+ collaterals: CollateralsSchema,
1335
+ callback: z7__namespace.object({
1336
+ address: z7__namespace.string().transform(transformAddress),
1337
+ data: z7__namespace.string().transform(transformHex),
1338
+ gasLimit: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1339
+ }),
1340
+ consumed: z7__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1341
+ blockNumber: z7__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1342
+ signature: z7__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1343
+ message: "Signature must be a valid 65-byte hex string"
1344
+ }).transform(transformHex).optional()
1345
+ });
1346
+ if (!omitHash) base = base.extend({ hash: OfferHashSchema });
1347
+ return base.refine((data) => data.start < data.expiry, {
1348
+ message: "Start must be before expiry",
1349
+ path: ["start"]
1350
+ }).refine((data) => data.expiry <= data.maturity, {
1351
+ message: "Expiry cannot be after maturity",
1352
+ path: ["expiry"]
1353
+ });
1354
+ };
1355
+ function from5(input) {
1356
+ try {
1357
+ const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1358
+ const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
1359
+ return {
1360
+ ...parsedOffer,
1361
+ hash: parsedHash
1362
+ };
1363
+ } catch (error) {
1364
+ throw new InvalidOfferError(error);
1365
+ }
1366
+ }
1367
+ function fromSnakeCase3(input) {
1368
+ return from5(fromSnakeCase(input));
1369
+ }
1370
+ function toSnakeCase2(offer) {
1371
+ return toSnakeCase(offer);
1372
+ }
1373
+ function random2() {
1374
+ const loanToken = accounts.privateKeyToAccount(accounts.generatePrivateKey()).address;
1375
+ const maturity = from3("end_of_month");
1376
+ const expiry = from3("end_of_week") - 1;
1377
+ const lltv = from(0.965);
1378
+ const offer = from5({
1379
+ offering: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1380
+ assets: BigInt(Math.floor(Math.random() * 1e6)),
1381
+ rate: BigInt(Math.floor(Math.random() * 1e6)),
1382
+ maturity,
1383
+ expiry,
1384
+ start: expiry - 10,
1385
+ nonce: BigInt(Math.floor(Math.random() * 1e6)),
1386
+ buy: Math.random() > 0.5,
1387
+ chainId: 1n,
1388
+ loanToken,
1389
+ collaterals: [
1390
+ from2({
1391
+ asset: viem.zeroAddress,
1392
+ oracle: viem.zeroAddress,
1393
+ lltv
1394
+ })
1395
+ ],
1396
+ callback: {
1397
+ address: viem.zeroAddress,
1398
+ data: "0x",
1399
+ gasLimit: 0n
1400
+ },
1401
+ consumed: 0n,
1402
+ blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
1403
+ });
1404
+ return offer;
1405
+ }
1406
+ var domain = (chainId) => ({
1407
+ chainId,
1408
+ verifyingContract: viem.zeroAddress
1409
+ });
1410
+ var types = {
1411
+ EIP712Domain: [
1412
+ { name: "chainId", type: "uint256" },
1413
+ { name: "verifyingContract", type: "address" }
1414
+ ],
1415
+ Offer: [
1416
+ { name: "offering", type: "address" },
1417
+ { name: "assets", type: "uint256" },
1418
+ { name: "rate", type: "uint256" },
1419
+ { name: "maturity", type: "uint256" },
1420
+ { name: "expiry", type: "uint256" },
1421
+ { name: "nonce", type: "uint256" },
1422
+ { name: "buy", type: "bool" },
1423
+ { name: "loanToken", type: "address" },
1424
+ { name: "collaterals", type: "Collateral[]" },
1425
+ { name: "callback", type: "Callback" }
1426
+ ],
1427
+ Collateral: [
1428
+ { name: "asset", type: "address" },
1429
+ { name: "oracle", type: "address" },
1430
+ { name: "lltv", type: "uint256" }
1431
+ ],
1432
+ Callback: [
1433
+ { name: "address", type: "address" },
1434
+ { name: "data", type: "bytes" },
1435
+ { name: "gasLimit", type: "uint256" }
1436
+ ]
1437
+ };
1438
+ function sign(offer, wallet) {
1439
+ if (!wallet.account) throw new AccountNotSetError();
1440
+ return wallet.signTypedData({
1441
+ account: wallet.account,
1442
+ domain: domain(offer.chainId),
1443
+ types,
1444
+ primaryType: "Offer",
1445
+ message: {
1446
+ offering: offer.offering.toLowerCase(),
1447
+ assets: offer.assets,
1448
+ rate: offer.rate,
1449
+ maturity: BigInt(offer.maturity),
1450
+ expiry: BigInt(offer.expiry),
1451
+ nonce: offer.nonce,
1452
+ buy: offer.buy,
1453
+ loanToken: offer.loanToken.toLowerCase(),
1454
+ collaterals: offer.collaterals,
1455
+ callback: {
1456
+ address: offer.callback.address.toLowerCase(),
1457
+ data: offer.callback.data,
1458
+ gasLimit: offer.callback.gasLimit
1459
+ }
1460
+ }
1461
+ });
1462
+ }
1463
+ function hash(offer) {
1464
+ return viem.hashTypedData({
1465
+ domain: domain(offer.chainId),
1466
+ message: {
1467
+ offering: offer.offering.toLowerCase(),
1468
+ assets: offer.assets,
1469
+ rate: offer.rate,
1470
+ maturity: BigInt(offer.maturity),
1471
+ expiry: BigInt(offer.expiry),
1472
+ nonce: offer.nonce,
1473
+ buy: offer.buy,
1474
+ loanToken: offer.loanToken.toLowerCase(),
1475
+ collaterals: offer.collaterals,
1476
+ callback: {
1477
+ address: offer.callback.address.toLowerCase(),
1478
+ data: offer.callback.data,
1479
+ gasLimit: offer.callback.gasLimit
1480
+ }
1481
+ },
1482
+ primaryType: "Offer",
1483
+ types
1484
+ });
1485
+ }
1486
+ function obligationId(offer) {
1487
+ return id(
1488
+ from4({
1489
+ chainId: offer.chainId,
1490
+ loanToken: offer.loanToken,
1491
+ collaterals: offer.collaterals,
1492
+ maturity: offer.maturity
1493
+ })
1494
+ );
1495
+ }
1496
+ var OfferAbi = [
1497
+ { name: "offering", type: "address" },
1498
+ { name: "assets", type: "uint256" },
1499
+ { name: "rate", type: "uint256" },
1500
+ { name: "maturity", type: "uint256" },
1501
+ { name: "expiry", type: "uint256" },
1502
+ { name: "nonce", type: "uint256" },
1503
+ { name: "buy", type: "bool" },
1504
+ { name: "chainId", type: "uint256" },
1505
+ { name: "loanToken", type: "address" },
1506
+ { name: "start", type: "uint256" },
1507
+ {
1508
+ name: "collaterals",
1509
+ type: "tuple[]",
1510
+ components: [
1511
+ { name: "asset", type: "address" },
1512
+ { name: "oracle", type: "address" },
1513
+ { name: "lltv", type: "uint256" }
1514
+ ]
1515
+ },
1516
+ {
1517
+ name: "callback",
1518
+ type: "tuple",
1519
+ components: [
1520
+ { name: "address", type: "address" },
1521
+ { name: "data", type: "bytes" },
1522
+ { name: "gasLimit", type: "uint256" }
1523
+ ]
1524
+ },
1525
+ { name: "signature", type: "bytes" }
1526
+ ];
1527
+ function encode2(offer) {
1528
+ return viem.encodeAbiParameters(OfferAbi, [
1529
+ offer.offering,
1530
+ offer.assets,
1531
+ offer.rate,
1532
+ BigInt(offer.maturity),
1533
+ BigInt(offer.expiry),
1534
+ offer.nonce,
1535
+ offer.buy,
1536
+ offer.chainId,
1537
+ offer.loanToken,
1538
+ BigInt(offer.start),
1539
+ offer.collaterals,
1540
+ offer.callback,
1541
+ offer.signature ?? "0x"
1542
+ ]);
1543
+ }
1544
+ function decode2(data, blockNumber) {
1545
+ let decoded;
1546
+ try {
1547
+ decoded = viem.decodeAbiParameters(OfferAbi, data);
1548
+ } catch (error) {
1549
+ throw new InvalidOfferError(error);
1550
+ }
1551
+ const offer = from5({
1552
+ offering: decoded[0],
1553
+ assets: decoded[1],
1554
+ rate: decoded[2],
1555
+ maturity: from3(Number(decoded[3])),
1556
+ expiry: Number(decoded[4]),
1557
+ nonce: decoded[5],
1558
+ buy: decoded[6],
1559
+ chainId: decoded[7],
1560
+ loanToken: decoded[8],
1561
+ start: Number(decoded[9]),
1562
+ collaterals: decoded[10].map((c) => {
1563
+ return from2({
1564
+ asset: c.asset,
1565
+ oracle: c.oracle,
1566
+ lltv: c.lltv
1567
+ });
1568
+ }),
1569
+ callback: {
1570
+ address: decoded[11].address,
1571
+ data: decoded[11].data,
1572
+ gasLimit: decoded[11].gasLimit
1573
+ },
1574
+ consumed: 0n,
1575
+ blockNumber: Number(blockNumber),
1576
+ ...decoded[12] === "0x" ? {} : { signature: decoded[12] }
1577
+ });
1578
+ return offer;
1579
+ }
1580
+ var consumedEvent = {
1581
+ type: "event",
1582
+ name: "Consumed",
1583
+ inputs: [
1584
+ { name: "user", type: "address", indexed: true, internalType: "address" },
1585
+ { name: "nonce", type: "uint256", indexed: true, internalType: "uint256" },
1586
+ { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }
1587
+ ],
1588
+ anonymous: false
1589
+ };
1590
+ function fromConsumedLog(parameters) {
1591
+ const { blockNumber, logIndex, chainId, transactionHash, user, nonce, amount } = parameters;
1592
+ return {
1593
+ id: `${blockNumber.toString()}-${logIndex.toString()}-${chainId}-${transactionHash}`,
1594
+ chainId: BigInt(chainId),
1595
+ offering: user,
1596
+ nonce,
1597
+ amount,
1598
+ blockNumber: Number(blockNumber)
1599
+ };
1600
+ }
1601
+ var InvalidOfferError = class extends BaseError {
1602
+ constructor(error) {
1603
+ super("Invalid offer.", { cause: error });
1604
+ __publicField(this, "name", "Offer.InvalidOfferError");
1605
+ }
1606
+ };
1607
+ var AccountNotSetError = class extends BaseError {
1608
+ constructor() {
1609
+ super("Account not set.");
1610
+ __publicField(this, "name", "Offer.AccountNotSetError");
1611
+ }
1612
+ };
1613
+
1614
+ // src/core/types.ts
1615
+ var BrandTypeId = Symbol.for("mempool/Brand");
1616
+
1617
+ // src/api/Api/Schema/ObligationResponse.ts
1618
+ function from6(obligation) {
1619
+ return toSnakeCase({ id: Obligation_exports.id(obligation), ...obligation });
1620
+ }
1621
+
1622
+ // src/api/Api/Schema/OfferResponse.ts
1623
+ var OfferResponse_exports = {};
1624
+ __export(OfferResponse_exports, {
1625
+ from: () => from7
1626
+ });
1627
+ function from7(offer) {
1628
+ return toSnakeCase(offer);
1629
+ }
1630
+ var MAX_LIMIT = 100;
1631
+ var DEFAULT_LIMIT = 20;
1632
+ var PaginationQueryParams = z7__namespace.object({
1633
+ cursor: z7__namespace.string().optional().refine(
1634
+ (val) => {
1635
+ if (!val) return true;
1636
+ try {
1637
+ const decoded = Cursor_exports.decode(val);
1638
+ return decoded !== null;
1639
+ } catch (_error) {
1640
+ return false;
1641
+ }
1642
+ },
1643
+ {
1644
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1645
+ }
1646
+ ).meta({
1647
+ description: "Pagination cursor in base64url-encoded format",
1648
+ example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
1649
+ }),
1650
+ limit: z7__namespace.string().regex(/^[1-9]\d*$/, {
1651
+ message: "Limit must be a positive integer"
1652
+ }).transform((val) => Number.parseInt(val, 10)).pipe(
1653
+ z7__namespace.number().max(MAX_LIMIT, {
1654
+ message: `Limit cannot exceed ${MAX_LIMIT}`
1655
+ })
1656
+ ).optional().default(DEFAULT_LIMIT).meta({
1657
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
1658
+ example: 10
1659
+ })
1660
+ });
1661
+ var GetOffersQueryParams = z7__namespace.object({
1662
+ ...PaginationQueryParams.shape,
1663
+ side: z7__namespace.enum(["buy", "sell"]).meta({
1664
+ description: "Side of the offer.",
1665
+ example: "buy"
1666
+ }),
1667
+ obligation_id: z7__namespace.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
1668
+ description: "Offers obligation id",
1669
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1670
+ })
1671
+ });
1672
+ var GetObligationsQueryParams = z7__namespace.object({
1673
+ ...PaginationQueryParams.shape,
1674
+ cursor: z7__namespace.string().optional().meta({
1675
+ description: "Obligation id cursor",
1676
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1677
+ })
1678
+ });
1679
+ var schemas = {
1680
+ get_offers: GetOffersQueryParams,
1681
+ get_obligations: GetObligationsQueryParams
1682
+ };
1683
+ function parse(action, query) {
1684
+ return schemas[action].parse(query);
1685
+ }
1686
+ function safeParse(action, query, error) {
1687
+ return schemas[action].safeParse(query, {
1688
+ error
1689
+ });
1690
+ }
1691
+
1692
+ // src/api/Api/Schema/openapi.ts
1693
+ var successResponseSchema = v4.z.object({
587
1694
  status: v4.z.literal("success"),
588
1695
  cursor: v4.z.string().nullable(),
589
1696
  data: v4.z.array(v4.z.any()),
@@ -605,8 +1712,8 @@ var errorResponseSchema = v4.z.object({
605
1712
  var paths = {
606
1713
  "/v1/offers": {
607
1714
  get: {
608
- summary: "Get offers",
609
- description: "Get all offers with optional filtering and pagination",
1715
+ summary: "Offers",
1716
+ description: "Find offers that match specific criteria",
610
1717
  tags: ["Offers"],
611
1718
  requestParams: {
612
1719
  query: GetOffersQueryParams
@@ -631,13 +1738,13 @@ var paths = {
631
1738
  }
632
1739
  }
633
1740
  },
634
- "/v1/offers/match": {
1741
+ "/v1/obligations": {
635
1742
  get: {
636
- summary: "Match offers",
637
- description: "Find offers that match specific criteria",
638
- tags: ["Offers"],
1743
+ summary: "Obligations",
1744
+ description: "List obligations with pagination support",
1745
+ tags: ["Obligations"],
639
1746
  requestParams: {
640
- query: MatchOffersQueryParams
1747
+ query: GetObligationsQueryParams
641
1748
  },
642
1749
  responses: {
643
1750
  200: {
@@ -658,869 +1765,234 @@ var paths = {
658
1765
  }
659
1766
  }
660
1767
  }
661
- }
662
- };
663
- zodOpenapi.createDocument({
664
- openapi: "3.1.0",
665
- info: {
666
- title: "Router API",
667
- version: "1.0.0",
668
- description: "API for the Morpho Router"
669
1768
  },
670
- tags: [
671
- {
672
- name: "Offers"
1769
+ "/v1/health": {
1770
+ get: {
1771
+ summary: "Router status",
1772
+ description: "Retrieve the aggregated status of the router.",
1773
+ tags: ["Health"],
1774
+ responses: {
1775
+ 200: {
1776
+ description: "Success",
1777
+ content: {
1778
+ "application/json": {
1779
+ schema: RouterStatusResponse
1780
+ }
1781
+ }
1782
+ }
1783
+ }
673
1784
  }
674
- ],
675
- servers: [
676
- {
677
- url: "https://router.morpho.dev",
678
- description: "Production server"
679
- },
680
- {
681
- url: "http://localhost:8081",
682
- description: "Local development server"
1785
+ },
1786
+ "/v1/health/collectors": {
1787
+ get: {
1788
+ summary: "Collectors health",
1789
+ description: "Retrieve the block numbers processed by collectors and their sync status.",
1790
+ tags: ["Health"],
1791
+ responses: {
1792
+ 200: {
1793
+ description: "Success",
1794
+ content: {
1795
+ "application/json": {
1796
+ schema: CollectorsHealthResponse
1797
+ }
1798
+ }
1799
+ }
1800
+ }
683
1801
  }
684
- ],
685
- paths
686
- });
687
-
688
- // src/api/Schema/utils.ts
689
- function toResponse(routerOffer) {
690
- const { consumed, status, metadata, ...offer } = routerOffer;
691
- return {
692
- offer,
693
- consumed,
694
- status,
695
- metadata
696
- };
697
- }
698
- function fromResponse(offerResponse) {
699
- const { offer, consumed, status, metadata } = offerResponse;
700
- return {
701
- ...offer,
702
- consumed,
703
- status,
704
- metadata
705
- };
706
- }
707
-
708
- // src/api/Client.ts
709
- function connect(opts) {
710
- const u = new URL(opts?.url || "https://router.morpho.dev");
711
- if (u.protocol !== "http:" && u.protocol !== "https:") {
712
- throw new InvalidUrlError(u.toString());
713
- }
714
- const headers = opts?.headers ?? new Headers();
715
- headers.set("Content-Type", "application/json");
716
- opts?.apiKey !== void 0 ? headers.set("X-API-Key", opts.apiKey) : null;
717
- const config = {
718
- url: u,
719
- headers
720
- };
721
- return {
722
- ...config,
723
- get: (parameters) => get(config, parameters),
724
- match: (parameters) => match(config, parameters)
725
- };
726
- }
727
- async function get(config, parameters) {
728
- const url = new URL(`${config.url.toString()}v1/offers`);
729
- if (parameters.creators?.length) {
730
- url.searchParams.set("creators", parameters.creators.join(","));
731
- }
732
- if (parameters.side) {
733
- url.searchParams.set("side", parameters.side);
734
- }
735
- if (parameters.chains?.length) {
736
- url.searchParams.set("chains", parameters.chains.join(","));
737
- }
738
- if (parameters.loanTokens?.length) {
739
- url.searchParams.set("loan_tokens", parameters.loanTokens.join(","));
740
- }
741
- if (parameters.status?.length) {
742
- url.searchParams.set("status", parameters.status.join(","));
743
- }
744
- if (parameters.callbackAddresses?.length) {
745
- url.searchParams.set("callback_addresses", parameters.callbackAddresses.join(","));
746
- }
747
- if (parameters.minAmount !== void 0) {
748
- url.searchParams.set("min_amount", parameters.minAmount.toString());
749
- }
750
- if (parameters.maxAmount !== void 0) {
751
- url.searchParams.set("max_amount", parameters.maxAmount.toString());
752
- }
753
- if (parameters.minRate !== void 0) {
754
- url.searchParams.set("min_rate", parameters.minRate.toString());
755
- }
756
- if (parameters.maxRate !== void 0) {
757
- url.searchParams.set("max_rate", parameters.maxRate.toString());
758
- }
759
- if (parameters.minMaturity !== void 0) {
760
- url.searchParams.set("min_maturity", parameters.minMaturity.toString());
761
- }
762
- if (parameters.maxMaturity !== void 0) {
763
- url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
764
- }
765
- if (parameters.minExpiry !== void 0) {
766
- url.searchParams.set("min_expiry", parameters.minExpiry.toString());
767
- }
768
- if (parameters.maxExpiry !== void 0) {
769
- url.searchParams.set("max_expiry", parameters.maxExpiry.toString());
770
- }
771
- if (parameters.collateralAssets?.length) {
772
- url.searchParams.set("collateral_assets", parameters.collateralAssets.join(","));
773
- }
774
- if (parameters.collateralOracles?.length) {
775
- url.searchParams.set("collateral_oracles", parameters.collateralOracles.join(","));
776
- }
777
- if (parameters.collateralTuple?.length) {
778
- const tupleStr = parameters.collateralTuple.map(({ asset, oracle, lltv }) => {
779
- let result = asset;
780
- if (oracle) {
781
- result += `:${oracle}`;
782
- } else if (lltv !== void 0) {
783
- result += `:`;
784
- }
785
- if (lltv !== void 0) result += `:${viem.formatUnits(lltv, 16)}`;
786
- return result;
787
- }).join("#");
788
- url.searchParams.set("collateral_tuple", tupleStr);
789
- }
790
- if (parameters.minLltv !== void 0) {
791
- url.searchParams.set("min_lltv", viem.formatUnits(parameters.minLltv, 16));
792
- }
793
- if (parameters.maxLltv !== void 0) {
794
- url.searchParams.set("max_lltv", viem.formatUnits(parameters.maxLltv, 16));
795
- }
796
- if (parameters.sortBy) {
797
- url.searchParams.set("sort_by", parameters.sortBy);
798
- }
799
- if (parameters.sortOrder) {
800
- url.searchParams.set("sort_order", parameters.sortOrder);
801
- }
802
- if (parameters.cursor) {
803
- url.searchParams.set("cursor", parameters.cursor);
804
- }
805
- if (parameters.limit !== void 0) {
806
- url.searchParams.set("limit", parameters.limit.toString());
807
- }
808
- const { cursor: returnedCursor, data: offers } = await getApi(config, url);
809
- const routerOffers = offers.map(mempool.Format.fromSnakeCase).map(fromResponse);
810
- return {
811
- cursor: returnedCursor,
812
- offers: routerOffers.map(from).map(toResponse)
813
- };
814
- }
815
- async function match(config, parameters) {
816
- const url = new URL(`${config.url.toString()}v1/offers/match`);
817
- url.searchParams.set("side", parameters.side);
818
- url.searchParams.set("chain_id", parameters.chainId.toString());
819
- if (parameters.rate !== void 0) {
820
- url.searchParams.set("rate", parameters.rate.toString());
821
- }
822
- if (parameters.collaterals?.length) {
823
- const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${viem.formatUnits(lltv, 16)}`).join("#");
824
- url.searchParams.set("collaterals", collateralsStr);
825
- }
826
- if (parameters.maturity !== void 0) {
827
- url.searchParams.set("maturity", parameters.maturity.toString());
828
- }
829
- if (parameters.minMaturity !== void 0) {
830
- url.searchParams.set("min_maturity", parameters.minMaturity.toString());
831
- }
832
- if (parameters.maxMaturity !== void 0) {
833
- url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
834
- }
835
- if (parameters.loanToken) {
836
- url.searchParams.set("loan_token", parameters.loanToken);
837
- }
838
- if (parameters.creator) {
839
- url.searchParams.set("creator", parameters.creator);
840
- }
841
- if (parameters.status?.length) {
842
- url.searchParams.set("status", parameters.status.join(","));
843
- }
844
- if (parameters.cursor) {
845
- url.searchParams.set("cursor", parameters.cursor);
846
- }
847
- if (parameters.limit !== void 0) {
848
- url.searchParams.set("limit", parameters.limit.toString());
849
- }
850
- const { cursor: returnedCursor, data: offers } = await getApi(config, url);
851
- const routerOffers = offers.map(mempool.Format.fromSnakeCase).map(fromResponse);
852
- return {
853
- cursor: returnedCursor,
854
- offers: routerOffers.map(from).map(toResponse)
855
- };
856
- }
857
- async function getApi(config, url) {
858
- const pathname = url.pathname;
859
- let action;
860
- switch (true) {
861
- case pathname.includes("/v1/offers/match"):
862
- action = "match_offers";
863
- break;
864
- case pathname.includes("/v1/offers"):
865
- action = "get_offers";
866
- break;
867
- default:
868
- throw new HttpGetOffersFailedError("Unknown endpoint", {
869
- details: `Unsupported path: ${pathname}`
870
- });
871
- }
872
- const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
873
- if (!schemaParseResult.success) {
874
- throw new HttpGetOffersFailedError(`Invalid URL parameters`, {
875
- details: schemaParseResult.error.issues[0]?.message
876
- });
877
- }
878
- const response = await fetch(url.toString(), {
879
- method: "GET",
880
- headers: config.headers
881
- });
882
- if (!response.ok) {
883
- switch (response.status) {
884
- case 401:
885
- throw new HttpUnauthorizedError();
886
- case 403:
887
- throw new HttpForbiddenError();
888
- case 429:
889
- throw new HttpRateLimitError();
890
- }
891
- throw new HttpGetOffersFailedError(`GET request returned ${response.status}`, {
892
- details: await response.text()
893
- });
894
- }
895
- return response.json();
896
- }
897
- var InvalidUrlError = class extends mempool.Errors.BaseError {
898
- constructor(url) {
899
- super(`URL "${url}" is not http/https.`);
900
- __publicField(this, "name", "Router.InvalidUrlError");
901
- }
902
- };
903
- var HttpUnauthorizedError = class extends mempool.Errors.BaseError {
904
- constructor() {
905
- super("Unauthorized.", {
906
- metaMessages: ["Ensure that an API key is provided."]
907
- });
908
- __publicField(this, "name", "Router.HttpUnauthorizedError");
909
- }
910
- };
911
- var HttpForbiddenError = class extends mempool.Errors.BaseError {
912
- constructor() {
913
- super("Forbidden.", {
914
- metaMessages: ["Ensure that the API key is valid."]
915
- });
916
- __publicField(this, "name", "Router.HttpForbiddenError");
917
- }
918
- };
919
- var HttpRateLimitError = class extends mempool.Errors.BaseError {
920
- constructor() {
921
- super("Rate limit exceeded.", {
922
- metaMessages: [
923
- "The number of allowed requests has been exceeded. You must wait for the rate limit to reset."
924
- ]
925
- });
926
- __publicField(this, "name", "Router.HttpRateLimitError");
927
- }
928
- };
929
- var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
930
- constructor(message, { details } = {}) {
931
- super(message, {
932
- metaMessages: [details]
933
- });
934
- __publicField(this, "name", "Router.HttpGetOffersFailedError");
935
- }
936
- };
937
-
938
- // src/core/Callback.ts
939
- var Callback_exports = {};
940
- __export(Callback_exports, {
941
- CallbackType: () => CallbackType,
942
- WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
943
- buildLiquidity: () => buildLiquidity,
944
- decode: () => decode2,
945
- encode: () => encode2,
946
- getCallbackIdForOffer: () => getCallbackIdForOffer
947
- });
948
- var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
949
- CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
950
- CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
951
- return CallbackType2;
952
- })(CallbackType || {});
953
- var WhitelistedCallbackAddresses = {
954
- ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
955
- ["sell_erc20_callback" /* SellERC20Callback */]: [
956
- "0x1111111111111111111111111111111111111111",
957
- "0x2222222222222222222222222222222222222222"
958
- // @TODO: update once deployed and add mapping per chain if needed
959
- ].map((address) => address.toLowerCase())
960
- };
961
- function buildLiquidity(parameters) {
962
- switch (parameters.type) {
963
- case "buy_with_empty_callback" /* BuyWithEmptyCallback */: {
964
- const { user, loanToken, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
965
- const amountStr = amount.toString();
966
- const id = `${user}-${chainId.toString()}-${parameters.type}-${loanToken}`.toLowerCase();
967
- const poolId = `${user}-${chainId.toString()}-${loanToken}`.toLowerCase();
968
- return {
969
- userPosition: {
970
- id,
971
- availableLiquidityQueueId: id,
972
- user: user.toLowerCase(),
973
- chainId,
974
- amount: amountStr,
975
- updatedAt
976
- },
977
- queues: [
978
- {
979
- queue: {
980
- queueId: id,
981
- availableLiquidityPoolId: poolId,
982
- index,
983
- callbackAmount: "0",
984
- updatedAt
985
- },
986
- pool: {
987
- id: poolId,
988
- amount: amountStr,
989
- updatedAt
990
- }
991
- }
992
- ]
993
- };
994
- }
995
- case "sell_erc20_callback" /* SellERC20Callback */: {
996
- const {
997
- user,
998
- termId,
999
- offerHash,
1000
- chainId,
1001
- amount,
1002
- collaterals,
1003
- index = 0,
1004
- updatedAt = /* @__PURE__ */ new Date()
1005
- } = parameters;
1006
- const amountStr = amount.toString();
1007
- const id = `${user}-${chainId.toString()}-${parameters.type}-${termId}-${offerHash}`.toLowerCase();
1008
- return {
1009
- userPosition: {
1010
- id,
1011
- availableLiquidityQueueId: id,
1012
- user: user.toLowerCase(),
1013
- chainId,
1014
- amount: amountStr,
1015
- updatedAt
1016
- },
1017
- queues: collaterals.map((collateral) => {
1018
- const poolId = `${user}-${chainId.toString()}-${collateral.collateralAddress}`.toLowerCase();
1019
- return {
1020
- queue: {
1021
- queueId: id,
1022
- availableLiquidityPoolId: poolId,
1023
- index,
1024
- callbackAmount: collateral.callbackAmount.toString(),
1025
- updatedAt
1026
- },
1027
- pool: {
1028
- id: poolId,
1029
- amount: collateral.balance.toString(),
1030
- updatedAt
1031
- }
1032
- };
1033
- })
1034
- };
1035
- }
1036
- default: {
1037
- throw new Error(`CallbackType not implemented`);
1038
- }
1039
- }
1040
- }
1041
- function getCallbackIdForOffer(offer) {
1042
- if (offer.buy && offer.callback.data === "0x") {
1043
- return `${offer.offering}-${offer.chainId.toString()}-${"buy_with_empty_callback" /* BuyWithEmptyCallback */}-${offer.loanToken}`.toLowerCase();
1044
- }
1045
- if (!offer.buy && offer.callback.data !== "0x" && WhitelistedCallbackAddresses["sell_erc20_callback" /* SellERC20Callback */].includes(
1046
- offer.callback.address.toLowerCase()
1047
- )) {
1048
- return `${offer.offering}-${offer.chainId.toString()}-${"sell_erc20_callback" /* SellERC20Callback */}-${mempool.Offer.termId(offer)}-${offer.hash}`.toLowerCase();
1049
- }
1050
- return null;
1051
- }
1052
- function decodeSellERC20CallbackData(data) {
1053
- if (!data || data === "0x") throw new Error("Empty callback data");
1054
- try {
1055
- const [collaterals, amounts] = viem.decodeAbiParameters(
1056
- [{ type: "address[]" }, { type: "uint256[]" }],
1057
- data
1058
- );
1059
- if (collaterals.length !== amounts.length) {
1060
- throw new Error("Mismatched array lengths");
1061
- }
1062
- return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
1063
- } catch (_) {
1064
- throw new Error("Invalid SellERC20Callback callback data");
1065
- }
1066
- }
1067
- function decode2(parameters) {
1068
- const { type, data } = parameters;
1069
- if (type === "sell_erc20_callback" /* SellERC20Callback */) {
1070
- return decodeSellERC20CallbackData(data);
1071
- }
1072
- throw new Error(`CallbackType not implemented: ${type}`);
1073
- }
1074
- function encode2(parameters) {
1075
- const { type, data } = parameters;
1076
- if (type === "sell_erc20_callback" /* SellERC20Callback */) {
1077
- return viem.encodeAbiParameters(
1078
- [{ type: "address[]" }, { type: "uint256[]" }],
1079
- [data.collaterals, data.amounts]
1080
- );
1081
- }
1082
- throw new Error(`CallbackType not implemented: ${type}`);
1083
- }
1084
-
1085
- // src/core/Liquidity.ts
1086
- var Liquidity_exports = {};
1087
- __export(Liquidity_exports, {
1088
- create: () => create,
1089
- serialize: () => serialize
1090
- });
1091
-
1092
- // src/core/Abi.ts
1093
- var Oracle = [
1094
- {
1095
- type: "function",
1096
- name: "price",
1097
- inputs: [],
1098
- outputs: [{ name: "", type: "uint256" }],
1099
- stateMutability: "view"
1100
- }
1101
- ];
1102
- var Morpho = [
1103
- {
1104
- type: "function",
1105
- name: "collateralOf",
1106
- inputs: [
1107
- {
1108
- name: "",
1109
- type: "address",
1110
- internalType: "address"
1111
- },
1112
- {
1113
- name: "",
1114
- type: "bytes32",
1115
- internalType: "bytes32"
1116
- },
1117
- {
1118
- name: "",
1119
- type: "address",
1120
- internalType: "address"
1121
- }
1122
- ],
1123
- outputs: [
1124
- {
1125
- name: "",
1126
- type: "uint256",
1127
- internalType: "uint256"
1128
- }
1129
- ],
1130
- stateMutability: "view"
1131
1802
  },
1132
- {
1133
- type: "function",
1134
- name: "debtOf",
1135
- inputs: [
1136
- {
1137
- name: "",
1138
- type: "address",
1139
- internalType: "address"
1140
- },
1141
- {
1142
- name: "",
1143
- type: "bytes32",
1144
- internalType: "bytes32"
1145
- }
1146
- ],
1147
- outputs: [
1148
- {
1149
- name: "",
1150
- type: "uint256",
1151
- internalType: "uint256"
1152
- }
1153
- ],
1154
- stateMutability: "view"
1155
- }
1156
- ];
1157
-
1158
- // src/core/Fetchers.ts
1159
- async function fetchBalancesAndAllowances(parameters) {
1160
- const { client, spender, pairs, options } = parameters;
1161
- if (pairs.length === 0) return /* @__PURE__ */ new Map();
1162
- const batchSize = Math.max(1, options?.batchSize ?? 5e3);
1163
- const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
1164
- const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
1165
- const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
1166
- const out = /* @__PURE__ */ new Map();
1167
- for (const pairsBatch of mempool.Utils.batch(pairs, batchSize)) {
1168
- const balanceCalls = [];
1169
- const allowanceCalls = [];
1170
- for (const { user, token } of pairsBatch) {
1171
- balanceCalls.push({
1172
- address: token,
1173
- abi: viem.erc20Abi,
1174
- functionName: "balanceOf",
1175
- args: [user]
1176
- });
1177
- allowanceCalls.push({
1178
- address: token,
1179
- abi: viem.erc20Abi,
1180
- functionName: "allowance",
1181
- args: [user, spender]
1182
- });
1183
- }
1184
- const [balances, allowances] = await Promise.all([
1185
- mempool.Utils.retry(
1186
- () => client.multicall({
1187
- allowFailure: false,
1188
- contracts: balanceCalls,
1189
- ...blockNumber ? { blockNumber } : {}
1190
- }),
1191
- retryAttempts,
1192
- retryDelayMs
1193
- ),
1194
- mempool.Utils.retry(
1195
- () => client.multicall({
1196
- allowFailure: false,
1197
- contracts: allowanceCalls,
1198
- ...blockNumber ? { blockNumber } : {}
1199
- }),
1200
- retryAttempts,
1201
- retryDelayMs
1202
- )
1203
- ]);
1204
- for (let i = 0; i < pairsBatch.length; i++) {
1205
- const { user, token } = pairsBatch[i];
1206
- const balance = balances[i];
1207
- const allowance = allowances[i];
1208
- let perUser = out.get(user);
1209
- if (!perUser) {
1210
- perUser = /* @__PURE__ */ new Map();
1211
- out.set(user, perUser);
1212
- }
1213
- perUser.set(token, { balance, allowance });
1214
- }
1215
- }
1216
- return out;
1217
- }
1218
- async function fetchOraclePrices(parameters) {
1219
- const { client, oracles, options } = parameters;
1220
- if (oracles.length === 0) return /* @__PURE__ */ new Map();
1221
- const batchSize = Math.max(1, options?.batchSize ?? 5e3);
1222
- const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
1223
- const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
1224
- const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
1225
- const out = /* @__PURE__ */ new Map();
1226
- for (const oraclesBatch of mempool.Utils.batch(oracles, batchSize)) {
1227
- const priceCalls = [];
1228
- for (const oracle of oraclesBatch) {
1229
- priceCalls.push({
1230
- address: oracle,
1231
- abi: Oracle,
1232
- functionName: "price",
1233
- args: []
1234
- });
1235
- }
1236
- const prices = await mempool.Utils.retry(
1237
- () => client.multicall({
1238
- allowFailure: false,
1239
- contracts: priceCalls,
1240
- ...blockNumber ? { blockNumber } : {}
1241
- }),
1242
- retryAttempts,
1243
- retryDelayMs
1244
- );
1245
- for (let i = 0; i < oraclesBatch.length; i++) {
1246
- const oracle = oraclesBatch[i];
1247
- const price = prices[i];
1248
- out.set(oracle, price);
1803
+ "/v1/health/chains": {
1804
+ get: {
1805
+ summary: "Chains health",
1806
+ description: "Retrieve the latest block processed for each chain.",
1807
+ tags: ["Health"],
1808
+ responses: {
1809
+ 200: {
1810
+ description: "Success",
1811
+ content: {
1812
+ "application/json": {
1813
+ schema: ChainsHealthResponse
1814
+ }
1815
+ }
1816
+ }
1817
+ }
1249
1818
  }
1250
1819
  }
1251
- return out;
1252
- }
1253
- async function fetchCollateralAndDebt(parameters) {
1254
- const { client, morphoAddress, queries, options } = parameters;
1255
- if (queries.length === 0) return /* @__PURE__ */ new Map();
1256
- const batchSize = Math.max(1, options?.batchSize ?? 5e3);
1257
- const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
1258
- const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
1259
- const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
1260
- const out = /* @__PURE__ */ new Map();
1261
- for (const queriesBatch of mempool.Utils.batch(queries, batchSize)) {
1262
- const collateralCalls = [];
1263
- const debtCalls = [];
1264
- for (const { user, termId, collateralAssets } of queriesBatch) {
1265
- debtCalls.push({
1266
- address: morphoAddress,
1267
- abi: Morpho,
1268
- functionName: "debtOf",
1269
- args: [user, termId]
1270
- });
1271
- for (const collateralAsset of collateralAssets) {
1272
- collateralCalls.push({
1273
- address: morphoAddress,
1274
- abi: Morpho,
1275
- functionName: "collateralOf",
1276
- args: [user, termId, collateralAsset]
1277
- });
1278
- }
1820
+ };
1821
+ var OpenApi = zodOpenapi.createDocument({
1822
+ openapi: "3.1.0",
1823
+ info: {
1824
+ title: "Router API",
1825
+ version: "1.0.0",
1826
+ description: "API for the Morpho Router"
1827
+ },
1828
+ tags: [
1829
+ {
1830
+ name: "Offers"
1831
+ },
1832
+ {
1833
+ name: "Obligations"
1834
+ },
1835
+ {
1836
+ name: "Health"
1279
1837
  }
1280
- const [collateralResults, debtResults] = await Promise.all([
1281
- mempool.Utils.retry(
1282
- () => client.multicall({
1283
- allowFailure: false,
1284
- contracts: collateralCalls,
1285
- ...blockNumber ? { blockNumber } : {}
1286
- }),
1287
- retryAttempts,
1288
- retryDelayMs
1289
- ),
1290
- mempool.Utils.retry(
1291
- () => client.multicall({
1292
- allowFailure: false,
1293
- contracts: debtCalls,
1294
- ...blockNumber ? { blockNumber } : {}
1295
- }),
1296
- retryAttempts,
1297
- retryDelayMs
1298
- )
1299
- ]);
1300
- let collateralIndex = 0;
1301
- for (let queryIndex = 0; queryIndex < queriesBatch.length; queryIndex++) {
1302
- const { user, termId, collateralAssets } = queriesBatch[queryIndex];
1303
- const debt = debtResults[queryIndex];
1304
- const collateralByAsset = /* @__PURE__ */ new Map();
1305
- for (const collateralAsset of collateralAssets) {
1306
- const collateralAmount = collateralResults[collateralIndex];
1307
- collateralByAsset.set(collateralAsset.toLowerCase(), collateralAmount);
1308
- collateralIndex++;
1309
- }
1310
- let perUser = out.get(user);
1311
- if (!perUser) {
1312
- perUser = /* @__PURE__ */ new Map();
1313
- out.set(user, perUser);
1314
- }
1315
- perUser.set(termId, {
1316
- collateralByAsset,
1317
- debt
1318
- });
1838
+ ],
1839
+ servers: [
1840
+ {
1841
+ url: "https://router.morpho.dev",
1842
+ description: "Production server"
1843
+ },
1844
+ {
1845
+ url: "http://localhost:8081",
1846
+ description: "Local development server"
1319
1847
  }
1848
+ ],
1849
+ paths
1850
+ });
1851
+
1852
+ // src/api/Client.ts
1853
+ var Client_exports = {};
1854
+ __export(Client_exports, {
1855
+ HttpForbiddenError: () => HttpForbiddenError,
1856
+ HttpGetApiFailedError: () => HttpGetApiFailedError,
1857
+ HttpRateLimitError: () => HttpRateLimitError,
1858
+ HttpUnauthorizedError: () => HttpUnauthorizedError,
1859
+ InvalidUrlError: () => InvalidUrlError,
1860
+ connect: () => connect,
1861
+ getObligations: () => getObligations,
1862
+ getOffers: () => getOffers
1863
+ });
1864
+ function connect(options) {
1865
+ const u = new URL(options?.url || "https://router.morpho.dev");
1866
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
1867
+ throw new InvalidUrlError(u.toString());
1320
1868
  }
1321
- return out;
1869
+ const headers = options?.headers ?? new Headers();
1870
+ headers.set("Content-Type", "application/json");
1871
+ options?.apiKey !== void 0 ? headers.set("X-API-Key", options.apiKey) : null;
1872
+ const config = {
1873
+ url: u,
1874
+ headers
1875
+ };
1876
+ return {
1877
+ ...config,
1878
+ getOffers: (parameters) => getOffers(config, parameters),
1879
+ getObligations: (parameters) => getObligations(config, parameters)
1880
+ };
1322
1881
  }
1323
-
1324
- // src/core/Liquidity.ts
1325
- async function fetchMaxBorrowCapacity(parameters) {
1326
- const { client, collaterals, existingDebt, options } = parameters;
1327
- if (collaterals.length === 0) return 0n;
1328
- const uniqueOracles = [...new Set(collaterals.map((c) => c.oracle))];
1329
- const oraclePrices = await fetchOraclePrices({
1330
- client,
1331
- oracles: uniqueOracles,
1332
- options
1333
- });
1334
- const ORACLE_PRICE_SCALE = 10n ** 36n;
1335
- const PRECISION = 10n ** 18n;
1336
- let maxDebt = 0n;
1337
- for (const collateral of collaterals) {
1338
- const price = oraclePrices.get(collateral.oracle);
1339
- if (!price) continue;
1340
- const collateralQuoted = collateral.totalAmount * price / ORACLE_PRICE_SCALE;
1341
- const collateralMaxDebt = collateralQuoted * collateral.lltv / PRECISION;
1342
- maxDebt = maxDebt + collateralMaxDebt;
1343
- }
1344
- if (existingDebt > 0n) {
1345
- if (maxDebt > existingDebt) {
1346
- maxDebt = maxDebt - existingDebt;
1347
- } else {
1348
- maxDebt = 0n;
1349
- }
1882
+ async function getOffers(config, parameters) {
1883
+ const url = new URL(`${config.url.toString()}v1/offers`);
1884
+ url.searchParams.set("side", parameters.side);
1885
+ url.searchParams.set("obligation_id", parameters.obligationId.toString());
1886
+ if (parameters.cursor) {
1887
+ url.searchParams.set("cursor", parameters.cursor);
1350
1888
  }
1351
- return maxDebt;
1889
+ if (parameters.limit !== void 0) {
1890
+ url.searchParams.set("limit", parameters.limit.toString());
1891
+ }
1892
+ const { cursor: returnedCursor, data: offers } = await getApi(config, url);
1893
+ const routerOffers = offers.map(Offer_exports.fromSnakeCase);
1894
+ return {
1895
+ cursor: returnedCursor,
1896
+ offers: routerOffers
1897
+ };
1352
1898
  }
1353
- var create = (config) => {
1354
- const { client } = config;
1899
+ async function getObligations(config, parameters) {
1900
+ const url = new URL(`${config.url.toString()}v1/obligations`);
1901
+ if (parameters?.cursor !== void 0) {
1902
+ url.searchParams.set("cursor", parameters.cursor);
1903
+ }
1904
+ if (parameters?.limit !== void 0) {
1905
+ url.searchParams.set("limit", parameters.limit.toString());
1906
+ }
1907
+ const { cursor: returnedCursor, data: obligationsSnake } = await getApi(config, url);
1908
+ const obligations = obligationsSnake.map(Obligation_exports.fromSnakeCase);
1355
1909
  return {
1356
- fetchBuyLiquidity: (parameters) => fetchBuyLiquidity({ ...parameters, client }),
1357
- fetchSellLiquidity: (parameters) => fetchSellLiquidity({ ...parameters, client })
1910
+ cursor: returnedCursor,
1911
+ obligations
1358
1912
  };
1359
- };
1360
- async function fetchBuyLiquidity(parameters) {
1361
- const { client, chainId, spender, pairs, options } = parameters;
1362
- const map = await fetchBalancesAndAllowances({
1363
- client,
1364
- spender,
1365
- pairs: pairs.map(({ user, loanToken }) => ({ user, token: loanToken })),
1366
- options
1913
+ }
1914
+ async function getApi(config, url) {
1915
+ const pathname = url.pathname;
1916
+ let action;
1917
+ switch (true) {
1918
+ case pathname.includes("/v1/offers"):
1919
+ action = "get_offers";
1920
+ break;
1921
+ case pathname.includes("/v1/obligations"):
1922
+ action = "get_obligations";
1923
+ break;
1924
+ default:
1925
+ throw new HttpGetApiFailedError("Unknown endpoint", {
1926
+ details: `Unsupported path: ${pathname}`
1927
+ });
1928
+ }
1929
+ const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
1930
+ if (!schemaParseResult.success) {
1931
+ throw new HttpGetApiFailedError(`Invalid URL parameters`, {
1932
+ details: schemaParseResult.error.issues[0]?.message
1933
+ });
1934
+ }
1935
+ const response = await fetch(url.toString(), {
1936
+ method: "GET",
1937
+ headers: config.headers
1367
1938
  });
1368
- const out = [];
1369
- for (const [user, perLoanToken] of map) {
1370
- for (const [loanToken, { balance, allowance }] of perLoanToken) {
1371
- const amount = balance < allowance ? balance : allowance;
1372
- out.push(
1373
- buildLiquidity({
1374
- type: "buy_with_empty_callback" /* BuyWithEmptyCallback */,
1375
- user,
1376
- loanToken,
1377
- chainId,
1378
- amount,
1379
- index: 0
1380
- })
1381
- );
1939
+ if (!response.ok) {
1940
+ switch (response.status) {
1941
+ case 401:
1942
+ throw new HttpUnauthorizedError();
1943
+ case 403:
1944
+ throw new HttpForbiddenError();
1945
+ case 429:
1946
+ throw new HttpRateLimitError();
1382
1947
  }
1948
+ throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
1949
+ details: await response.text()
1950
+ });
1383
1951
  }
1384
- return out;
1952
+ return response.json();
1385
1953
  }
1386
- async function fetchSellLiquidity(parameters) {
1387
- const { client, chainId, spender, morphoAddress, offers, options } = parameters;
1388
- const collateralPairs = [];
1389
- for (const offer of offers) {
1390
- const user = offer.offering;
1391
- const callbackData = decode2({
1392
- type: "sell_erc20_callback" /* SellERC20Callback */,
1393
- data: offer.callback.data
1394
- });
1395
- for (const { collateral } of callbackData) {
1396
- collateralPairs.push({ user, token: collateral });
1397
- }
1954
+ var InvalidUrlError = class extends BaseError {
1955
+ constructor(url) {
1956
+ super(`URL "${url}" is not http/https.`);
1957
+ __publicField(this, "name", "Router.InvalidUrlError");
1398
1958
  }
1399
- const map = await fetchBalancesAndAllowances({
1400
- client,
1401
- spender,
1402
- pairs: collateralPairs,
1403
- options
1404
- });
1405
- const collateralDebt = [];
1406
- for (const offer of offers) {
1407
- const user = offer.offering;
1408
- const termId = mempool.Offer.termId(offer);
1409
- const collateralAssets = offer.collaterals.map((collateral) => collateral.asset);
1410
- collateralDebt.push({
1411
- user,
1412
- termId,
1413
- collateralAssets
1959
+ };
1960
+ var HttpUnauthorizedError = class extends BaseError {
1961
+ constructor() {
1962
+ super("Unauthorized.", {
1963
+ metaMessages: ["Ensure that an API key is provided."]
1414
1964
  });
1965
+ __publicField(this, "name", "Router.HttpUnauthorizedError");
1415
1966
  }
1416
- const existingPositions = await fetchCollateralAndDebt({
1417
- client,
1418
- morphoAddress,
1419
- queries: collateralDebt,
1420
- options
1421
- });
1422
- const out = [];
1423
- for (const offer of offers) {
1424
- const user = offer.offering;
1425
- const termId = mempool.Offer.termId(offer);
1426
- const callbackData = decode2({
1427
- type: "sell_erc20_callback" /* SellERC20Callback */,
1428
- data: offer.callback.data
1967
+ };
1968
+ var HttpForbiddenError = class extends BaseError {
1969
+ constructor() {
1970
+ super("Forbidden.", {
1971
+ metaMessages: ["Ensure that the API key is valid."]
1429
1972
  });
1430
- const callbackCollaterals = callbackData.map((callbackItem) => {
1431
- const userMap = map.get(user);
1432
- const tokenData = userMap?.get(callbackItem.collateral);
1433
- if (!tokenData) return null;
1434
- const offerCollateral = offer.collaterals.find(
1435
- (c) => c.asset.toLowerCase() === callbackItem.collateral.toLowerCase()
1436
- );
1437
- if (!offerCollateral) return null;
1438
- const { balance, allowance } = tokenData;
1439
- const availableAmount = balance < allowance ? balance : allowance;
1440
- return {
1441
- collateralAddress: callbackItem.collateral,
1442
- balance: availableAmount,
1443
- callbackAmount: callbackItem.amount
1444
- };
1445
- }).filter((collateral) => collateral !== null);
1446
- if (callbackCollaterals.length === 0) continue;
1447
- const existingPosition = existingPositions.get(user)?.get(termId);
1448
- const collaterals = offer.collaterals.map((c) => {
1449
- const callbackCollateral = callbackCollaterals.find(
1450
- (cc) => cc.collateralAddress.toLowerCase() === c.asset.toLowerCase()
1451
- );
1452
- const callbackAmount = callbackCollateral?.balance ?? 0n;
1453
- const existingAmount = existingPosition?.collateralByAsset.get(c.asset.toLowerCase()) ?? 0n;
1454
- return {
1455
- asset: c.asset,
1456
- oracle: c.oracle,
1457
- lltv: c.lltv,
1458
- totalAmount: existingAmount + callbackAmount
1459
- };
1973
+ __publicField(this, "name", "Router.HttpForbiddenError");
1974
+ }
1975
+ };
1976
+ var HttpRateLimitError = class extends BaseError {
1977
+ constructor() {
1978
+ super("Rate limit exceeded.", {
1979
+ metaMessages: [
1980
+ "The number of allowed requests has been exceeded. You must wait for the rate limit to reset."
1981
+ ]
1460
1982
  });
1461
- const existingDebt = existingPosition?.debt ?? 0n;
1462
- const userPositionAmount = await fetchMaxBorrowCapacity({
1463
- client,
1464
- collaterals,
1465
- existingDebt,
1466
- options
1983
+ __publicField(this, "name", "Router.HttpRateLimitError");
1984
+ }
1985
+ };
1986
+ var HttpGetApiFailedError = class extends BaseError {
1987
+ constructor(message, { details } = {}) {
1988
+ super(message, {
1989
+ metaMessages: [details]
1467
1990
  });
1468
- out.push(
1469
- buildLiquidity({
1470
- type: "sell_erc20_callback" /* SellERC20Callback */,
1471
- user,
1472
- termId,
1473
- offerHash: offer.hash,
1474
- chainId,
1475
- amount: userPositionAmount,
1476
- collaterals: callbackCollaterals,
1477
- index: 0
1478
- })
1479
- );
1991
+ __publicField(this, "name", "Router.HttpGetApiFailedError");
1480
1992
  }
1481
- return out;
1482
- }
1483
- function serialize(liquidity) {
1484
- const normalized = {
1485
- userPosition: {
1486
- id: liquidity.userPosition.id,
1487
- availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
1488
- user: liquidity.userPosition.user,
1489
- chainId: String(liquidity.userPosition.chainId),
1490
- amount: String(liquidity.userPosition.amount)
1491
- },
1492
- queues: liquidity.queues.map((queueWithPool) => ({
1493
- queue: {
1494
- queueId: queueWithPool.queue.queueId,
1495
- availableLiquidityPoolId: queueWithPool.queue.availableLiquidityPoolId,
1496
- index: queueWithPool.queue.index
1497
- },
1498
- pool: {
1499
- id: queueWithPool.pool.id,
1500
- amount: String(queueWithPool.pool.amount)
1501
- }
1502
- })).sort(
1503
- (left, right) => {
1504
- const leftQueueId = left.queue.queueId || "";
1505
- const rightQueueId = right.queue.queueId || "";
1506
- if (leftQueueId < rightQueueId) return -1;
1507
- if (leftQueueId > rightQueueId) return 1;
1508
- const leftPoolId = left.pool.id;
1509
- const rightPoolId = right.pool.id;
1510
- if (leftPoolId < rightPoolId) return -1;
1511
- if (leftPoolId > rightPoolId) return 1;
1512
- const leftIndex = left.queue.index;
1513
- const rightIndex = right.queue.index;
1514
- if (leftIndex < rightIndex) return -1;
1515
- if (leftIndex > rightIndex) return 1;
1516
- return 0;
1517
- }
1518
- )
1519
- };
1520
- return JSON.stringify(normalized);
1521
- }
1993
+ };
1522
1994
 
1523
- // src/core/Validation.ts
1995
+ // src/collectors/validations/Validation.ts
1524
1996
  var Validation_exports = {};
1525
1997
  __export(Validation_exports, {
1526
1998
  run: () => run
@@ -1567,17 +2039,17 @@ async function run(parameters) {
1567
2039
  };
1568
2040
  }
1569
2041
 
1570
- // src/core/ValidationRule.ts
2042
+ // src/collectors/validations/ValidationRule.ts
1571
2043
  var ValidationRule_exports = {};
1572
2044
  __export(ValidationRule_exports, {
1573
- batch: () => batch,
2045
+ batch: () => batch2,
1574
2046
  morpho: () => morpho,
1575
2047
  single: () => single
1576
2048
  });
1577
2049
  function single(name, run2) {
1578
2050
  return { kind: "single", name, run: run2 };
1579
2051
  }
1580
- function batch(name, run2) {
2052
+ function batch2(name, run2) {
1581
2053
  return { kind: "batch", name, run: run2 };
1582
2054
  }
1583
2055
  function morpho() {
@@ -1615,7 +2087,17 @@ function morpho() {
1615
2087
  "buy_offers_non_empty_callback",
1616
2088
  (offer, _) => {
1617
2089
  if (offer.buy && offer.callback.data !== "0x") {
1618
- return { message: "Buy offers must use an empty callback." };
2090
+ const allowed = new Set(
2091
+ Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.BuyVaultV1Callback].map(
2092
+ (a) => a.toLowerCase()
2093
+ )
2094
+ );
2095
+ const callbackAddress = offer.callback.address?.toLowerCase();
2096
+ if (!callbackAddress || !allowed.has(callbackAddress)) {
2097
+ return {
2098
+ message: "Buy offers with non-empty callback must use a whitelisted BuyVaultV1Callback."
2099
+ };
2100
+ }
1619
2101
  }
1620
2102
  }
1621
2103
  );
@@ -1624,7 +2106,7 @@ function morpho() {
1624
2106
  (offer, _) => {
1625
2107
  if (!offer.buy && offer.callback.data !== "0x") {
1626
2108
  const allowed = new Set(
1627
- WhitelistedCallbackAddresses["sell_erc20_callback" /* SellERC20Callback */].map(
2109
+ Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.SellERC20Callback].map(
1628
2110
  (a) => a.toLowerCase()
1629
2111
  )
1630
2112
  );
@@ -1640,10 +2122,7 @@ function morpho() {
1640
2122
  (offer, _) => {
1641
2123
  if (!offer.buy && offer.callback.data !== "0x") {
1642
2124
  try {
1643
- const decoded = decode2({
1644
- type: "sell_erc20_callback" /* SellERC20Callback */,
1645
- data: offer.callback.data
1646
- });
2125
+ const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
1647
2126
  if (decoded.length === 0) {
1648
2127
  return { message: "Sell offer callback data must include at least one collateral." };
1649
2128
  }
@@ -1658,10 +2137,7 @@ function morpho() {
1658
2137
  (offer, _) => {
1659
2138
  if (!offer.buy && offer.callback.data !== "0x") {
1660
2139
  try {
1661
- const decoded = decode2({
1662
- type: "sell_erc20_callback" /* SellERC20Callback */,
1663
- data: offer.callback.data
1664
- });
2140
+ const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
1665
2141
  const offerCollaterals = new Set(
1666
2142
  offer.collaterals.map((c) => c.asset.toLowerCase())
1667
2143
  );
@@ -1675,8 +2151,128 @@ function morpho() {
1675
2151
  }
1676
2152
  }
1677
2153
  );
2154
+ const buyCallbackDataInvalid = single(
2155
+ "buy_offers_callback_data_invalid",
2156
+ (offer, _) => {
2157
+ if (offer.buy && offer.callback.data !== "0x") {
2158
+ try {
2159
+ const decoded = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2160
+ if (decoded.length === 0) {
2161
+ return { message: "Buy offer callback data must include at least one vault." };
2162
+ }
2163
+ } catch (_2) {
2164
+ return { message: "Buy offer callback data cannot be decoded." };
2165
+ }
2166
+ }
2167
+ }
2168
+ );
2169
+ const buyCallbackVaultInvalid = batch2(
2170
+ "buy_offers_callback_vault_invalid",
2171
+ async (offers, { client, chain }) => {
2172
+ const validationIssues = /* @__PURE__ */ new Map();
2173
+ const offersByVaultAddress = /* @__PURE__ */ new Map();
2174
+ for (let i = 0; i < offers.length; i++) {
2175
+ const offer = offers[i];
2176
+ if (offer.buy && offer.callback.data !== "0x") {
2177
+ try {
2178
+ const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2179
+ for (const { vault } of callbackVaults) {
2180
+ const normalizedVaultAddress = vault.toLowerCase();
2181
+ if (!offersByVaultAddress.has(normalizedVaultAddress)) {
2182
+ offersByVaultAddress.set(normalizedVaultAddress, []);
2183
+ }
2184
+ offersByVaultAddress.get(normalizedVaultAddress).push({ index: i, offer });
2185
+ }
2186
+ } catch (_) {
2187
+ }
2188
+ }
2189
+ }
2190
+ const uniqueVaultAddresses = Array.from(offersByVaultAddress.keys());
2191
+ if (uniqueVaultAddresses.length === 0) return validationIssues;
2192
+ const whitelistedFactories = Object.values(chain.vaultV1Factory);
2193
+ const multicallContracts = [];
2194
+ for (const vaultAddress of uniqueVaultAddresses) {
2195
+ multicallContracts.push({
2196
+ address: vaultAddress,
2197
+ abi: Abi_exports.ERC4626,
2198
+ functionName: "asset"
2199
+ });
2200
+ for (const factoryAddress of whitelistedFactories) {
2201
+ multicallContracts.push({
2202
+ address: factoryAddress,
2203
+ abi: Abi_exports.MetaMorphoFactory,
2204
+ functionName: "isMetaMorpho",
2205
+ args: [vaultAddress]
2206
+ });
2207
+ }
2208
+ }
2209
+ const multicallResults = await client.multicall({
2210
+ contracts: multicallContracts,
2211
+ allowFailure: true
2212
+ });
2213
+ const vaultAssetByAddress = /* @__PURE__ */ new Map();
2214
+ const registeredVaults = /* @__PURE__ */ new Set();
2215
+ const numberOfFactories = whitelistedFactories.length;
2216
+ let resultIndex = 0;
2217
+ for (const vaultAddress of uniqueVaultAddresses) {
2218
+ const assetCallResult = multicallResults[resultIndex++];
2219
+ const assetAddress = assetCallResult.status === "success" ? assetCallResult.result : null;
2220
+ vaultAssetByAddress.set(vaultAddress, assetAddress);
2221
+ let isRegisteredInFactory = false;
2222
+ for (let factoryIndex = 0; factoryIndex < numberOfFactories; factoryIndex++) {
2223
+ const factoryCallResult = multicallResults[resultIndex++];
2224
+ if (factoryCallResult.status === "success" && factoryCallResult.result === true) {
2225
+ isRegisteredInFactory = true;
2226
+ }
2227
+ }
2228
+ if (isRegisteredInFactory) {
2229
+ registeredVaults.add(vaultAddress);
2230
+ }
2231
+ }
2232
+ const uniqueOffers = /* @__PURE__ */ new Map();
2233
+ for (const offersArray of offersByVaultAddress.values()) {
2234
+ for (const { index, offer } of offersArray) {
2235
+ uniqueOffers.set(index, offer);
2236
+ }
2237
+ }
2238
+ for (const [index, offer] of uniqueOffers) {
2239
+ try {
2240
+ const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
2241
+ const vaultsWithIssues = [];
2242
+ for (const { vault } of callbackVaults) {
2243
+ const normalizedVaultAddress = vault.toLowerCase();
2244
+ const assetAddress = vaultAssetByAddress.get(normalizedVaultAddress);
2245
+ const isRegistered = registeredVaults.has(normalizedVaultAddress);
2246
+ const failureReasons = [];
2247
+ if (assetAddress === null) {
2248
+ failureReasons.push("asset call failed");
2249
+ } else if (assetAddress && assetAddress.toLowerCase() !== offer.loanToken.toLowerCase()) {
2250
+ failureReasons.push("asset mismatch");
2251
+ }
2252
+ if (!isRegistered) {
2253
+ failureReasons.push("not registered in factory");
2254
+ }
2255
+ if (failureReasons.length > 0) {
2256
+ vaultsWithIssues.push({
2257
+ vaultAddress: vault,
2258
+ failureReasons: failureReasons.join(", ")
2259
+ });
2260
+ }
2261
+ }
2262
+ if (vaultsWithIssues.length > 0) {
2263
+ const failureDetails = vaultsWithIssues.map((v) => `${v.vaultAddress} (${v.failureReasons})`).join("; ");
2264
+ validationIssues.set(index, {
2265
+ message: `Buy offer callback vaults are invalid: ${failureDetails}`
2266
+ });
2267
+ }
2268
+ } catch (_) {
2269
+ }
2270
+ }
2271
+ return validationIssues;
2272
+ }
2273
+ );
1678
2274
  const maturity = single("maturity", (offer, _) => {
1679
- const allowedMaturities = [mempool.Maturity.from("end_of_month"), mempool.Maturity.from("end_of_next_month")];
2275
+ const allowedMaturities = [Maturity_exports.from("end_of_month"), Maturity_exports.from("end_of_next_month")];
1680
2276
  if (!allowedMaturities.includes(offer.maturity)) {
1681
2277
  return {
1682
2278
  message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
@@ -1694,22 +2290,186 @@ function morpho() {
1694
2290
  buyNonEmptyCallback,
1695
2291
  sellNonWhitelistedCallback,
1696
2292
  sellCallbackDataInvalid,
1697
- sellCallbackCollateralInvalid
2293
+ sellCallbackCollateralInvalid,
2294
+ buyCallbackDataInvalid,
2295
+ buyCallbackVaultInvalid
1698
2296
  ];
1699
2297
  }
1700
2298
 
2299
+ // src/mempool/MempoolClient.ts
2300
+ var MempoolClient_exports = {};
2301
+ __export(MempoolClient_exports, {
2302
+ connect: () => connect2
2303
+ });
2304
+ var DEFAULT_BATCH_SIZE2 = 100;
2305
+ var DEFAULT_BLOCK_WINDOW2 = 100;
2306
+ function from8(parameters) {
2307
+ const config = {
2308
+ client: parameters.client,
2309
+ mempoolAddress: parameters.mempoolAddress,
2310
+ blockWindow: parameters.blockWindow || DEFAULT_BLOCK_WINDOW2
2311
+ };
2312
+ return {
2313
+ add: (parameters2) => add(config, parameters2),
2314
+ get: (parameters2) => get(config, parameters2),
2315
+ watch: (parameters2) => watch(config, parameters2),
2316
+ stream: (parameters2) => streamOffers(config, parameters2)
2317
+ };
2318
+ }
2319
+ async function add(config, parameters) {
2320
+ const offer = Offer_exports.from(parameters.offer);
2321
+ if (!config.client.account) throw new WalletAccountNotSetError();
2322
+ const chainId = await getChainId(config.client);
2323
+ if (BigInt(chainId) !== offer.chainId)
2324
+ throw new ChainIdMismatchError(offer.chainId, BigInt(chainId));
2325
+ try {
2326
+ const tx = await config.client.sendTransaction({
2327
+ chain: config.client.chain,
2328
+ account: config.client.account,
2329
+ to: config.mempoolAddress,
2330
+ data: Offer_exports.encode(offer)
2331
+ });
2332
+ return { offer, txHash: tx };
2333
+ } catch (error) {
2334
+ throw new ViemClientError(error instanceof Error ? error.message : "Unknown error");
2335
+ }
2336
+ }
2337
+ async function* get(config, parameters) {
2338
+ const {
2339
+ loanToken,
2340
+ blockNumberGte,
2341
+ blockNumberLte,
2342
+ order = "desc",
2343
+ options: { maxBatchSize = DEFAULT_BATCH_SIZE2 } = {}
2344
+ } = parameters || {};
2345
+ yield* streamOffers(config, {
2346
+ loanToken,
2347
+ order,
2348
+ blockNumberGte,
2349
+ blockNumberLte,
2350
+ options: { maxBatchSize, blockWindow: config.blockWindow }
2351
+ });
2352
+ }
2353
+ function watch(config, parameters) {
2354
+ const {
2355
+ loanToken,
2356
+ lastSyncedBlock,
2357
+ polling: { interval = 3e4, maxBatchSize = DEFAULT_BATCH_SIZE2 } = {},
2358
+ onOffers
2359
+ } = parameters;
2360
+ return poll(
2361
+ async () => {
2362
+ const blockNumberGte = await lastSyncedBlock();
2363
+ const stream = streamOffers(config, {
2364
+ loanToken,
2365
+ order: "asc",
2366
+ blockNumberGte,
2367
+ options: { maxBatchSize, blockWindow: config.blockWindow }
2368
+ });
2369
+ for await (const { offers, blockNumber } of stream) {
2370
+ await onOffers(offers, blockNumber);
2371
+ }
2372
+ },
2373
+ { interval }
2374
+ );
2375
+ }
2376
+ var chainIdCache = /* @__PURE__ */ new Map();
2377
+ var getChainId = async (client) => {
2378
+ if (chainIdCache.has(client.uid)) return chainIdCache.get(client.uid);
2379
+ const chainId = await client.getChainId();
2380
+ chainIdCache.set(client.uid, chainId);
2381
+ return chainId;
2382
+ };
2383
+ async function* streamOffers(config, parameters) {
2384
+ const {
2385
+ loanToken,
2386
+ blockNumberGte,
2387
+ blockNumberLte,
2388
+ order = "desc",
2389
+ options: { maxBatchSize = DEFAULT_BATCH_SIZE2, blockWindow = config.blockWindow } = {}
2390
+ } = parameters;
2391
+ const stream = Chain_exports.streamLogs({
2392
+ client: config.client.extend(viem.publicActions),
2393
+ contractAddress: config.mempoolAddress,
2394
+ event: {
2395
+ type: "event",
2396
+ name: "Event",
2397
+ inputs: [{ name: "data", type: "bytes", indexed: false, internalType: "bytes" }],
2398
+ anonymous: false
2399
+ },
2400
+ blockNumberGte,
2401
+ blockNumberLte,
2402
+ order,
2403
+ options: { maxBatchSize, blockWindow }
2404
+ });
2405
+ let blockNumber = order === "asc" ? blockNumberGte : blockNumberLte;
2406
+ for await (const { logs, blockNumber: newBlockNumber } of stream) {
2407
+ blockNumber = newBlockNumber;
2408
+ if (logs.length === 0) break;
2409
+ let offersAndBlockNumbers = logs.map((log) => {
2410
+ const [payload] = viem.decodeAbiParameters([{ type: "bytes" }], log.data);
2411
+ try {
2412
+ return { offer: Offer_exports.decode(payload, log.blockNumber), blockNumber: log.blockNumber };
2413
+ } catch (_) {
2414
+ return null;
2415
+ }
2416
+ }).filter((item) => item !== null);
2417
+ if (loanToken)
2418
+ offersAndBlockNumbers = offersAndBlockNumbers.filter(
2419
+ (o) => o.offer.loanToken.toLowerCase() === loanToken.toLowerCase()
2420
+ );
2421
+ if (offersAndBlockNumbers.length === 0) continue;
2422
+ yield {
2423
+ offers: offersAndBlockNumbers.map((item) => item.offer),
2424
+ blockNumber
2425
+ };
2426
+ }
2427
+ yield { offers: [], blockNumber };
2428
+ return;
2429
+ }
2430
+ var WalletAccountNotSetError = class extends BaseError {
2431
+ constructor() {
2432
+ super("Wallet account is not set.");
2433
+ __publicField(this, "name", "Mempool.WalletAccountNotSetError");
2434
+ }
2435
+ };
2436
+ var ViemClientError = class extends BaseError {
2437
+ constructor() {
2438
+ super(...arguments);
2439
+ __publicField(this, "name", "Mempool.ViemClientError");
2440
+ }
2441
+ };
2442
+ var ChainIdMismatchError = class extends BaseError {
2443
+ constructor(expected, actual) {
2444
+ super(`Chain ID mismatch. Offer chain ID is ${expected}, network chain ID is ${actual}.`);
2445
+ __publicField(this, "name", "Mempool.ChainIdMismatchError");
2446
+ }
2447
+ };
2448
+
2449
+ // src/mempool/MempoolClient.ts
2450
+ function connect2(parameters) {
2451
+ return from8(parameters);
2452
+ }
2453
+
2454
+ exports.Abi = Abi_exports;
2455
+ exports.BrandTypeId = BrandTypeId;
1701
2456
  exports.Callback = Callback_exports;
2457
+ exports.Chain = Chain_exports;
2458
+ exports.Collateral = Collateral_exports;
1702
2459
  exports.Cursor = Cursor_exports;
2460
+ exports.Errors = Errors_exports;
2461
+ exports.Format = Format_exports;
2462
+ exports.LLTV = LLTV_exports;
1703
2463
  exports.Liquidity = Liquidity_exports;
1704
- exports.RouterApi = Client_exports;
1705
- exports.RouterOffer = RouterOffer_exports;
2464
+ exports.Maturity = Maturity_exports;
2465
+ exports.Mempool = MempoolClient_exports;
2466
+ exports.Obligation = Obligation_exports;
2467
+ exports.Offer = Offer_exports;
2468
+ exports.RouterApi = Schema_exports;
2469
+ exports.RouterClient = Client_exports;
2470
+ exports.Time = time_exports;
2471
+ exports.Utils = utils_exports;
1706
2472
  exports.Validation = Validation_exports;
1707
2473
  exports.ValidationRule = ValidationRule_exports;
1708
- Object.keys(mempool).forEach(function (k) {
1709
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
1710
- enumerable: true,
1711
- get: function () { return mempool[k]; }
1712
- });
1713
- });
1714
2474
  //# sourceMappingURL=index.browser.js.map
1715
2475
  //# sourceMappingURL=index.browser.js.map