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