@morpho-dev/router 0.1.11 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +97 -64
  2. package/dist/cli.js +6043 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/drizzle/router_v1.4/0000_add_obligation_id.sql +112 -0
  5. package/dist/drizzle/router_v1.4/0001_update-primary-key-on-link.sql +3 -0
  6. package/dist/drizzle/{offers_v1.1/meta/0006_snapshot.json → router_v1.4/meta/0000_snapshot.json} +344 -256
  7. package/dist/drizzle/{offers_v1.1/meta/0007_snapshot.json → router_v1.4/meta/0001_snapshot.json} +306 -266
  8. package/dist/drizzle/router_v1.4/meta/_journal.json +20 -0
  9. package/dist/index.browser.d.cts +1367 -559
  10. package/dist/index.browser.d.ts +1367 -559
  11. package/dist/index.browser.js +1911 -835
  12. package/dist/index.browser.js.map +1 -1
  13. package/dist/index.browser.mjs +1878 -830
  14. package/dist/index.browser.mjs.map +1 -1
  15. package/dist/index.node.d.cts +2827 -1562
  16. package/dist/index.node.d.ts +2827 -1562
  17. package/dist/index.node.js +5964 -2718
  18. package/dist/index.node.js.map +1 -1
  19. package/dist/index.node.mjs +5915 -2710
  20. package/dist/index.node.mjs.map +1 -1
  21. package/package.json +16 -10
  22. package/dist/drizzle/offers_v1.1/0000_init.sql +0 -95
  23. package/dist/drizzle/offers_v1.1/0001_new_table_for_collectors_block_numbers.sql +0 -5
  24. package/dist/drizzle/offers_v1.1/0002_update-liquidity-tables.sql +0 -8
  25. package/dist/drizzle/offers_v1.1/0003_add-not-null-for-queue-id.sql +0 -1
  26. package/dist/drizzle/offers_v1.1/0004_add-callback-id-to-offer.sql +0 -1
  27. package/dist/drizzle/offers_v1.1/0005_add-missing-indices-to-liquidity-tables.sql +0 -2
  28. package/dist/drizzle/offers_v1.1/0006_add-callback-amount-to-queues-table.sql +0 -1
  29. package/dist/drizzle/offers_v1.1/0007_add-index-to-created-at.sql +0 -2
  30. package/dist/drizzle/offers_v1.1/meta/0000_snapshot.json +0 -827
  31. package/dist/drizzle/offers_v1.1/meta/0001_snapshot.json +0 -827
  32. package/dist/drizzle/offers_v1.1/meta/0002_snapshot.json +0 -833
  33. package/dist/drizzle/offers_v1.1/meta/0003_snapshot.json +0 -833
  34. package/dist/drizzle/offers_v1.1/meta/0004_snapshot.json +0 -839
  35. package/dist/drizzle/offers_v1.1/meta/0005_snapshot.json +0 -877
  36. package/dist/drizzle/offers_v1.1/meta/_journal.json +0 -62
@@ -1,8 +1,10 @@
1
- import { Errors, LLTV, Offer, Utils, Format } from '@morpho-dev/mempool';
2
- export * from '@morpho-dev/mempool';
3
- import { parseUnits, encodeAbiParameters, maxUint256, formatUnits, decodeAbiParameters, erc20Abi } from 'viem';
4
- import { Base64 } from 'js-base64';
5
1
  import { z } from 'zod/v4';
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
+ import { Base64 } from 'js-base64';
7
+ import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
6
8
  import { createDocument } from 'zod-openapi';
7
9
 
8
10
  var __defProp = Object.defineProperty;
@@ -11,122 +13,289 @@ 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);
17
+
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"])
53
+ });
54
+
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
69
+ });
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
+ }
252
+ ];
15
253
 
16
254
  // src/core/Callback.ts
17
255
  var Callback_exports = {};
18
256
  __export(Callback_exports, {
19
257
  CallbackType: () => CallbackType,
20
258
  WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
21
- buildLiquidity: () => buildLiquidity,
22
- decode: () => decode,
23
- encode: () => encode,
24
- getCallbackIdForOffer: () => getCallbackIdForOffer
259
+ decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
260
+ decodeSellERC20Callback: () => decodeSellERC20Callback,
261
+ encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
262
+ encodeSellERC20Callback: () => encodeSellERC20Callback
25
263
  });
26
264
  var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
27
265
  CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
28
- CallbackType2["SellWithdrawFromWallet"] = "sell_withdraw_from_wallet";
266
+ CallbackType2["BuyVaultV1Callback"] = "buy_vault_v1_callback";
267
+ CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
29
268
  return CallbackType2;
30
269
  })(CallbackType || {});
31
270
  var WhitelistedCallbackAddresses = {
32
271
  ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
33
- ["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */]: [
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 */]: [
34
278
  "0x1111111111111111111111111111111111111111",
35
279
  "0x2222222222222222222222222222222222222222"
36
280
  // @TODO: update once deployed and add mapping per chain if needed
37
281
  ].map((address) => address.toLowerCase())
38
282
  };
39
- function buildLiquidity(parameters) {
40
- switch (parameters.type) {
41
- case "buy_with_empty_callback" /* BuyWithEmptyCallback */: {
42
- const { user, loanToken, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
43
- const amountStr = amount.toString();
44
- const id = `${user}-${chainId.toString()}-${parameters.type}-${loanToken}`.toLowerCase();
45
- const poolId = `${user}-${chainId.toString()}-${loanToken}`.toLowerCase();
46
- return {
47
- userPosition: {
48
- id,
49
- availableLiquidityQueueId: id,
50
- user: user.toLowerCase(),
51
- chainId,
52
- amount: amountStr,
53
- updatedAt
54
- },
55
- queues: [
56
- {
57
- queue: {
58
- queueId: id,
59
- availableLiquidityPoolId: poolId,
60
- index,
61
- callbackAmount: "0",
62
- updatedAt
63
- },
64
- pool: {
65
- id: poolId,
66
- amount: amountStr,
67
- updatedAt
68
- }
69
- }
70
- ]
71
- };
72
- }
73
- case "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */: {
74
- const {
75
- user,
76
- termId,
77
- chainId,
78
- amount,
79
- collaterals,
80
- index = 0,
81
- updatedAt = /* @__PURE__ */ new Date()
82
- } = parameters;
83
- const amountStr = amount.toString();
84
- const id = `${user}-${chainId.toString()}-${parameters.type}-${termId}`.toLowerCase();
85
- return {
86
- userPosition: {
87
- id,
88
- availableLiquidityQueueId: id,
89
- user: user.toLowerCase(),
90
- chainId,
91
- amount: amountStr,
92
- updatedAt
93
- },
94
- queues: collaterals.map((collateral) => {
95
- const poolId = `${user}-${chainId.toString()}-${collateral.collateralAddress}`.toLowerCase();
96
- return {
97
- queue: {
98
- queueId: id,
99
- availableLiquidityPoolId: poolId,
100
- index,
101
- callbackAmount: collateral.callbackAmount.toString(),
102
- updatedAt
103
- },
104
- pool: {
105
- id: poolId,
106
- amount: collateral.balance.toString(),
107
- updatedAt
108
- }
109
- };
110
- })
111
- };
112
- }
113
- default: {
114
- throw new Error(`CallbackType not implemented`);
283
+ function decodeBuyVaultV1Callback(data) {
284
+ if (!data || data === "0x") throw new Error("Empty callback data");
285
+ try {
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");
115
292
  }
293
+ return vaults.map((v, i) => ({ vault: v, amount: amounts[i] }));
294
+ } catch (_) {
295
+ throw new Error("Invalid BuyVaultV1Callback callback data");
116
296
  }
117
297
  }
118
- function getCallbackIdForOffer(offer) {
119
- if (offer.buy && offer.callback.data === "0x") {
120
- return `${offer.offering}-${offer.chainId.toString()}-${"buy_with_empty_callback" /* BuyWithEmptyCallback */}-${offer.loanToken}`.toLowerCase();
121
- }
122
- if (!offer.buy && offer.callback.data !== "0x" && WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].includes(
123
- offer.callback.address.toLowerCase()
124
- )) {
125
- return `${offer.offering}-${offer.chainId.toString()}-${"sell_withdraw_from_wallet" /* SellWithdrawFromWallet */}-${Offer.termId(offer)}`.toLowerCase();
126
- }
127
- return null;
128
- }
129
- function decodeSellWithdrawFromWalletData(data) {
298
+ function decodeSellERC20Callback(data) {
130
299
  if (!data || data === "0x") throw new Error("Empty callback data");
131
300
  try {
132
301
  const [collaterals, amounts] = decodeAbiParameters(
@@ -138,32 +307,449 @@ function decodeSellWithdrawFromWalletData(data) {
138
307
  }
139
308
  return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
140
309
  } catch (_) {
141
- throw new Error("Invalid SellWithdrawFromWallet callback data");
310
+ throw new Error("Invalid SellERC20Callback callback data");
142
311
  }
143
312
  }
144
- function decode(parameters) {
145
- const { type, data } = parameters;
146
- if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
147
- return decodeSellWithdrawFromWalletData(data);
313
+ function encodeBuyVaultV1Callback(parameters) {
314
+ return encodeAbiParameters(
315
+ [{ type: "address[]" }, { type: "uint256[]" }],
316
+ [parameters.vaults, parameters.amounts]
317
+ );
318
+ }
319
+ function encodeSellERC20Callback(parameters) {
320
+ return encodeAbiParameters(
321
+ [{ type: "address[]" }, { type: "uint256[]" }],
322
+ [parameters.collaterals, parameters.amounts]
323
+ );
324
+ }
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;
345
+ }
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);
148
354
  }
149
- throw new Error(`CallbackType not implemented: ${type}`);
150
355
  }
151
- function encode(parameters) {
152
- const { type, data } = parameters;
153
- if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
154
- return encodeAbiParameters(
155
- [{ type: "address[]" }, { type: "uint256[]" }],
156
- [data.collaterals, data.amounts]
356
+
357
+ // src/utils/Errors.ts
358
+ var Errors_exports = {};
359
+ __export(Errors_exports, {
360
+ BaseError: () => BaseError
361
+ });
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);
390
+ }
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}.`
157
606
  );
607
+ __publicField(this, "name", "Chain.InvalidBlockWindowError");
158
608
  }
159
- throw new Error(`CallbackType not implemented: ${type}`);
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);
160
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
+ };
161
747
 
162
748
  // src/core/Cursor.ts
163
749
  var Cursor_exports = {};
164
750
  __export(Cursor_exports, {
165
- decode: () => decode2,
166
- encode: () => encode2,
751
+ decode: () => decode,
752
+ encode: () => encode,
167
753
  validate: () => validate
168
754
  });
169
755
  function validate(cursor) {
@@ -233,12 +819,17 @@ function validate(cursor) {
233
819
  `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
234
820
  );
235
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
+ }
236
827
  return true;
237
828
  }
238
- function encode2(c) {
829
+ function encode(c) {
239
830
  return Base64.encodeURL(JSON.stringify(c));
240
831
  }
241
- function decode2(token) {
832
+ function decode(token) {
242
833
  if (!token) return null;
243
834
  const decoded = JSON.parse(Base64.decode(token));
244
835
  validate(decoded);
@@ -248,578 +839,780 @@ function decode2(token) {
248
839
  // src/core/Liquidity.ts
249
840
  var Liquidity_exports = {};
250
841
  __export(Liquidity_exports, {
251
- fetch: () => fetch2,
252
- fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
253
- serialize: () => serialize
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
254
852
  });
255
- async function fetchBalancesAndAllowances(parameters) {
256
- const { client, spender, pairs, options } = parameters;
257
- if (pairs.length === 0) return /* @__PURE__ */ new Map();
258
- const batchSize = Math.max(1, options?.batchSize ?? 5e3);
259
- const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
260
- const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
261
- const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
262
- const out = /* @__PURE__ */ new Map();
263
- for (const pairsBatch of Utils.batch(pairs, batchSize)) {
264
- const balanceContracts = [];
265
- const allowanceContracts = [];
266
- for (const { user, token } of pairsBatch) {
267
- balanceContracts.push({
268
- address: token,
269
- abi: erc20Abi,
270
- functionName: "balanceOf",
271
- args: [user]
272
- });
273
- allowanceContracts.push({
274
- address: token,
275
- abi: erc20Abi,
276
- functionName: "allowance",
277
- args: [user, spender]
278
- });
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;
279
913
  }
280
- const [balances, allowances] = await Promise.all([
281
- Utils.retry(
282
- () => client.multicall({
283
- allowFailure: false,
284
- contracts: balanceContracts,
285
- ...blockNumber ? { blockNumber } : {}
286
- }),
287
- retryAttempts,
288
- retryDelayMs
289
- ),
290
- Utils.retry(
291
- () => client.multicall({
292
- allowFailure: false,
293
- contracts: allowanceContracts,
294
- ...blockNumber ? { blockNumber } : {}
295
- }),
296
- retryAttempts,
297
- retryDelayMs
298
- )
299
- ]);
300
- for (let i = 0; i < pairsBatch.length; i++) {
301
- const { user, token } = pairsBatch[i];
302
- const balance = balances[i];
303
- const allowance = allowances[i];
304
- let perUser = out.get(user);
305
- if (!perUser) {
306
- perUser = /* @__PURE__ */ new Map();
307
- out.set(user, perUser);
914
+ },
915
+ {
916
+ error: (issue) => {
917
+ try {
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).`;
308
922
  }
309
- perUser.set(token, { balance, allowance });
310
923
  }
311
924
  }
312
- return out;
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;
313
943
  }
314
- async function fetch2(parameters) {
315
- const { client, chainId, spender, type, pairs, options } = parameters;
316
- if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
317
- throw new Error(`CallbackType not implemented: ${type}`);
318
- const map = await fetchBalancesAndAllowances({
319
- client,
320
- spender,
321
- pairs: pairs.map(({ user, loanToken }) => ({ user, token: loanToken })),
322
- options
323
- });
324
- const out = [];
325
- for (const [user, perLoanToken] of map) {
326
- for (const [loanToken, { balance, allowance }] of perLoanToken) {
327
- const amount = balance < allowance ? balance : allowance;
328
- out.push(
329
- buildLiquidity({
330
- type,
331
- user,
332
- loanToken,
333
- chainId,
334
- amount,
335
- index: 0
336
- })
337
- );
338
- }
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;
958
+ }
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);
967
+ }
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");
339
983
  }
340
- return out;
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");
993
+ }
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
+ );
341
1039
  }
342
- function serialize(liquidity) {
343
- const normalized = {
344
- userPosition: {
345
- id: liquidity.userPosition.id,
346
- availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
347
- user: liquidity.userPosition.user,
348
- chainId: String(liquidity.userPosition.chainId),
349
- amount: String(liquidity.userPosition.amount)
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;
350
1048
  },
351
- queues: liquidity.queues.map((queueWithPool) => ({
352
- queue: {
353
- queueId: queueWithPool.queue.queueId,
354
- availableLiquidityPoolId: queueWithPool.queue.availableLiquidityPoolId,
355
- index: queueWithPool.queue.index
356
- },
357
- pool: {
358
- id: queueWithPool.pool.id,
359
- amount: String(queueWithPool.pool.amount)
360
- }
361
- })).sort(
362
- (left, right) => {
363
- const leftQueueId = left.queue.queueId || "";
364
- const rightQueueId = right.queue.queueId || "";
365
- if (leftQueueId < rightQueueId) return -1;
366
- if (leftQueueId > rightQueueId) return 1;
367
- const leftPoolId = left.pool.id;
368
- const rightPoolId = right.pool.id;
369
- if (leftPoolId < rightPoolId) return -1;
370
- if (leftPoolId > rightPoolId) return 1;
371
- const leftIndex = left.queue.index;
372
- const rightIndex = right.queue.index;
373
- if (leftIndex < rightIndex) return -1;
374
- if (leftIndex > rightIndex) return 1;
375
- return 0;
376
- }
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
+ ]
377
1123
  )
378
- };
379
- return JSON.stringify(normalized);
1124
+ );
380
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
+ };
381
1152
 
382
- // src/core/RouterOffer.ts
383
- var RouterOffer_exports = {};
384
- __export(RouterOffer_exports, {
385
- InvalidRouterOfferError: () => InvalidRouterOfferError,
386
- OfferStatusValues: () => OfferStatusValues,
387
- RouterOfferSchema: () => RouterOfferSchema,
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,
388
1160
  consumedEvent: () => consumedEvent,
389
- from: () => from,
1161
+ decode: () => decode2,
1162
+ domain: () => domain,
1163
+ encode: () => encode2,
1164
+ from: () => from5,
390
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,
391
1182
  fromSnakeCase: () => fromSnakeCase,
392
- random: () => random,
393
- toSnakeCase: () => toSnakeCase
1183
+ lazy: () => lazy,
1184
+ max: () => max,
1185
+ min: () => min,
1186
+ poll: () => poll,
1187
+ retry: () => retry,
1188
+ toSnakeCase: () => toSnakeCase,
1189
+ wait: () => wait
394
1190
  });
395
- var OfferStatusValues = [
396
- "valid",
397
- "callback_not_supported",
398
- "callback_error",
399
- "unverified"
400
- ];
401
- var RouterOfferSchema = (parameters) => Offer.OfferSchema(parameters).extend({
402
- consumed: z.bigint({ coerce: true }).min(0n).max(maxUint256),
403
- status: z.enum(OfferStatusValues),
404
- metadata: z.object({
405
- issue: z.string()
406
- }).optional()
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
407
1286
  });
408
- var consumedEvent = {
409
- type: "event",
410
- name: "Consumed",
411
- inputs: [
412
- { name: "user", type: "address", indexed: true, internalType: "address" },
413
- { name: "nonce", type: "uint256", indexed: true, internalType: "uint256" },
414
- { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }
415
- ],
416
- anonymous: false
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
+ });
417
1332
  };
418
- function from(input) {
1333
+ function from5(input) {
419
1334
  try {
420
- const parsedOffer = RouterOfferSchema({ omitHash: true }).parse(input);
421
- const parsedHash = Offer.OfferHashSchema.parse(Offer.hash(parsedOffer));
1335
+ const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1336
+ const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
422
1337
  return {
423
1338
  ...parsedOffer,
424
1339
  hash: parsedHash
425
1340
  };
426
1341
  } catch (error) {
427
- throw new InvalidRouterOfferError(error);
1342
+ throw new InvalidOfferError(error);
428
1343
  }
429
1344
  }
430
- function fromSnakeCase(input) {
431
- return from(Format.fromSnakeCase(input));
1345
+ function fromSnakeCase3(input) {
1346
+ return from5(fromSnakeCase(input));
432
1347
  }
433
- function toSnakeCase(offer) {
434
- return Format.toSnakeCase(offer);
1348
+ function toSnakeCase2(offer) {
1349
+ return toSnakeCase(offer);
435
1350
  }
436
- function random() {
437
- const baseOffer = Offer.random();
438
- return from({
439
- ...baseOffer,
440
- status: "valid",
441
- metadata: void 0,
442
- consumed: 0n
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)
443
1381
  });
1382
+ return offer;
444
1383
  }
445
- function fromConsumedLog(parameters) {
446
- const { blockNumber, logIndex, chainId, transactionHash, user, nonce, amount } = parameters;
447
- return {
448
- id: `${blockNumber.toString()}-${logIndex.toString()}-${chainId}-${transactionHash}`,
449
- chainId: BigInt(chainId),
450
- offering: user,
451
- nonce,
452
- amount
453
- };
454
- }
455
- var InvalidRouterOfferError = class extends Errors.BaseError {
456
- constructor(error) {
457
- super("Invalid router offer.", { cause: error });
458
- __publicField(this, "name", "RouterOffer.InvalidRouterOfferError");
459
- }
460
- };
461
-
462
- // src/core/router/Client.ts
463
- var Client_exports = {};
464
- __export(Client_exports, {
465
- HttpForbiddenError: () => HttpForbiddenError,
466
- HttpGetOffersFailedError: () => HttpGetOffersFailedError,
467
- HttpRateLimitError: () => HttpRateLimitError,
468
- HttpUnauthorizedError: () => HttpUnauthorizedError,
469
- InvalidUrlError: () => InvalidUrlError,
470
- connect: () => connect,
471
- get: () => get,
472
- match: () => match
1384
+ var domain = (chainId) => ({
1385
+ chainId,
1386
+ verifyingContract: zeroAddress
473
1387
  });
474
- var MAX_LIMIT = 100;
475
- var DEFAULT_LIMIT = 20;
476
- var MAX_LLTV = 100;
477
- var MIN_LLTV = 0;
478
- var GetOffersQueryParams = z.object({
479
- // Core filtering parameters
480
- creators: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
481
- message: "Creators must be comma-separated Ethereum addresses"
482
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
483
- description: "Filter by multiple creator addresses (comma-separated)",
484
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
485
- }),
486
- side: z.enum(["buy", "sell"]).optional().meta({
487
- description: "Filter by offer type: buy offers or sell offers",
488
- example: "buy"
489
- }),
490
- chains: z.string().regex(/^\d+(,\d+)*$/, {
491
- message: "Chains must be comma-separated chain IDs"
492
- }).transform((val) => val.split(",").map(Number)).optional().meta({
493
- description: "Filter by multiple blockchain networks (comma-separated chain IDs)",
494
- example: "1,137,10"
495
- }),
496
- loan_tokens: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
497
- message: "Loan assets must be comma-separated Ethereum addresses"
498
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
499
- description: "Filter by multiple loan assets (comma-separated)",
500
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
501
- }),
502
- status: z.string().regex(/^[a-zA-Z_]+(,[a-zA-Z_]+)*$/, {
503
- message: "Status must be comma-separated status values"
504
- }).transform((val) => val.split(",")).refine((statuses) => statuses.every((status) => OfferStatusValues.includes(status)), {
505
- message: `Invalid status value. Must be one of: ${OfferStatusValues.join(", ")}`
506
- }).optional().meta({
507
- description: `Filter by multiple statuses (comma-separated). Valid values: ${OfferStatusValues.join(", ")}. By default, only offers with 'valid' status are returned.`,
508
- example: "valid,callback_error"
509
- }),
510
- callback_addresses: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
511
- message: "Callback addresses must be comma-separated Ethereum addresses"
512
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
513
- description: "Filter by multiple callback addresses (comma-separated)",
514
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
515
- }),
516
- // Asset range
517
- min_amount: z.bigint({ coerce: true }).positive({
518
- message: "Min amount must be a positive number"
519
- }).optional().meta({
520
- description: "Minimum amount of assets in the offer",
521
- example: "1000"
522
- }),
523
- max_amount: z.bigint({ coerce: true }).positive({
524
- message: "Max amount must be a positive number"
525
- }).optional().meta({
526
- description: "Maximum amount of assets in the offer",
527
- example: "10000"
528
- }),
529
- // Rate range
530
- min_rate: z.bigint({ coerce: true }).positive({
531
- message: "Min rate must be a positive number"
532
- }).optional().meta({
533
- description: "Minimum rate per asset (in wei)",
534
- example: "500000000000000000"
535
- }),
536
- max_rate: z.bigint({ coerce: true }).positive({
537
- message: "Max rate must be a positive number"
538
- }).optional().meta({
539
- description: "Maximum rate per asset (in wei)",
540
- example: "1500000000000000000"
541
- }),
542
- // Time range
543
- min_maturity: z.coerce.number().int().min(0).optional().meta({
544
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
545
- example: "1700000000"
546
- }),
547
- max_maturity: z.coerce.number().int().min(0).optional().meta({
548
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
549
- example: "1800000000"
550
- }),
551
- min_expiry: z.coerce.number().int().optional().meta({
552
- description: "Minimum expiry timestamp (Unix timestamp in seconds)",
553
- example: "1700000000"
554
- }),
555
- max_expiry: z.coerce.number().int().optional().meta({
556
- description: "Maximum expiry timestamp (Unix timestamp in seconds)",
557
- example: "1800000000"
558
- }),
559
- // Collateral filtering
560
- collateral_assets: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
561
- message: "Collateral assets must be comma-separated Ethereum addresses"
562
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
563
- description: "Filter by multiple collateral assets (comma-separated)",
564
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
565
- }),
566
- collateral_oracles: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
567
- message: "Collateral oracles must be comma-separated Ethereum addresses"
568
- }).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
569
- description: "Filter by multiple rate oracles (comma-separated)",
570
- example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
571
- }),
572
- collateral_tuple: z.string().transform((val, ctx) => {
573
- 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]+)?)?)*$/;
574
- if (!pattern.test(val)) {
575
- ctx.addIssue({
576
- code: "custom",
577
- message: "collateral_tuple has an invalid format",
578
- input: val
579
- });
580
- }
581
- return val.split("#").map((tuple) => {
582
- const parts = tuple.split(":");
583
- if (parts.length === 0 || !parts[0]) {
584
- ctx.addIssue({
585
- code: "custom",
586
- message: "Asset address is required for each collateral tuple",
587
- path: ["asset"],
588
- input: val
589
- });
590
- return z.NEVER;
591
- }
592
- const asset = parts[0]?.toLowerCase();
593
- const oracle = parts[1]?.toLowerCase();
594
- const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
595
- if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
596
- ctx.addIssue({
597
- code: "custom",
598
- message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
599
- path: ["lltv"],
600
- input: val
601
- });
602
- return z.NEVER;
603
- }
604
- let lltvValue;
605
- if (lltv !== void 0) {
606
- try {
607
- lltvValue = LLTV.from(parseUnits(lltv.toString(), 16));
608
- } catch (e) {
609
- ctx.issues.push({
610
- code: "custom",
611
- message: e instanceof LLTV.InvalidLLTVError || e instanceof LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
612
- input: lltv,
613
- path: ["lltv"]
614
- });
615
- return z.NEVER;
616
- }
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
617
1437
  }
618
- return {
619
- asset,
620
- oracle,
621
- lltv: lltvValue
622
- };
623
- });
624
- }).optional().meta({
625
- description: "Filter by collateral combinations in format: asset:oracle:lltv#asset2:oracle2:lltv2. Oracle and lltv are optional. Use # to separate multiple combinations.",
626
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:94.5"
627
- }),
628
- min_lltv: z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).optional().meta({
629
- description: "Minimum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 80.5 = 80.5%)",
630
- example: "80.5"
631
- }),
632
- max_lltv: z.coerce.number().min(0, { message: "LLTV must be above 0" }).max(100, { message: "LLTV must be below 100" }).optional().meta({
633
- description: "Maximum Loan-to-Value ratio (LLTV) for collateral (percentage as decimal, e.g., 95.5 = 95.5%)",
634
- example: "95.5"
635
- }),
636
- // Sorting parameters
637
- sort_by: z.enum(["rate", "maturity", "expiry", "amount"]).optional().meta({
638
- description: "Field to sort results by",
639
- example: "rate"
640
- }),
641
- sort_order: z.enum(["asc", "desc"]).optional().default("desc").meta({
642
- description: "Sort direction: asc (ascending) or desc (descending, default)",
643
- example: "desc"
644
- }),
645
- // Pagination
646
- cursor: z.string().optional().refine(
647
- (val) => {
648
- if (!val) return true;
649
- try {
650
- const decoded = decode2(val);
651
- return decoded !== null;
652
- } catch (_error) {
653
- return false;
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
654
1458
  }
655
1459
  },
656
- {
657
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
658
- }
659
- ).meta({
660
- description: "Pagination cursor in base64url-encoded format",
661
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
662
- }),
663
- limit: z.string().regex(/^[1-9]\d*$/, {
664
- message: "Limit must be a positive integer"
665
- }).transform((val) => Number.parseInt(val, 10)).pipe(
666
- z.number().max(MAX_LIMIT, {
667
- message: `Limit cannot exceed ${MAX_LIMIT}`
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
668
1471
  })
669
- ).optional().default(DEFAULT_LIMIT).meta({
670
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
671
- example: 10
672
- })
673
- }).refine(
674
- (data) => data.min_maturity === void 0 || data.max_maturity === void 0 || data.max_maturity >= data.min_maturity,
675
- {
676
- message: "max_maturity must be greater than or equal to min_maturity",
677
- path: ["max_maturity"]
678
- }
679
- ).refine(
680
- (data) => data.min_expiry === void 0 || data.max_expiry === void 0 || data.max_expiry >= data.min_expiry,
681
- {
682
- message: "max_expiry must be greater than or equal to min_expiry",
683
- path: ["max_expiry"]
684
- }
685
- ).refine(
686
- (data) => data.min_amount === void 0 || data.max_amount === void 0 || data.max_amount >= data.min_amount,
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" },
687
1485
  {
688
- message: "max_amount must be greater than or equal to min_amount",
689
- path: ["max_amount"]
690
- }
691
- ).refine(
692
- (data) => data.min_rate === void 0 || data.max_rate === void 0 || data.max_rate >= data.min_rate,
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
+ },
693
1494
  {
694
- message: "max_rate must be greater than or equal to min_rate",
695
- path: ["max_rate"]
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");
696
1583
  }
697
- ).refine(
698
- (data) => data.min_lltv === void 0 || data.max_lltv === void 0 || data.max_lltv >= data.min_lltv,
699
- {
700
- message: "max_lltv must be greater than or equal to min_lltv",
701
- path: ["max_lltv"]
1584
+ };
1585
+ var AccountNotSetError = class extends BaseError {
1586
+ constructor() {
1587
+ super("Account not set.");
1588
+ __publicField(this, "name", "Offer.AccountNotSetError");
702
1589
  }
703
- );
704
- var MatchOffersQueryParams = z.object({
705
- // Required parameters
706
- side: z.enum(["buy", "sell"]).meta({
707
- 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.",
708
- example: "buy"
709
- }),
710
- chain_id: z.string().transform((val, ctx) => {
711
- const numericLike = /^-?\d+$/.test(val);
712
- if (!numericLike) {
713
- ctx.addIssue({
714
- code: "custom",
715
- message: "chain_id has an invalid format",
716
- input: val
717
- });
718
- ctx.addIssue({
719
- code: "custom",
720
- message: "Invalid input: expected number, received NaN",
721
- input: val
722
- });
723
- return z.NEVER;
724
- }
725
- return Number.parseInt(val, 10);
726
- }).pipe(z.number().positive()).meta({
727
- description: "The blockchain network chain ID",
728
- example: "1"
729
- }),
730
- // Optional parameters
731
- rate: z.bigint({ coerce: true }).positive({
732
- message: "Rate must be a positive number"
733
- }).optional().meta({
734
- description: "Rate per asset (in wei) for matching offers",
735
- example: "1000000000000000000"
736
- }),
737
- // Collateral filtering
738
- collaterals: z.string().transform((val, ctx) => {
739
- 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]+)?)*$/;
740
- if (!pattern.test(val)) {
741
- ctx.addIssue({
742
- code: "custom",
743
- message: "Collaterals must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. All fields are required for each collateral.",
744
- input: val
745
- });
746
- }
747
- return val.split("#").map((collateral) => {
748
- const parts = collateral.split(":");
749
- if (parts.length !== 3) {
750
- ctx.addIssue({
751
- code: "custom",
752
- message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
753
- path: ["collaterals"],
754
- input: val
755
- });
756
- return z.NEVER;
757
- }
758
- const [asset, oracle, lltvStr] = parts;
759
- if (!asset || !oracle || !lltvStr) {
760
- ctx.addIssue({
761
- code: "custom",
762
- message: "Asset, oracle, and lltv are all required for each collateral",
763
- path: ["collaterals"],
764
- input: val
765
- });
766
- }
767
- let lltvValue;
768
- if (lltvStr !== void 0) {
769
- try {
770
- lltvValue = LLTV.from(parseUnits(lltvStr, 16));
771
- } catch (e) {
772
- ctx.issues.push({
773
- code: "custom",
774
- message: e instanceof LLTV.InvalidLLTVError || e instanceof LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
775
- input: lltvStr,
776
- path: ["lltv"]
777
- });
778
- return z.NEVER;
779
- }
780
- }
781
- return {
782
- asset: asset.toLowerCase(),
783
- oracle: oracle.toLowerCase(),
784
- lltv: lltvValue
785
- };
786
- });
787
- }).optional().meta({
788
- description: "Collateral requirements in format: asset:oracle:lltv#asset2:oracle2:lltv2. Use # to separate multiple collaterals.",
789
- example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:94.5"
790
- }),
791
- // Maturity filtering
792
- maturity: z.coerce.number().int().min(0).optional().meta({
793
- description: "Maturity timestamp (Unix timestamp in seconds)",
794
- example: "1700000000"
795
- }),
796
- min_maturity: z.coerce.number().int().min(0).optional().meta({
797
- description: "Minimum maturity timestamp (Unix timestamp in seconds)",
798
- example: "1700000000"
799
- }),
800
- max_maturity: z.coerce.number().int().min(0).optional().meta({
801
- description: "Maximum maturity timestamp (Unix timestamp in seconds)",
802
- example: "1800000000"
803
- }),
804
- // Asset and creator filtering
805
- loan_token: z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
806
- message: "Loan asset must be a valid Ethereum address"
807
- }).transform((val) => val.toLowerCase()).optional().meta({
808
- description: "The loan asset address to match against",
809
- example: "0x1234567890123456789012345678901234567890"
810
- }),
811
- creator: z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
812
- message: "Creator must be a valid Ethereum address"
813
- }).transform((val) => val.toLowerCase()).optional().meta({
814
- description: "Filter by a specific offer creator address",
815
- example: "0x1234567890123456789012345678901234567890"
816
- }),
817
- // Pagination
818
- cursor: z.string().optional().refine(
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(
819
1612
  (val) => {
820
1613
  if (!val) return true;
821
1614
  try {
822
- const decoded = decode2(val);
1615
+ const decoded = Cursor_exports.decode(val);
823
1616
  return decoded !== null;
824
1617
  } catch (_error) {
825
1618
  return false;
@@ -832,34 +1625,49 @@ var MatchOffersQueryParams = z.object({
832
1625
  description: "Pagination cursor in base64url-encoded format",
833
1626
  example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
834
1627
  }),
835
- limit: z.string().regex(/^[1-9]\d*$/, {
1628
+ limit: z7.string().regex(/^[1-9]\d*$/, {
836
1629
  message: "Limit must be a positive integer"
837
1630
  }).transform((val) => Number.parseInt(val, 10)).pipe(
838
- z.number().max(MAX_LIMIT, {
1631
+ z7.number().max(MAX_LIMIT, {
839
1632
  message: `Limit cannot exceed ${MAX_LIMIT}`
840
1633
  })
841
1634
  ).optional().default(DEFAULT_LIMIT).meta({
842
1635
  description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
843
1636
  example: 10
844
1637
  })
845
- }).refine(
846
- (data) => data.min_maturity === void 0 || data.max_maturity === void 0 || data.max_maturity >= data.min_maturity,
847
- {
848
- message: "max_maturity must be greater than or equal to min_maturity",
849
- path: ["max_maturity"]
850
- }
851
- );
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
+ });
852
1657
  var schemas = {
853
1658
  get_offers: GetOffersQueryParams,
854
- match_offers: MatchOffersQueryParams
1659
+ get_obligations: GetObligationsQueryParams
855
1660
  };
1661
+ function parse(action, query) {
1662
+ return schemas[action].parse(query);
1663
+ }
856
1664
  function safeParse(action, query, error) {
857
1665
  return schemas[action].safeParse(query, {
858
1666
  error
859
1667
  });
860
1668
  }
861
1669
 
862
- // src/core/apiSchema/openapi.ts
1670
+ // src/api/Api/Schema/openapi.ts
863
1671
  var successResponseSchema = z.object({
864
1672
  status: z.literal("success"),
865
1673
  cursor: z.string().nullable(),
@@ -882,8 +1690,8 @@ var errorResponseSchema = z.object({
882
1690
  var paths = {
883
1691
  "/v1/offers": {
884
1692
  get: {
885
- summary: "Get offers",
886
- description: "Get all offers with optional filtering and pagination",
1693
+ summary: "Offers",
1694
+ description: "Find offers that match specific criteria",
887
1695
  tags: ["Offers"],
888
1696
  requestParams: {
889
1697
  query: GetOffersQueryParams
@@ -908,13 +1716,13 @@ var paths = {
908
1716
  }
909
1717
  }
910
1718
  },
911
- "/v1/offers/match": {
1719
+ "/v1/obligations": {
912
1720
  get: {
913
- summary: "Match offers",
914
- description: "Find offers that match specific criteria",
915
- tags: ["Offers"],
1721
+ summary: "Obligations",
1722
+ description: "List obligations with pagination support",
1723
+ tags: ["Obligations"],
916
1724
  requestParams: {
917
- query: MatchOffersQueryParams
1725
+ query: GetObligationsQueryParams
918
1726
  },
919
1727
  responses: {
920
1728
  200: {
@@ -935,9 +1743,60 @@ var paths = {
935
1743
  }
936
1744
  }
937
1745
  }
1746
+ },
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
1758
+ }
1759
+ }
1760
+ }
1761
+ }
1762
+ }
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
+ }
1778
+ }
1779
+ }
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
+ }
1796
+ }
938
1797
  }
939
1798
  };
940
- createDocument({
1799
+ var OpenApi = createDocument({
941
1800
  openapi: "3.1.0",
942
1801
  info: {
943
1802
  title: "Router API",
@@ -947,6 +1806,12 @@ createDocument({
947
1806
  tags: [
948
1807
  {
949
1808
  name: "Offers"
1809
+ },
1810
+ {
1811
+ name: "Obligations"
1812
+ },
1813
+ {
1814
+ name: "Health"
950
1815
  }
951
1816
  ],
952
1817
  servers: [
@@ -962,120 +1827,40 @@ createDocument({
962
1827
  paths
963
1828
  });
964
1829
 
965
- // src/core/apiSchema/utils.ts
966
- function toResponse(routerOffer) {
967
- const { consumed, status, metadata, ...offer } = routerOffer;
968
- return {
969
- offer,
970
- consumed,
971
- status,
972
- metadata
973
- };
974
- }
975
- function fromResponse(offerResponse) {
976
- const { offer, consumed, status, metadata } = offerResponse;
977
- return {
978
- ...offer,
979
- consumed,
980
- status,
981
- metadata
982
- };
983
- }
984
-
985
- // src/core/router/Client.ts
986
- function connect(opts) {
987
- const u = new URL(opts?.url || "https://router.morpho.dev");
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");
988
1844
  if (u.protocol !== "http:" && u.protocol !== "https:") {
989
1845
  throw new InvalidUrlError(u.toString());
990
1846
  }
991
- const headers = opts?.headers ?? new Headers();
1847
+ const headers = options?.headers ?? new Headers();
992
1848
  headers.set("Content-Type", "application/json");
993
- opts?.apiKey !== void 0 ? headers.set("X-API-Key", opts.apiKey) : null;
1849
+ options?.apiKey !== void 0 ? headers.set("X-API-Key", options.apiKey) : null;
994
1850
  const config = {
995
1851
  url: u,
996
1852
  headers
997
1853
  };
998
1854
  return {
999
1855
  ...config,
1000
- get: (parameters) => get(config, parameters),
1001
- match: (parameters) => match(config, parameters)
1856
+ getOffers: (parameters) => getOffers(config, parameters),
1857
+ getObligations: (parameters) => getObligations(config, parameters)
1002
1858
  };
1003
1859
  }
1004
- async function get(config, parameters) {
1860
+ async function getOffers(config, parameters) {
1005
1861
  const url = new URL(`${config.url.toString()}v1/offers`);
1006
- if (parameters.creators?.length) {
1007
- url.searchParams.set("creators", parameters.creators.join(","));
1008
- }
1009
- if (parameters.side) {
1010
- url.searchParams.set("side", parameters.side);
1011
- }
1012
- if (parameters.chains?.length) {
1013
- url.searchParams.set("chains", parameters.chains.join(","));
1014
- }
1015
- if (parameters.loanTokens?.length) {
1016
- url.searchParams.set("loan_tokens", parameters.loanTokens.join(","));
1017
- }
1018
- if (parameters.status?.length) {
1019
- url.searchParams.set("status", parameters.status.join(","));
1020
- }
1021
- if (parameters.callbackAddresses?.length) {
1022
- url.searchParams.set("callback_addresses", parameters.callbackAddresses.join(","));
1023
- }
1024
- if (parameters.minAmount !== void 0) {
1025
- url.searchParams.set("min_amount", parameters.minAmount.toString());
1026
- }
1027
- if (parameters.maxAmount !== void 0) {
1028
- url.searchParams.set("max_amount", parameters.maxAmount.toString());
1029
- }
1030
- if (parameters.minRate !== void 0) {
1031
- url.searchParams.set("min_rate", parameters.minRate.toString());
1032
- }
1033
- if (parameters.maxRate !== void 0) {
1034
- url.searchParams.set("max_rate", parameters.maxRate.toString());
1035
- }
1036
- if (parameters.minMaturity !== void 0) {
1037
- url.searchParams.set("min_maturity", parameters.minMaturity.toString());
1038
- }
1039
- if (parameters.maxMaturity !== void 0) {
1040
- url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
1041
- }
1042
- if (parameters.minExpiry !== void 0) {
1043
- url.searchParams.set("min_expiry", parameters.minExpiry.toString());
1044
- }
1045
- if (parameters.maxExpiry !== void 0) {
1046
- url.searchParams.set("max_expiry", parameters.maxExpiry.toString());
1047
- }
1048
- if (parameters.collateralAssets?.length) {
1049
- url.searchParams.set("collateral_assets", parameters.collateralAssets.join(","));
1050
- }
1051
- if (parameters.collateralOracles?.length) {
1052
- url.searchParams.set("collateral_oracles", parameters.collateralOracles.join(","));
1053
- }
1054
- if (parameters.collateralTuple?.length) {
1055
- const tupleStr = parameters.collateralTuple.map(({ asset, oracle, lltv }) => {
1056
- let result = asset;
1057
- if (oracle) {
1058
- result += `:${oracle}`;
1059
- } else if (lltv !== void 0) {
1060
- result += `:`;
1061
- }
1062
- if (lltv !== void 0) result += `:${formatUnits(lltv, 16)}`;
1063
- return result;
1064
- }).join("#");
1065
- url.searchParams.set("collateral_tuple", tupleStr);
1066
- }
1067
- if (parameters.minLltv !== void 0) {
1068
- url.searchParams.set("min_lltv", formatUnits(parameters.minLltv, 16));
1069
- }
1070
- if (parameters.maxLltv !== void 0) {
1071
- url.searchParams.set("max_lltv", formatUnits(parameters.maxLltv, 16));
1072
- }
1073
- if (parameters.sortBy) {
1074
- url.searchParams.set("sort_by", parameters.sortBy);
1075
- }
1076
- if (parameters.sortOrder) {
1077
- url.searchParams.set("sort_order", parameters.sortOrder);
1078
- }
1862
+ url.searchParams.set("side", parameters.side);
1863
+ url.searchParams.set("obligation_id", parameters.obligationId.toString());
1079
1864
  if (parameters.cursor) {
1080
1865
  url.searchParams.set("cursor", parameters.cursor);
1081
1866
  }
@@ -1083,72 +1868,45 @@ async function get(config, parameters) {
1083
1868
  url.searchParams.set("limit", parameters.limit.toString());
1084
1869
  }
1085
1870
  const { cursor: returnedCursor, data: offers } = await getApi(config, url);
1086
- const routerOffers = offers.map(Format.fromSnakeCase).map(fromResponse);
1871
+ const routerOffers = offers.map(Offer_exports.fromSnakeCase);
1087
1872
  return {
1088
1873
  cursor: returnedCursor,
1089
- offers: routerOffers.map(from).map(toResponse)
1874
+ offers: routerOffers
1090
1875
  };
1091
1876
  }
1092
- async function match(config, parameters) {
1093
- const url = new URL(`${config.url.toString()}v1/offers/match`);
1094
- url.searchParams.set("side", parameters.side);
1095
- url.searchParams.set("chain_id", parameters.chainId.toString());
1096
- if (parameters.rate !== void 0) {
1097
- url.searchParams.set("rate", parameters.rate.toString());
1098
- }
1099
- if (parameters.collaterals?.length) {
1100
- const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${formatUnits(lltv, 16)}`).join("#");
1101
- url.searchParams.set("collaterals", collateralsStr);
1102
- }
1103
- if (parameters.maturity !== void 0) {
1104
- url.searchParams.set("maturity", parameters.maturity.toString());
1105
- }
1106
- if (parameters.minMaturity !== void 0) {
1107
- url.searchParams.set("min_maturity", parameters.minMaturity.toString());
1108
- }
1109
- if (parameters.maxMaturity !== void 0) {
1110
- url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
1111
- }
1112
- if (parameters.loanToken) {
1113
- url.searchParams.set("loan_token", parameters.loanToken);
1114
- }
1115
- if (parameters.creator) {
1116
- url.searchParams.set("creator", parameters.creator);
1117
- }
1118
- if (parameters.status?.length) {
1119
- url.searchParams.set("status", parameters.status.join(","));
1120
- }
1121
- if (parameters.cursor) {
1877
+ async function getObligations(config, parameters) {
1878
+ const url = new URL(`${config.url.toString()}v1/obligations`);
1879
+ if (parameters?.cursor !== void 0) {
1122
1880
  url.searchParams.set("cursor", parameters.cursor);
1123
1881
  }
1124
- if (parameters.limit !== void 0) {
1882
+ if (parameters?.limit !== void 0) {
1125
1883
  url.searchParams.set("limit", parameters.limit.toString());
1126
1884
  }
1127
- const { cursor: returnedCursor, data: offers } = await getApi(config, url);
1128
- const routerOffers = offers.map(Format.fromSnakeCase).map(fromResponse);
1885
+ const { cursor: returnedCursor, data: obligationsSnake } = await getApi(config, url);
1886
+ const obligations = obligationsSnake.map(Obligation_exports.fromSnakeCase);
1129
1887
  return {
1130
1888
  cursor: returnedCursor,
1131
- offers: routerOffers.map(from).map(toResponse)
1889
+ obligations
1132
1890
  };
1133
1891
  }
1134
1892
  async function getApi(config, url) {
1135
1893
  const pathname = url.pathname;
1136
1894
  let action;
1137
1895
  switch (true) {
1138
- case pathname.includes("/v1/offers/match"):
1139
- action = "match_offers";
1140
- break;
1141
1896
  case pathname.includes("/v1/offers"):
1142
1897
  action = "get_offers";
1143
1898
  break;
1899
+ case pathname.includes("/v1/obligations"):
1900
+ action = "get_obligations";
1901
+ break;
1144
1902
  default:
1145
- throw new HttpGetOffersFailedError("Unknown endpoint", {
1903
+ throw new HttpGetApiFailedError("Unknown endpoint", {
1146
1904
  details: `Unsupported path: ${pathname}`
1147
1905
  });
1148
1906
  }
1149
1907
  const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
1150
1908
  if (!schemaParseResult.success) {
1151
- throw new HttpGetOffersFailedError(`Invalid URL parameters`, {
1909
+ throw new HttpGetApiFailedError(`Invalid URL parameters`, {
1152
1910
  details: schemaParseResult.error.issues[0]?.message
1153
1911
  });
1154
1912
  }
@@ -1165,19 +1923,19 @@ async function getApi(config, url) {
1165
1923
  case 429:
1166
1924
  throw new HttpRateLimitError();
1167
1925
  }
1168
- throw new HttpGetOffersFailedError(`GET request returned ${response.status}`, {
1926
+ throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
1169
1927
  details: await response.text()
1170
1928
  });
1171
1929
  }
1172
1930
  return response.json();
1173
1931
  }
1174
- var InvalidUrlError = class extends Errors.BaseError {
1932
+ var InvalidUrlError = class extends BaseError {
1175
1933
  constructor(url) {
1176
1934
  super(`URL "${url}" is not http/https.`);
1177
1935
  __publicField(this, "name", "Router.InvalidUrlError");
1178
1936
  }
1179
1937
  };
1180
- var HttpUnauthorizedError = class extends Errors.BaseError {
1938
+ var HttpUnauthorizedError = class extends BaseError {
1181
1939
  constructor() {
1182
1940
  super("Unauthorized.", {
1183
1941
  metaMessages: ["Ensure that an API key is provided."]
@@ -1185,7 +1943,7 @@ var HttpUnauthorizedError = class extends Errors.BaseError {
1185
1943
  __publicField(this, "name", "Router.HttpUnauthorizedError");
1186
1944
  }
1187
1945
  };
1188
- var HttpForbiddenError = class extends Errors.BaseError {
1946
+ var HttpForbiddenError = class extends BaseError {
1189
1947
  constructor() {
1190
1948
  super("Forbidden.", {
1191
1949
  metaMessages: ["Ensure that the API key is valid."]
@@ -1193,7 +1951,7 @@ var HttpForbiddenError = class extends Errors.BaseError {
1193
1951
  __publicField(this, "name", "Router.HttpForbiddenError");
1194
1952
  }
1195
1953
  };
1196
- var HttpRateLimitError = class extends Errors.BaseError {
1954
+ var HttpRateLimitError = class extends BaseError {
1197
1955
  constructor() {
1198
1956
  super("Rate limit exceeded.", {
1199
1957
  metaMessages: [
@@ -1203,16 +1961,16 @@ var HttpRateLimitError = class extends Errors.BaseError {
1203
1961
  __publicField(this, "name", "Router.HttpRateLimitError");
1204
1962
  }
1205
1963
  };
1206
- var HttpGetOffersFailedError = class extends Errors.BaseError {
1964
+ var HttpGetApiFailedError = class extends BaseError {
1207
1965
  constructor(message, { details } = {}) {
1208
1966
  super(message, {
1209
1967
  metaMessages: [details]
1210
1968
  });
1211
- __publicField(this, "name", "Router.HttpGetOffersFailedError");
1969
+ __publicField(this, "name", "Router.HttpGetApiFailedError");
1212
1970
  }
1213
1971
  };
1214
1972
 
1215
- // src/core/Validation.ts
1973
+ // src/collectors/validations/Validation.ts
1216
1974
  var Validation_exports = {};
1217
1975
  __export(Validation_exports, {
1218
1976
  run: () => run
@@ -1259,17 +2017,17 @@ async function run(parameters) {
1259
2017
  };
1260
2018
  }
1261
2019
 
1262
- // src/core/ValidationRule.ts
2020
+ // src/collectors/validations/ValidationRule.ts
1263
2021
  var ValidationRule_exports = {};
1264
2022
  __export(ValidationRule_exports, {
1265
- batch: () => batch,
2023
+ batch: () => batch2,
1266
2024
  morpho: () => morpho,
1267
2025
  single: () => single
1268
2026
  });
1269
2027
  function single(name, run2) {
1270
2028
  return { kind: "single", name, run: run2 };
1271
2029
  }
1272
- function batch(name, run2) {
2030
+ function batch2(name, run2) {
1273
2031
  return { kind: "batch", name, run: run2 };
1274
2032
  }
1275
2033
  function morpho() {
@@ -1307,7 +2065,17 @@ function morpho() {
1307
2065
  "buy_offers_non_empty_callback",
1308
2066
  (offer, _) => {
1309
2067
  if (offer.buy && offer.callback.data !== "0x") {
1310
- 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
+ }
1311
2079
  }
1312
2080
  }
1313
2081
  );
@@ -1316,7 +2084,7 @@ function morpho() {
1316
2084
  (offer, _) => {
1317
2085
  if (!offer.buy && offer.callback.data !== "0x") {
1318
2086
  const allowed = new Set(
1319
- WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].map(
2087
+ Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.SellERC20Callback].map(
1320
2088
  (a) => a.toLowerCase()
1321
2089
  )
1322
2090
  );
@@ -1332,10 +2100,7 @@ function morpho() {
1332
2100
  (offer, _) => {
1333
2101
  if (!offer.buy && offer.callback.data !== "0x") {
1334
2102
  try {
1335
- const decoded = decode({
1336
- type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
1337
- data: offer.callback.data
1338
- });
2103
+ const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
1339
2104
  if (decoded.length === 0) {
1340
2105
  return { message: "Sell offer callback data must include at least one collateral." };
1341
2106
  }
@@ -1350,10 +2115,7 @@ function morpho() {
1350
2115
  (offer, _) => {
1351
2116
  if (!offer.buy && offer.callback.data !== "0x") {
1352
2117
  try {
1353
- const decoded = decode({
1354
- type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
1355
- data: offer.callback.data
1356
- });
2118
+ const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
1357
2119
  const offerCollaterals = new Set(
1358
2120
  offer.collaterals.map((c) => c.asset.toLowerCase())
1359
2121
  );
@@ -1367,20 +2129,306 @@ function morpho() {
1367
2129
  }
1368
2130
  }
1369
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
+ );
2252
+ const maturity = single("maturity", (offer, _) => {
2253
+ const allowedMaturities = [Maturity_exports.from("end_of_month"), Maturity_exports.from("end_of_next_month")];
2254
+ if (!allowedMaturities.includes(offer.maturity)) {
2255
+ return {
2256
+ message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
2257
+ };
2258
+ }
2259
+ });
1370
2260
  return [
1371
2261
  chainId,
1372
2262
  loanToken,
1373
2263
  expiry,
2264
+ maturity,
1374
2265
  // note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
1375
2266
  // integrators should be able to choose if they want to keep the offer or not
1376
2267
  sellEmptyCallback,
1377
2268
  buyNonEmptyCallback,
1378
2269
  sellNonWhitelistedCallback,
1379
2270
  sellCallbackDataInvalid,
1380
- sellCallbackCollateralInvalid
2271
+ sellCallbackCollateralInvalid,
2272
+ buyCallbackDataInvalid,
2273
+ buyCallbackVaultInvalid
1381
2274
  ];
1382
2275
  }
1383
2276
 
1384
- export { Callback_exports as Callback, Cursor_exports as Cursor, Liquidity_exports as Liquidity, Client_exports as Router, 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 };
1385
2433
  //# sourceMappingURL=index.browser.mjs.map
1386
2434
  //# sourceMappingURL=index.browser.mjs.map