@morpho-dev/router 0.1.16 → 0.1.18

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.
@@ -3,13 +3,15 @@
3
3
  var nodeServer = require('@hono/node-server');
4
4
  var hono = require('hono');
5
5
  var cors = require('hono/cors');
6
- var async_hooks = require('async_hooks');
6
+ var v4 = require('zod/v4');
7
+ var zodOpenapi = require('zod-openapi');
7
8
  var viem = require('viem');
8
9
  var actions = require('viem/actions');
9
10
  var chains$1 = require('viem/chains');
10
11
  var z6 = require('zod');
11
- var jsBase64 = require('js-base64');
12
12
  var accounts = require('viem/accounts');
13
+ var jsBase64 = require('js-base64');
14
+ var async_hooks = require('async_hooks');
13
15
  var drizzleOrm = require('drizzle-orm');
14
16
  var pgCore = require('drizzle-orm/pg-core');
15
17
  var path = require('path');
@@ -19,8 +21,6 @@ var migrator = require('drizzle-orm/node-postgres/migrator');
19
21
  var pglite$1 = require('drizzle-orm/pglite');
20
22
  var migrator$1 = require('drizzle-orm/pglite/migrator');
21
23
  var pg = require('pg');
22
- var v4 = require('zod/v4');
23
- var zodOpenapi = require('zod-openapi');
24
24
 
25
25
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
26
26
 
@@ -51,9 +51,9 @@ var __export = (target, all) => {
51
51
  __defProp(target, name, { get: all[name], enumerable: true });
52
52
  };
53
53
 
54
- // src/api/Api/index.ts
55
- var Api_exports2 = {};
56
- __export(Api_exports2, {
54
+ // src/api/RouterApi.ts
55
+ var RouterApi_exports = {};
56
+ __export(RouterApi_exports, {
57
57
  ChainHealth: () => ChainHealth,
58
58
  ChainsHealthResponse: () => ChainsHealthResponse,
59
59
  CollectorHealth: () => CollectorHealth,
@@ -68,1747 +68,1319 @@ __export(Api_exports2, {
68
68
  safeParse: () => safeParse
69
69
  });
70
70
 
71
- // src/api/Api/Controllers/index.ts
71
+ // src/api/Controllers/index.ts
72
72
  var Controllers_exports = {};
73
73
  __export(Controllers_exports, {
74
+ getDocsHtml: () => getDocsHtml,
74
75
  getHealth: () => getHealth,
75
76
  getHealthChains: () => getHealthChains,
76
77
  getHealthCollectors: () => getHealthCollectors,
77
78
  getObligations: () => getObligations,
78
- getOffers: () => getOffers
79
- });
80
-
81
- // src/services/Health.ts
82
- var Health_exports = {};
83
- __export(Health_exports, {
84
- create: () => create9
85
- });
86
-
87
- // src/collectors/index.ts
88
- var collectors_exports = {};
89
- __export(collectors_exports, {
90
- batch: () => batch2,
91
- create: () => create2,
92
- createBuilder: () => createBuilder,
93
- fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
94
- fetchCollateralAndDebt: () => fetchCollateralAndDebt,
95
- fetchOraclePrices: () => fetchOraclePrices,
96
- fetchUserVaultMarketLiquidity: () => fetchUserVaultMarketLiquidity,
97
- morpho: () => morpho,
98
- names: () => names,
99
- run: () => run,
100
- single: () => single,
101
- start: () => start
79
+ getOffers: () => getOffers,
80
+ getSwaggerJson: () => getSwaggerJson
102
81
  });
103
82
 
104
- // src/services/Logger.ts
105
- var Logger_exports = {};
106
- __export(Logger_exports, {
107
- LogLevelValues: () => LogLevelValues,
108
- defaultLogger: () => defaultLogger,
109
- getLogger: () => getLogger,
110
- runWithLogger: () => runWithLogger,
111
- silentLogger: () => silentLogger
83
+ // src/core/Abi.ts
84
+ var Abi_exports = {};
85
+ __export(Abi_exports, {
86
+ ERC4626: () => ERC4626,
87
+ MetaMorpho: () => MetaMorpho,
88
+ MetaMorphoFactory: () => MetaMorphoFactory,
89
+ Morpho: () => Morpho,
90
+ Oracle: () => Oracle
112
91
  });
113
- var LogLevelValues = [
114
- "trace",
115
- "debug",
116
- "info",
117
- "warn",
118
- "error",
119
- "fatal",
120
- "silent"
121
- ];
122
- function defaultLogger(minLevel, pretty) {
123
- const threshold = minLevel ?? process.env.ROUTER_LOG_LEVEL ?? "info";
124
- const prettyEnabled = typeof pretty === "boolean" ? pretty : String(process.env.ROUTER_LOG_PRETTY ?? "false").toLowerCase() === "true";
125
- const levelIndexByName = LogLevelValues.reduce(
126
- (acc, lvl, idx) => {
127
- acc[lvl] = idx;
128
- return acc;
129
- },
130
- {}
131
- );
132
- const isEnabled = (methodLevel) => levelIndexByName[methodLevel] >= levelIndexByName[threshold];
133
- const wrap = (consoleMethod, methodLevel) => isEnabled(methodLevel) ? (entry) => {
134
- if (!prettyEnabled) {
135
- console[consoleMethod](viem.stringify({ level: methodLevel, ...entry }));
136
- return;
137
- }
138
- const { msg, ...rest } = entry;
139
- const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
140
- const level = methodLevel.toUpperCase();
141
- const extras = Object.entries(rest).map(([k, v]) => `${k}=${formatValue(v)}`).join(" ");
142
- const line = extras.length > 0 ? `${timestamp2} [${level}] ${msg} ${extras}` : `${timestamp2} [${level}] ${msg}`;
143
- console[consoleMethod](line);
144
- } : () => {
145
- };
146
- return {
147
- trace: wrap("trace", "trace"),
148
- debug: wrap("debug", "debug"),
149
- info: wrap("info", "info"),
150
- warn: wrap("warn", "warn"),
151
- error: wrap("error", "error"),
152
- fatal: wrap("error", "fatal")
153
- };
154
- }
155
- function silentLogger() {
156
- const noop = () => {
157
- };
158
- return { trace: noop, debug: noop, info: noop, warn: noop, error: noop, fatal: noop };
159
- }
160
- var loggerContext = new async_hooks.AsyncLocalStorage();
161
- function runWithLogger(logger, fn) {
162
- return loggerContext.run(logger, fn);
163
- }
164
- function getLogger() {
165
- return loggerContext.getStore() ?? defaultLogger();
166
- }
167
- function formatValue(value) {
168
- if (value === null || value === void 0 || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
169
- return String(value);
170
- }
171
- if (typeof value === "string") {
172
- if (value.includes(" ")) return JSON.stringify(value);
173
- return value;
174
- }
175
- try {
176
- return viem.stringify(value);
177
- } catch {
178
- try {
179
- return JSON.stringify(value);
180
- } catch {
181
- return String(value);
182
- }
92
+ var Oracle = [
93
+ {
94
+ type: "function",
95
+ name: "price",
96
+ inputs: [],
97
+ outputs: [{ name: "", type: "uint256" }],
98
+ stateMutability: "view"
183
99
  }
184
- }
185
-
186
- // src/utils/index.ts
187
- var utils_exports = {};
188
- __export(utils_exports, {
189
- BaseError: () => BaseError,
190
- Time: () => time_exports,
191
- batch: () => batch,
192
- batchMulticall: () => batchMulticall,
193
- fromSnakeCase: () => fromSnakeCase,
194
- lazy: () => lazy,
195
- max: () => max,
196
- min: () => min,
197
- poll: () => poll,
198
- retry: () => retry,
199
- toSnakeCase: () => toSnakeCase,
200
- wait: () => wait
201
- });
202
-
203
- // src/utils/BigMath.ts
204
- function max(a, b) {
205
- return a > b ? a : b;
206
- }
207
- function min(a, b) {
208
- return a < b ? a : b;
209
- }
210
-
211
- // src/utils/batch.ts
212
- function* batch(array2, batchSize) {
213
- for (let i = 0; i < array2.length; i += batchSize) {
214
- yield array2.slice(i, i + batchSize);
100
+ ];
101
+ var ERC4626 = [
102
+ {
103
+ type: "function",
104
+ name: "asset",
105
+ inputs: [],
106
+ outputs: [{ name: "", type: "address" }],
107
+ stateMutability: "view"
215
108
  }
216
- }
217
-
218
- // src/utils/retry.ts
219
- var retry = async (fn, attempts = 3, delayMs = 50) => {
220
- let lastErr;
221
- for (let i = 0; i < attempts; i++) {
222
- try {
223
- return await fn();
224
- } catch (err) {
225
- lastErr = err;
226
- if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
227
- }
109
+ ];
110
+ var MetaMorphoFactory = [
111
+ {
112
+ type: "function",
113
+ name: "isMetaMorpho",
114
+ inputs: [{ name: "target", type: "address" }],
115
+ outputs: [{ name: "", type: "bool" }],
116
+ stateMutability: "view"
228
117
  }
229
- throw lastErr;
230
- };
231
-
232
- // src/utils/batchMulticall.ts
233
- async function batchMulticall(parameters) {
234
- const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
235
- const results = [];
236
- for (const callsBatch of batch(calls, batchSize)) {
237
- const batchResults = await retry(
238
- () => client.multicall({
239
- allowFailure: false,
240
- contracts: callsBatch,
241
- ...blockNumber ? { blockNumber } : {}
242
- }),
243
- retryAttempts,
244
- retryDelayMs
245
- );
246
- results.push(...batchResults);
118
+ ];
119
+ var MetaMorpho = [
120
+ {
121
+ type: "function",
122
+ name: "withdrawQueue",
123
+ inputs: [{ name: "index", type: "uint256" }],
124
+ outputs: [{ name: "", type: "bytes32" }],
125
+ stateMutability: "view"
126
+ },
127
+ {
128
+ type: "function",
129
+ name: "withdrawQueueLength",
130
+ inputs: [],
131
+ outputs: [{ name: "", type: "uint256" }],
132
+ stateMutability: "view"
133
+ },
134
+ {
135
+ type: "function",
136
+ name: "maxWithdraw",
137
+ inputs: [{ name: "owner", type: "address" }],
138
+ outputs: [{ name: "", type: "uint256" }],
139
+ stateMutability: "view"
247
140
  }
248
- return results;
249
- }
250
-
251
- // src/utils/Errors.ts
252
- var Errors_exports = {};
253
- __export(Errors_exports, {
254
- BaseError: () => BaseError
255
- });
256
- var BaseError = class _BaseError extends Error {
257
- details;
258
- shortMessage;
259
- cause;
260
- name = "BaseError";
261
- constructor(shortMessage, options = {}) {
262
- const details = (() => {
263
- if (options.cause instanceof _BaseError) {
264
- if (options.cause.details) return options.cause.details;
265
- if (options.cause.shortMessage) return options.cause.shortMessage;
266
- }
267
- if (options.cause && "details" in options.cause && typeof options.cause.details === "string")
268
- return options.cause.details;
269
- if (options.cause?.message) return options.cause.message;
270
- return options.details;
271
- })();
272
- const message = [
273
- shortMessage || "An error occurred.",
274
- ...options.metaMessages ? ["", ...options.metaMessages] : [],
275
- ...details ? ["", details ? `Details: ${details}` : void 0] : []
276
- ].filter((x) => typeof x === "string").join("\n");
277
- super(message, options.cause ? { cause: options.cause } : void 0);
278
- this.cause = options.cause;
279
- this.details = details;
280
- this.shortMessage = shortMessage;
281
- }
282
- walk(fn) {
283
- return walk(this, fn);
141
+ ];
142
+ var Morpho = [
143
+ {
144
+ type: "function",
145
+ name: "collateralOf",
146
+ inputs: [
147
+ {
148
+ name: "",
149
+ type: "address",
150
+ internalType: "address"
151
+ },
152
+ {
153
+ name: "",
154
+ type: "bytes32",
155
+ internalType: "bytes32"
156
+ },
157
+ {
158
+ name: "",
159
+ type: "address",
160
+ internalType: "address"
161
+ }
162
+ ],
163
+ outputs: [
164
+ {
165
+ name: "",
166
+ type: "uint256",
167
+ internalType: "uint256"
168
+ }
169
+ ],
170
+ stateMutability: "view"
171
+ },
172
+ {
173
+ type: "function",
174
+ name: "debtOf",
175
+ inputs: [
176
+ {
177
+ name: "",
178
+ type: "address",
179
+ internalType: "address"
180
+ },
181
+ {
182
+ name: "",
183
+ type: "bytes32",
184
+ internalType: "bytes32"
185
+ }
186
+ ],
187
+ outputs: [
188
+ {
189
+ name: "",
190
+ type: "uint256",
191
+ internalType: "uint256"
192
+ }
193
+ ],
194
+ stateMutability: "view"
195
+ },
196
+ {
197
+ type: "function",
198
+ name: "market",
199
+ inputs: [
200
+ {
201
+ name: "id",
202
+ type: "bytes32",
203
+ internalType: "Id"
204
+ }
205
+ ],
206
+ outputs: [
207
+ {
208
+ name: "totalSupplyAssets",
209
+ type: "uint128",
210
+ internalType: "uint128"
211
+ },
212
+ {
213
+ name: "totalSupplyShares",
214
+ type: "uint128",
215
+ internalType: "uint128"
216
+ },
217
+ {
218
+ name: "totalBorrowAssets",
219
+ type: "uint128",
220
+ internalType: "uint128"
221
+ },
222
+ {
223
+ name: "totalBorrowShares",
224
+ type: "uint128",
225
+ internalType: "uint128"
226
+ },
227
+ {
228
+ name: "lastUpdate",
229
+ type: "uint128",
230
+ internalType: "uint128"
231
+ },
232
+ {
233
+ name: "fee",
234
+ type: "uint128",
235
+ internalType: "uint128"
236
+ }
237
+ ],
238
+ stateMutability: "view"
239
+ },
240
+ {
241
+ type: "function",
242
+ name: "position",
243
+ inputs: [
244
+ {
245
+ name: "id",
246
+ type: "bytes32",
247
+ internalType: "Id"
248
+ },
249
+ {
250
+ name: "user",
251
+ type: "address",
252
+ internalType: "address"
253
+ }
254
+ ],
255
+ outputs: [
256
+ {
257
+ name: "supplyShares",
258
+ type: "uint256",
259
+ internalType: "uint256"
260
+ },
261
+ {
262
+ name: "borrowShares",
263
+ type: "uint128",
264
+ internalType: "uint128"
265
+ },
266
+ {
267
+ name: "collateral",
268
+ type: "uint128",
269
+ internalType: "uint128"
270
+ }
271
+ ],
272
+ stateMutability: "view"
284
273
  }
285
- };
286
- function walk(err, fn) {
287
- if (fn?.(err)) return err;
288
- if (err && typeof err === "object" && "cause" in err && err.cause) return walk(err.cause, fn);
289
- return fn ? null : err;
290
- }
274
+ ];
291
275
 
292
- // src/utils/Format.ts
293
- var Format_exports = {};
294
- __export(Format_exports, {
295
- fromSnakeCase: () => fromSnakeCase,
296
- toSnakeCase: () => toSnakeCase
276
+ // src/core/Callback.ts
277
+ var Callback_exports = {};
278
+ __export(Callback_exports, {
279
+ CallbackType: () => CallbackType,
280
+ WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
281
+ decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
282
+ decodeSellERC20Callback: () => decodeSellERC20Callback,
283
+ encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
284
+ encodeSellERC20Callback: () => encodeSellERC20Callback
297
285
  });
298
- function toSnakeCase(obj) {
299
- return stringifyBigint(
300
- processObject(
301
- obj,
302
- (s2) => s2.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`),
303
- (value) => typeof value === "string" && viem.isAddress(value.toLowerCase()) ? viem.getAddress(value.toLowerCase()) : value
304
- )
305
- );
286
+ var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
287
+ CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
288
+ CallbackType2["BuyVaultV1Callback"] = "buy_vault_v1_callback";
289
+ CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
290
+ return CallbackType2;
291
+ })(CallbackType || {});
292
+ var WhitelistedCallbackAddresses = {
293
+ ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
294
+ ["buy_vault_v1_callback" /* BuyVaultV1Callback */]: [
295
+ "0x3333333333333333333333333333333333333333",
296
+ "0x4444444444444444444444444444444444444444"
297
+ // @TODO: update once deployed and add mapping per chain if needed
298
+ ].map((address) => address.toLowerCase()),
299
+ ["sell_erc20_callback" /* SellERC20Callback */]: [
300
+ "0x1111111111111111111111111111111111111111",
301
+ "0x2222222222222222222222222222222222222222"
302
+ // @TODO: update once deployed and add mapping per chain if needed
303
+ ].map((address) => address.toLowerCase())
304
+ };
305
+ function decodeBuyVaultV1Callback(data) {
306
+ if (!data || data === "0x") throw new Error("Empty callback data");
307
+ try {
308
+ const [vaults, amounts] = viem.decodeAbiParameters(
309
+ [{ type: "address[]" }, { type: "uint256[]" }],
310
+ data
311
+ );
312
+ if (vaults.length !== amounts.length) {
313
+ throw new Error("Mismatched array lengths");
314
+ }
315
+ return vaults.map((v, i) => ({ vault: v, amount: amounts[i] }));
316
+ } catch (_) {
317
+ throw new Error("Invalid BuyVaultV1Callback callback data");
318
+ }
306
319
  }
307
- function fromSnakeCase(obj) {
308
- return processObject(
309
- obj,
310
- (s2) => viem.isAddress(s2.toLowerCase()) ? s2 : s2.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
311
- (value) => typeof value === "string" && viem.isAddress(value.toLowerCase()) ? value.toLowerCase() : value
312
- );
320
+ function decodeSellERC20Callback(data) {
321
+ if (!data || data === "0x") throw new Error("Empty callback data");
322
+ try {
323
+ const [collaterals, amounts] = viem.decodeAbiParameters(
324
+ [{ type: "address[]" }, { type: "uint256[]" }],
325
+ data
326
+ );
327
+ if (collaterals.length !== amounts.length) {
328
+ throw new Error("Mismatched array lengths");
329
+ }
330
+ return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
331
+ } catch (_) {
332
+ throw new Error("Invalid SellERC20Callback callback data");
333
+ }
313
334
  }
314
- function processObject(obj, fnKey, fnValue) {
315
- if (typeof obj !== "object" || obj === null) return obj;
316
- if (Array.isArray(obj)) return obj.map((item) => processObject(item, fnKey, fnValue));
317
- return Object.entries(obj).reduce(
318
- (acc, [key, value]) => {
319
- const newKey = fnKey(key);
320
- acc[newKey] = typeof value === "object" && value !== null ? processObject(value, fnKey, fnValue) : fnValue(value);
321
- return acc;
322
- },
323
- {}
335
+ function encodeBuyVaultV1Callback(parameters) {
336
+ return viem.encodeAbiParameters(
337
+ [{ type: "address[]" }, { type: "uint256[]" }],
338
+ [parameters.vaults, parameters.amounts]
324
339
  );
325
340
  }
326
- function stringifyBigint(value) {
327
- if (typeof value === "bigint") return value.toString();
328
- if (Array.isArray(value)) return value.map(stringifyBigint);
329
- if (value && typeof value === "object") {
330
- const out = {};
331
- for (const [k, v] of Object.entries(value)) {
332
- out[k] = stringifyBigint(v);
333
- }
334
- return out;
335
- }
336
- return value;
341
+ function encodeSellERC20Callback(parameters) {
342
+ return viem.encodeAbiParameters(
343
+ [{ type: "address[]" }, { type: "uint256[]" }],
344
+ [parameters.collaterals, parameters.amounts]
345
+ );
337
346
  }
338
347
 
339
- // src/utils/lazy.ts
340
- function lazy(pollFn) {
341
- return () => async function* () {
342
- let active = true;
343
- let resolveNext = null;
344
- const queue = [];
345
- const wait2 = () => new Promise((resolve) => {
346
- resolveNext = resolve;
347
- });
348
- const emit = (item) => {
349
- queue.push(item);
350
- resolveNext?.();
351
- resolveNext = null;
352
- };
353
- let unpoll = null;
354
- const stop = () => {
355
- active = false;
356
- unpoll?.();
357
- resolveNext?.();
358
- resolveNext = null;
359
- };
360
- unpoll = pollFn(emit, { stop });
361
- try {
362
- while (active) {
363
- if (queue.length === 0) await wait2();
364
- while (queue.length > 0 && active) yield queue.shift();
365
- }
366
- } finally {
367
- stop();
368
- }
369
- }();
370
- }
348
+ // src/core/Chain.ts
349
+ var Chain_exports = {};
350
+ __export(Chain_exports, {
351
+ ChainId: () => ChainId,
352
+ InvalidBatchSizeError: () => InvalidBatchSizeError,
353
+ InvalidBlockRangeError: () => InvalidBlockRangeError,
354
+ InvalidBlockWindowError: () => InvalidBlockWindowError,
355
+ MissingBlockNumberError: () => MissingBlockNumberError,
356
+ chainIds: () => chainIds,
357
+ chainNames: () => chainNames,
358
+ chains: () => chains,
359
+ getChain: () => getChain,
360
+ getWhitelistedChains: () => getWhitelistedChains,
361
+ streamLogs: () => streamLogs
362
+ });
371
363
 
372
- // src/utils/wait.ts
373
- async function wait(time) {
374
- return new Promise((res) => setTimeout(res, time));
364
+ // src/utils/BigMath.ts
365
+ function max(a, b) {
366
+ return a > b ? a : b;
367
+ }
368
+ function min(a, b) {
369
+ return a < b ? a : b;
375
370
  }
376
371
 
377
- // src/utils/poll.ts
378
- function poll(fn, { interval }) {
379
- let active = true;
380
- const unwatch = () => active = false;
381
- const watch2 = async () => {
382
- await wait(interval);
383
- const poll2 = async () => {
384
- if (!active) return;
385
- await fn({ unpoll: unwatch });
386
- await wait(interval);
387
- poll2();
388
- };
389
- poll2();
390
- };
391
- watch2();
392
- return unwatch;
372
+ // src/utils/batch.ts
373
+ function* batch(array2, batchSize) {
374
+ for (let i = 0; i < array2.length; i += batchSize) {
375
+ yield array2.slice(i, i + batchSize);
376
+ }
393
377
  }
394
378
 
395
- // src/utils/time.ts
396
- var time_exports = {};
397
- __export(time_exports, {
398
- max: () => max2,
399
- now: () => now
379
+ // src/utils/Errors.ts
380
+ var Errors_exports = {};
381
+ __export(Errors_exports, {
382
+ BaseError: () => BaseError
400
383
  });
401
- function now() {
402
- return Math.floor(Date.now() / 1e3);
403
- }
404
- function max2() {
405
- return 864e16;
384
+ var BaseError = class _BaseError extends Error {
385
+ details;
386
+ shortMessage;
387
+ cause;
388
+ name = "BaseError";
389
+ constructor(shortMessage, options = {}) {
390
+ const details = (() => {
391
+ if (options.cause instanceof _BaseError) {
392
+ if (options.cause.details) return options.cause.details;
393
+ if (options.cause.shortMessage) return options.cause.shortMessage;
394
+ }
395
+ if (options.cause && "details" in options.cause && typeof options.cause.details === "string")
396
+ return options.cause.details;
397
+ if (options.cause?.message) return options.cause.message;
398
+ return options.details;
399
+ })();
400
+ const message = [
401
+ shortMessage || "An error occurred.",
402
+ ...options.metaMessages ? ["", ...options.metaMessages] : [],
403
+ ...details ? ["", details ? `Details: ${details}` : void 0] : []
404
+ ].filter((x) => typeof x === "string").join("\n");
405
+ super(message, options.cause ? { cause: options.cause } : void 0);
406
+ this.cause = options.cause;
407
+ this.details = details;
408
+ this.shortMessage = shortMessage;
409
+ }
410
+ walk(fn) {
411
+ return walk(this, fn);
412
+ }
413
+ };
414
+ function walk(err, fn) {
415
+ if (fn?.(err)) return err;
416
+ if (err && typeof err === "object" && "cause" in err && err.cause) return walk(err.cause, fn);
417
+ return fn ? null : err;
406
418
  }
407
419
 
408
- // src/services/RouterAdmin.ts
409
- function create(parameters) {
410
- const collector = "admin";
420
+ // src/core/Chain.ts
421
+ var chainNames = ["ethereum", "base", "ethereum-virtual-testnet", "anvil"];
422
+ var ChainId = {
423
+ ETHEREUM: BigInt(chains$1.mainnet.id),
424
+ BASE: BigInt(chains$1.base.id),
425
+ "ETHEREUM-VIRTUAL-TESTNET": 109111114n,
426
+ ANVIL: 505050505n
427
+ // random id to not clash with other chains
428
+ };
429
+ var chainIds = new Set(Object.values(ChainId));
430
+ var chainNameLookup = new Map(Object.entries(ChainId).map(([key, value]) => [value, key]));
431
+ function getChain(chainId) {
432
+ const chainName = chainNameLookup.get(chainId)?.toLowerCase();
433
+ if (!chainName) {
434
+ return void 0;
435
+ }
436
+ return chains[chainName];
437
+ }
438
+ var getWhitelistedChains = () => {
439
+ return [chains.ethereum, chains.base, chains["ethereum-virtual-testnet"], chains.anvil];
440
+ };
441
+ var chains = {
442
+ ethereum: {
443
+ ...chains$1.mainnet,
444
+ id: ChainId.ETHEREUM,
445
+ name: "ethereum",
446
+ whitelistedAssets: new Set(
447
+ [
448
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
449
+ // USDC
450
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F",
451
+ // DAI
452
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
453
+ // WETH
454
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
455
+ // WBTC
456
+ ].map((address) => address.toLowerCase())
457
+ ),
458
+ morpho: "0x0000000000000000000000000000000000000000",
459
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
460
+ mempool: {
461
+ address: "0x0000000000000000000000000000000000000000",
462
+ deploymentBlock: 23347674,
463
+ reindexBuffer: 10
464
+ },
465
+ vaultV1Factory: {
466
+ "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
467
+ "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
468
+ }
469
+ },
470
+ base: {
471
+ ...chains$1.base,
472
+ id: ChainId.BASE,
473
+ name: "base",
474
+ whitelistedAssets: new Set(
475
+ [
476
+ "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
477
+ // USDC
478
+ "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
479
+ // DAI
480
+ "0x4200000000000000000000000000000000000006",
481
+ // WETH
482
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
483
+ // WBTC
484
+ ].map((address) => address.toLowerCase())
485
+ ),
486
+ morpho: "0x0000000000000000000000000000000000000000",
487
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
488
+ mempool: {
489
+ address: "0x0000000000000000000000000000000000000000",
490
+ deploymentBlock: 35449942,
491
+ reindexBuffer: 10
492
+ },
493
+ vaultV1Factory: {
494
+ "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
495
+ "v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
496
+ }
497
+ },
498
+ "ethereum-virtual-testnet": {
499
+ ...chains$1.mainnet,
500
+ id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
501
+ name: "ethereum-virtual-testnet",
502
+ whitelistedAssets: new Set(
503
+ [
504
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
505
+ // USDC
506
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F",
507
+ // DAI
508
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
509
+ // WETH
510
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
511
+ // WBTC
512
+ ].map((address) => address.toLowerCase())
513
+ ),
514
+ morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
515
+ // @TODO: This is mock Consumed contract, update with Terms once stable
516
+ morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
517
+ mempool: {
518
+ address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
519
+ deploymentBlock: 23224302,
520
+ reindexBuffer: 10
521
+ },
522
+ vaultV1Factory: {
523
+ "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
524
+ "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
525
+ }
526
+ },
527
+ anvil: {
528
+ ...chains$1.anvil,
529
+ id: ChainId.ANVIL,
530
+ name: "anvil",
531
+ whitelistedAssets: new Set(
532
+ [
533
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
534
+ // USDC
535
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F",
536
+ // DAI
537
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
538
+ // WETH
539
+ "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
540
+ // WBTC
541
+ ].map((address) => address.toLowerCase())
542
+ ),
543
+ morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
544
+ morphoBlue: "0x0000000000000000000000000000000000000000",
545
+ // Set dynamically in tests
546
+ mempool: {
547
+ address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
548
+ deploymentBlock: 23223727,
549
+ reindexBuffer: 10
550
+ },
551
+ vaultV1Factory: {
552
+ "v1.0": "0x0000000000000000000000000000000000000000",
553
+ "v1.1": "0x0000000000000000000000000000000000000000"
554
+ }
555
+ }
556
+ };
557
+ var MAX_BATCH_SIZE = 1e4;
558
+ var DEFAULT_BATCH_SIZE = 2500;
559
+ var MAX_BLOCK_WINDOW = 2e3;
560
+ var DEFAULT_BLOCK_WINDOW = 500;
561
+ async function* streamLogs(parameters) {
411
562
  const {
412
563
  client,
413
- chain,
414
- withTransaction,
415
- options: { maxBatchSize = 25, maxBlockNumber } = {}
564
+ contractAddress,
565
+ event,
566
+ blockNumberGte,
567
+ blockNumberLte,
568
+ order = "desc",
569
+ options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
416
570
  } = parameters;
417
- const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
418
- let finalizedBlock = null;
419
- let unfinalizedBlocks = [];
420
- const logger = getLogger();
421
- let isMaxBlockNumberReached = false;
422
- let tick = 0;
423
- return {
424
- syncBlock: async () => {
425
- if (isMaxBlockNumberReached) return;
426
- const head = await client.getBlock({
427
- blockTag: "latest",
428
- includeTransactions: false
429
- });
430
- await withTransaction(async ({ chainStore, collectorStore }) => {
431
- const { epoch, blockNumber: latestSavedBlockNumber } = await chainStore.getBlockNumber(
432
- chain.id
433
- );
434
- if (maxBlockNumberBI !== void 0 && head.number >= maxBlockNumberBI) {
435
- logger.info({
436
- msg: `Head is greater than max block number`,
437
- collector,
438
- chainId: chain.id,
439
- block_number: head.number,
440
- max_block_number: maxBlockNumber
441
- });
442
- isMaxBlockNumberReached = true;
443
- await chainStore.saveBlockNumber({
444
- chainId: chain.id,
445
- blockNumber: maxBlockNumber,
446
- epoch: epoch + 1n
447
- });
448
- await Promise.all(
449
- names.map(
450
- async (collectorName) => collectorStore.saveBlockNumber({
451
- collectorName,
452
- chainId: chain.id,
453
- blockNumber: maxBlockNumber,
454
- epoch: epoch + 1n
455
- })
456
- )
457
- );
458
- return;
459
- }
460
- finalizedBlock = await fetchFinalizedBlock({
461
- tick,
462
- client,
463
- chain,
464
- logger,
465
- collector,
466
- unfinalizedBlocks,
467
- previousFinalizedBlock: finalizedBlock
468
- });
469
- tick++;
470
- let {
471
- block: returnedBlock,
472
- didReorgHappened,
473
- unfinalizedBlocks: newUnfinalizedBlocks
474
- } = await reconcile({
475
- client,
476
- block: head,
477
- unfinalizedBlocks,
478
- finalizedBlock,
479
- logger,
480
- collector,
481
- chain,
482
- maxBatchSize
483
- });
484
- unfinalizedBlocks = newUnfinalizedBlocks;
485
- const blockNumber = Number(returnedBlock.number);
486
- didReorgHappened = didReorgHappened || blockNumber < latestSavedBlockNumber;
487
- await chainStore.saveBlockNumber({
488
- chainId: chain.id,
489
- blockNumber,
490
- epoch: didReorgHappened ? epoch + 1n : epoch
491
- });
492
- if (didReorgHappened) {
493
- await Promise.all(
494
- names.map(
495
- async (collectorName) => collectorStore.saveBlockNumber({
496
- collectorName,
497
- chainId: chain.id,
498
- blockNumber,
499
- epoch: epoch + 1n
500
- })
501
- )
502
- );
503
- }
504
- });
505
- }
506
- };
507
- }
508
- var commonAncestor = (block, unfinalizedBlocks) => {
509
- const parent = unfinalizedBlocks.find((b) => b.hash === block.parentHash);
510
- if (parent) return parent;
511
- return null;
512
- };
513
- var fetchFinalizedBlock = async (parameters) => {
514
- let { tick, client, chain, logger, collector, unfinalizedBlocks, previousFinalizedBlock } = parameters;
515
- let finalizedBlock = previousFinalizedBlock;
516
- if (tick % 20 === 0 || previousFinalizedBlock === null) {
517
- finalizedBlock = await client.getBlock({
518
- blockTag: "finalized",
519
- includeTransactions: false
571
+ if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
572
+ if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
573
+ if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
574
+ const latestBlock = (await actions.getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
575
+ let toBlock = 0n;
576
+ if (order === "asc")
577
+ toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
578
+ if (order === "desc")
579
+ toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
580
+ let fromBlock = 0n;
581
+ if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
582
+ if (order === "desc")
583
+ fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
584
+ if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
585
+ if (order === "desc") fromBlock = max(fromBlock, toBlock - BigInt(blockWindow));
586
+ if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
587
+ let streaming = true;
588
+ while (streaming) {
589
+ const logs = await actions.getLogs(client, {
590
+ address: contractAddress,
591
+ event,
592
+ fromBlock,
593
+ toBlock
520
594
  });
521
- if (finalizedBlock === null || finalizedBlock.number === null) {
522
- const msg = "Failed to get finalized block";
523
- logger.fatal({ collector, chainId: chain.id, msg });
524
- throw new Error(msg);
595
+ logs.sort((a, b) => {
596
+ if (a.blockNumber !== b.blockNumber)
597
+ return order === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
598
+ if (a.transactionIndex !== b.transactionIndex)
599
+ return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
600
+ return order === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
601
+ });
602
+ for (const logBatch of batch(logs, maxBatchSize)) {
603
+ if (logBatch.length === 0) break;
604
+ yield {
605
+ logs: logBatch,
606
+ blockNumber: logBatch.length === maxBatchSize ? (
607
+ // if the batch is full, return the last block number, block numbers are always sorted
608
+ Number(logBatch[logBatch.length - 1]?.blockNumber)
609
+ ) : (
610
+ // if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
611
+ order === "asc" ? Number(toBlock) : Number(fromBlock)
612
+ )
613
+ };
614
+ }
615
+ streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
616
+ if (order === "asc") {
617
+ fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
618
+ toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
619
+ }
620
+ if (order === "desc") {
621
+ const lowerBound = BigInt(blockNumberGte || 0);
622
+ const windowSize = BigInt(blockWindow);
623
+ const nextToBlock = max(fromBlock - 1n, lowerBound);
624
+ const nextFromBlock = max(nextToBlock - windowSize, lowerBound);
625
+ toBlock = nextToBlock;
626
+ fromBlock = nextFromBlock;
525
627
  }
526
- unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number >= finalizedBlock.number);
527
628
  }
528
- if (finalizedBlock.number === null || finalizedBlock.hash === null || finalizedBlock.parentHash === null) {
529
- const msg = "Failed to get finalized block";
530
- logger.fatal({ collector, chainId: chain.id, msg });
531
- throw new Error(msg);
629
+ yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
630
+ return;
631
+ }
632
+ var InvalidBlockRangeError = class extends BaseError {
633
+ name = "Chain.InvalidBlockRangeError";
634
+ constructor(fromBlock, toBlock) {
635
+ super(
636
+ `Invalid block range while streaming data from chain. From block ${fromBlock} to block ${toBlock}.`
637
+ );
532
638
  }
533
- return {
534
- hash: finalizedBlock.hash,
535
- number: finalizedBlock.number,
536
- parentHash: finalizedBlock.parentHash
537
- };
538
639
  };
539
- var reconcile = async (parameters) => {
540
- let { client, block, unfinalizedBlocks, finalizedBlock, logger, collector, chain, maxBatchSize } = parameters;
541
- if (block.hash === null || block.number === null || block.parentHash === null)
542
- throw new Error("Failed to get block");
543
- const latestBlock = unfinalizedBlocks[unfinalizedBlocks.length - 1];
544
- if (latestBlock === void 0) {
545
- const newBlock2 = {
546
- hash: block.hash,
547
- number: block.number,
548
- parentHash: block.parentHash
549
- };
550
- unfinalizedBlocks.push(newBlock2);
551
- return { block: newBlock2, didReorgHappened: false, unfinalizedBlocks };
552
- }
553
- if (latestBlock.hash === block.hash)
554
- return { block: latestBlock, didReorgHappened: false, unfinalizedBlocks };
555
- if (latestBlock.number >= block.number) {
556
- const ancestor = commonAncestor(block, unfinalizedBlocks) || finalizedBlock;
557
- logger.info({
558
- msg: `Reorg detected`,
559
- collector,
560
- chain_id: chain.id,
561
- ancestor: ancestor.number,
562
- block_range: [latestBlock.number, block.number]
563
- });
564
- unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number <= ancestor.number);
565
- return { block: ancestor, didReorgHappened: true, unfinalizedBlocks };
566
- }
567
- if (latestBlock.number + 1n < block.number) {
568
- logger.debug({
569
- collector,
570
- chain_id: chain.id,
571
- block_range: [latestBlock.number, block.number],
572
- msg: `Missing blocks`
573
- });
574
- const missingBlockNumbers = (() => {
575
- const missingBlockNumbers2 = [];
576
- let start2 = latestBlock.number + 1n;
577
- const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
578
- while (start2 < threshold) {
579
- missingBlockNumbers2.push(start2);
580
- start2 = start2 + 1n;
581
- }
582
- return missingBlockNumbers2;
583
- })();
584
- const missingBlocks = await Promise.all(
585
- missingBlockNumbers.map(
586
- (blockNumber) => retry(async () => await client.getBlock({ blockNumber, includeTransactions: false }))
587
- )
640
+ var InvalidBlockWindowError = class extends BaseError {
641
+ name = "Chain.InvalidBlockWindowError";
642
+ constructor(blockWindow) {
643
+ super(
644
+ `Invalid block window while streaming data from chain. Maximum is ${MAX_BLOCK_WINDOW}. Got ${blockWindow}.`
588
645
  );
589
- for (const missingBlock of missingBlocks) {
590
- const { block: returnedBlock, didReorgHappened } = await reconcile({
591
- client,
592
- block: missingBlock,
593
- unfinalizedBlocks,
594
- finalizedBlock,
595
- logger,
596
- collector,
597
- chain,
598
- maxBatchSize
599
- });
600
- if (returnedBlock.number !== missingBlock.number)
601
- return { block: returnedBlock, didReorgHappened, unfinalizedBlocks };
602
- }
603
- return reconcile({
604
- client,
605
- block,
606
- unfinalizedBlocks,
607
- finalizedBlock,
608
- logger,
609
- collector,
610
- chain,
611
- maxBatchSize
612
- });
613
646
  }
614
- if (block.parentHash !== latestBlock.hash) {
615
- const ancestor = commonAncestor(block, unfinalizedBlocks) || finalizedBlock;
616
- logger.info({
617
- msg: `Reorg detected`,
618
- collector,
619
- chain_id: chain.id,
620
- ancestor: ancestor.number,
621
- block_range: [latestBlock.number, block.number]
622
- });
623
- unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number <= ancestor.number);
624
- return { block: ancestor, didReorgHappened: true, unfinalizedBlocks };
647
+ };
648
+ var InvalidBatchSizeError = class extends BaseError {
649
+ name = "Chain.InvalidBatchSizeError";
650
+ constructor(maxBatchSize) {
651
+ super(
652
+ `Invalid batch size while streaming data from chain. Maximum is ${MAX_BATCH_SIZE}. Got ${maxBatchSize}.`
653
+ );
654
+ }
655
+ };
656
+ var MissingBlockNumberError = class extends BaseError {
657
+ name = "Chain.MissingBlockNumberError";
658
+ constructor() {
659
+ super("Missing block number when streaming data from chain in ascending order.");
625
660
  }
626
- const newBlock = {
627
- hash: block.hash,
628
- number: block.number,
629
- parentHash: block.parentHash
630
- };
631
- unfinalizedBlocks.push(newBlock);
632
- return { block: newBlock, didReorgHappened: false, unfinalizedBlocks };
633
661
  };
634
662
 
635
- // src/collectors/Collector.ts
636
- var names = [
637
- "mempool_offers",
638
- "consumed_events",
639
- "buy_with_empty_callback_liquidity",
640
- "buy_vault_v1_callback_liquidity",
641
- "sell_erc20_callback_liquidity"
642
- ];
643
- function create2({
644
- name,
645
- collect,
646
- client,
647
- chain,
648
- withTransaction,
649
- collectorStore,
650
- options
651
- }) {
652
- const admin = create({
653
- client,
654
- chain,
655
- withTransaction,
656
- options
657
- });
658
- return {
659
- name,
660
- chain,
661
- collect: lazy((emit) => {
662
- const collector = name;
663
- const logger = getLogger();
664
- logger.info({
665
- msg: `Collector started`,
666
- collector,
667
- chain_id: chain.id,
668
- interval: options.interval
669
- });
670
- return poll(
671
- async () => {
672
- let { blockNumber: lastBlockNumber, epoch } = await collectorStore.getBlockNumber({
673
- collectorName: name,
674
- chainId: chain.id
675
- });
676
- await admin.syncBlock();
677
- lastBlockNumber = await collect({
678
- chain,
679
- client,
680
- collector: name,
681
- epoch,
682
- lastBlockNumber,
683
- withTransaction
684
- });
685
- emit(lastBlockNumber);
686
- },
687
- { interval: options.interval }
688
- );
689
- })
690
- };
691
- }
692
- function start(collector) {
693
- let stopped = false;
694
- const it = collector.collect();
695
- (async () => {
696
- while (!stopped) await it.next();
697
- await it.return();
698
- })();
699
- return () => {
700
- stopped = true;
701
- };
702
- }
703
-
704
- // src/mempool/index.ts
705
- var mempool_exports = {};
706
- __export(mempool_exports, {
707
- ChainIdMismatchError: () => ChainIdMismatchError,
708
- ViemClientError: () => ViemClientError,
709
- WalletAccountNotSetError: () => WalletAccountNotSetError,
710
- add: () => add,
711
- connect: () => connect,
712
- from: () => from6,
713
- get: () => get,
714
- watch: () => watch
663
+ // src/core/Collateral.ts
664
+ var Collateral_exports = {};
665
+ __export(Collateral_exports, {
666
+ CollateralSchema: () => CollateralSchema,
667
+ CollateralsSchema: () => CollateralsSchema,
668
+ from: () => from2
715
669
  });
670
+ var transformHex = (val, ctx) => {
671
+ if (viem.isHex(val)) return val;
672
+ ctx.addIssue({
673
+ code: "invalid_format",
674
+ input: val,
675
+ format: "hex",
676
+ error: "not a hex"
677
+ });
678
+ return z6__namespace.NEVER;
679
+ };
680
+ var transformAddress = (val, ctx) => {
681
+ if (viem.isAddress(val.toLowerCase())) return val.toLowerCase();
682
+ ctx.addIssue({
683
+ code: "invalid_format",
684
+ input: val,
685
+ format: "address",
686
+ error: "not a valid address"
687
+ });
688
+ return z6__namespace.NEVER;
689
+ };
716
690
 
717
- // src/core/Abi.ts
718
- var Abi_exports = {};
719
- __export(Abi_exports, {
720
- ERC4626: () => ERC4626,
721
- MetaMorpho: () => MetaMorpho,
722
- MetaMorphoFactory: () => MetaMorphoFactory,
723
- Morpho: () => Morpho,
724
- Oracle: () => Oracle
691
+ // src/core/LLTV.ts
692
+ var LLTV_exports = {};
693
+ __export(LLTV_exports, {
694
+ InvalidLLTVError: () => InvalidLLTVError,
695
+ InvalidOptionError: () => InvalidOptionError,
696
+ LLTVSchema: () => LLTVSchema,
697
+ Options: () => Options,
698
+ from: () => from
725
699
  });
726
- var Oracle = [
727
- {
728
- type: "function",
729
- name: "price",
730
- inputs: [],
731
- outputs: [{ name: "", type: "uint256" }],
732
- stateMutability: "view"
700
+ var Options = [0.385, 0.5, 0.625, 0.77, 0.86, 0.915, 0.945, 0.965, 0.98];
701
+ var LLTV_SCALED = Options.map((lltv) => BigInt(lltv * 10 ** 18));
702
+ function from(lltv) {
703
+ if (typeof lltv === "bigint" && !LLTV_SCALED.includes(lltv)) throw new InvalidLLTVError(lltv);
704
+ if (typeof lltv === "bigint") return lltv;
705
+ if (typeof lltv === "number" && !Options.includes(lltv)) throw new InvalidOptionError(lltv);
706
+ return BigInt(lltv * 10 ** 18);
707
+ }
708
+ var InvalidOptionError = class extends BaseError {
709
+ name = "LLTV.InvalidOptionError";
710
+ constructor(input) {
711
+ super(
712
+ `Invalid LLTV option. Input: "${input}". Accepted values are: ${Options.map(
713
+ (option) => `"${option}"`
714
+ ).join(", ")}.`
715
+ );
733
716
  }
734
- ];
735
- var ERC4626 = [
736
- {
737
- type: "function",
738
- name: "asset",
739
- inputs: [],
740
- outputs: [{ name: "", type: "address" }],
741
- stateMutability: "view"
717
+ };
718
+ var InvalidLLTVError = class extends BaseError {
719
+ name = "LLTV.InvalidLLTVError";
720
+ constructor(input) {
721
+ super(
722
+ `Invalid LLTV. Input: "${input}". Accepted values are: ${LLTV_SCALED.map(
723
+ (option) => `"${option}"`
724
+ ).join(", ")}.`
725
+ );
742
726
  }
743
- ];
744
- var MetaMorphoFactory = [
727
+ };
728
+ var LLTVSchema = z6__namespace.bigint({ coerce: true }).refine(
729
+ (lltv) => {
730
+ try {
731
+ from(lltv);
732
+ return true;
733
+ } catch (_) {
734
+ return false;
735
+ }
736
+ },
745
737
  {
746
- type: "function",
747
- name: "isMetaMorpho",
748
- inputs: [{ name: "target", type: "address" }],
749
- outputs: [{ name: "", type: "bool" }],
750
- stateMutability: "view"
738
+ error: () => {
739
+ 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)";
740
+ }
751
741
  }
752
- ];
753
- var MetaMorpho = [
754
- {
755
- type: "function",
756
- name: "withdrawQueue",
757
- inputs: [{ name: "index", type: "uint256" }],
758
- outputs: [{ name: "", type: "bytes32" }],
759
- stateMutability: "view"
742
+ ).transform((lltv) => from(lltv));
743
+
744
+ // src/core/Collateral.ts
745
+ var CollateralSchema = z6__namespace.object({
746
+ asset: z6__namespace.string().transform(transformAddress),
747
+ oracle: z6__namespace.string().transform(transformAddress),
748
+ lltv: LLTVSchema
749
+ });
750
+ var CollateralsSchema = z6__namespace.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine(
751
+ (collaterals) => {
752
+ for (let i = 1; i < collaterals.length; i++) {
753
+ if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
754
+ return false;
755
+ }
756
+ }
757
+ return true;
760
758
  },
761
759
  {
762
- type: "function",
763
- name: "withdrawQueueLength",
764
- inputs: [],
765
- outputs: [{ name: "", type: "uint256" }],
766
- stateMutability: "view"
760
+ message: "Collaterals must be sorted alphabetically by address"
761
+ }
762
+ ).refine(
763
+ (collaterals) => {
764
+ const uniqueAssets = /* @__PURE__ */ new Set();
765
+ for (const collateral of collaterals) {
766
+ const assetAddress = collateral.asset.toLowerCase();
767
+ if (uniqueAssets.has(assetAddress)) {
768
+ return false;
769
+ }
770
+ uniqueAssets.add(assetAddress);
771
+ }
772
+ return true;
767
773
  },
768
774
  {
769
- type: "function",
770
- name: "maxWithdraw",
771
- inputs: [{ name: "owner", type: "address" }],
772
- outputs: [{ name: "", type: "uint256" }],
773
- stateMutability: "view"
775
+ message: "Collaterals must not contain duplicate assets"
774
776
  }
775
- ];
776
- var Morpho = [
777
- {
778
- type: "function",
779
- name: "collateralOf",
780
- inputs: [
781
- {
782
- name: "",
783
- type: "address",
784
- internalType: "address"
785
- },
786
- {
787
- name: "",
788
- type: "bytes32",
789
- internalType: "bytes32"
790
- },
791
- {
792
- name: "",
793
- type: "address",
794
- internalType: "address"
795
- }
796
- ],
797
- outputs: [
798
- {
799
- name: "",
800
- type: "uint256",
801
- internalType: "uint256"
802
- }
803
- ],
804
- stateMutability: "view"
805
- },
806
- {
807
- type: "function",
808
- name: "debtOf",
809
- inputs: [
810
- {
811
- name: "",
812
- type: "address",
813
- internalType: "address"
814
- },
815
- {
816
- name: "",
817
- type: "bytes32",
818
- internalType: "bytes32"
819
- }
820
- ],
821
- outputs: [
822
- {
823
- name: "",
824
- type: "uint256",
825
- internalType: "uint256"
826
- }
827
- ],
828
- stateMutability: "view"
829
- },
830
- {
831
- type: "function",
832
- name: "market",
833
- inputs: [
834
- {
835
- name: "id",
836
- type: "bytes32",
837
- internalType: "Id"
838
- }
839
- ],
840
- outputs: [
841
- {
842
- name: "totalSupplyAssets",
843
- type: "uint128",
844
- internalType: "uint128"
845
- },
846
- {
847
- name: "totalSupplyShares",
848
- type: "uint128",
849
- internalType: "uint128"
850
- },
851
- {
852
- name: "totalBorrowAssets",
853
- type: "uint128",
854
- internalType: "uint128"
855
- },
856
- {
857
- name: "totalBorrowShares",
858
- type: "uint128",
859
- internalType: "uint128"
860
- },
861
- {
862
- name: "lastUpdate",
863
- type: "uint128",
864
- internalType: "uint128"
865
- },
866
- {
867
- name: "fee",
868
- type: "uint128",
869
- internalType: "uint128"
870
- }
871
- ],
872
- stateMutability: "view"
873
- },
874
- {
875
- type: "function",
876
- name: "position",
877
- inputs: [
878
- {
879
- name: "id",
880
- type: "bytes32",
881
- internalType: "Id"
882
- },
883
- {
884
- name: "user",
885
- type: "address",
886
- internalType: "address"
887
- }
888
- ],
889
- outputs: [
890
- {
891
- name: "supplyShares",
892
- type: "uint256",
893
- internalType: "uint256"
894
- },
895
- {
896
- name: "borrowShares",
897
- type: "uint128",
898
- internalType: "uint128"
899
- },
900
- {
901
- name: "collateral",
902
- type: "uint128",
903
- internalType: "uint128"
904
- }
905
- ],
906
- stateMutability: "view"
907
- }
908
- ];
777
+ );
778
+ var from2 = (parameters) => {
779
+ return {
780
+ asset: parameters.asset.toLowerCase(),
781
+ lltv: from(parameters.lltv),
782
+ oracle: parameters.oracle.toLowerCase()
783
+ };
784
+ };
909
785
 
910
- // src/core/Callback.ts
911
- var Callback_exports = {};
912
- __export(Callback_exports, {
913
- CallbackType: () => CallbackType,
914
- WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
915
- decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
916
- decodeSellERC20Callback: () => decodeSellERC20Callback,
917
- encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
918
- encodeSellERC20Callback: () => encodeSellERC20Callback
786
+ // src/core/Liquidity.ts
787
+ var Liquidity_exports = {};
788
+ __export(Liquidity_exports, {
789
+ calculateMaxDebt: () => calculateMaxDebt,
790
+ generateAllowancePoolId: () => generateAllowancePoolId,
791
+ generateBalancePoolId: () => generateBalancePoolId,
792
+ generateBuyVaultCallbackPoolId: () => generateBuyVaultCallbackPoolId,
793
+ generateDebtPoolId: () => generateDebtPoolId,
794
+ generateMarketLiquidityPoolId: () => generateMarketLiquidityPoolId,
795
+ generateObligationCollateralPoolId: () => generateObligationCollateralPoolId,
796
+ generateSellERC20CallbackPoolId: () => generateSellERC20CallbackPoolId,
797
+ generateUserVaultPositionPoolId: () => generateUserVaultPositionPoolId,
798
+ generateVaultPositionPoolId: () => generateVaultPositionPoolId
919
799
  });
920
- var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
921
- CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
922
- CallbackType2["BuyVaultV1Callback"] = "buy_vault_v1_callback";
923
- CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
924
- return CallbackType2;
925
- })(CallbackType || {});
926
- var WhitelistedCallbackAddresses = {
927
- ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
928
- ["buy_vault_v1_callback" /* BuyVaultV1Callback */]: [
929
- "0x3333333333333333333333333333333333333333",
930
- "0x4444444444444444444444444444444444444444"
931
- // @TODO: update once deployed and add mapping per chain if needed
932
- ].map((address) => address.toLowerCase()),
933
- ["sell_erc20_callback" /* SellERC20Callback */]: [
934
- "0x1111111111111111111111111111111111111111",
935
- "0x2222222222222222222222222222222222222222"
936
- // @TODO: update once deployed and add mapping per chain if needed
937
- ].map((address) => address.toLowerCase())
938
- };
939
- function decodeBuyVaultV1Callback(data) {
940
- if (!data || data === "0x") throw new Error("Empty callback data");
941
- try {
942
- const [vaults, amounts] = viem.decodeAbiParameters(
943
- [{ type: "address[]" }, { type: "uint256[]" }],
944
- data
945
- );
946
- if (vaults.length !== amounts.length) {
947
- throw new Error("Mismatched array lengths");
948
- }
949
- return vaults.map((v, i) => ({ vault: v, amount: amounts[i] }));
950
- } catch (_) {
951
- throw new Error("Invalid BuyVaultV1Callback callback data");
952
- }
800
+ function calculateMaxDebt(amount, oraclePrice, lltv) {
801
+ const ORACLE_PRICE_SCALE = 10n ** 36n;
802
+ const PRECISION = 10n ** 18n;
803
+ const collateralQuoted = amount * oraclePrice / ORACLE_PRICE_SCALE;
804
+ const maxDebt = collateralQuoted * lltv / PRECISION;
805
+ return maxDebt;
953
806
  }
954
- function decodeSellERC20Callback(data) {
955
- if (!data || data === "0x") throw new Error("Empty callback data");
956
- try {
957
- const [collaterals, amounts] = viem.decodeAbiParameters(
958
- [{ type: "address[]" }, { type: "uint256[]" }],
959
- data
960
- );
961
- if (collaterals.length !== amounts.length) {
962
- throw new Error("Mismatched array lengths");
963
- }
964
- return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
965
- } catch (_) {
966
- throw new Error("Invalid SellERC20Callback callback data");
967
- }
807
+ function generateBalancePoolId(parameters) {
808
+ const { user, chainId, token } = parameters;
809
+ return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
968
810
  }
969
- function encodeBuyVaultV1Callback(parameters) {
970
- return viem.encodeAbiParameters(
971
- [{ type: "address[]" }, { type: "uint256[]" }],
972
- [parameters.vaults, parameters.amounts]
973
- );
811
+ function generateAllowancePoolId(parameters) {
812
+ const { user, chainId, token } = parameters;
813
+ return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
974
814
  }
975
- function encodeSellERC20Callback(parameters) {
976
- return viem.encodeAbiParameters(
977
- [{ type: "address[]" }, { type: "uint256[]" }],
978
- [parameters.collaterals, parameters.amounts]
979
- );
815
+ function generateSellERC20CallbackPoolId(parameters) {
816
+ const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
817
+ return `${user}-${chainId.toString()}-${obligationId2}-${token}-${offerHash}-sell_erc20_callback`.toLowerCase();
818
+ }
819
+ function generateObligationCollateralPoolId(parameters) {
820
+ const { user, chainId, obligationId: obligationId2, token } = parameters;
821
+ return `${user}-${chainId.toString()}-${obligationId2}-${token}-obligation-collateral`.toLowerCase();
822
+ }
823
+ function generateBuyVaultCallbackPoolId(parameters) {
824
+ const { user, chainId, vault, offerHash } = parameters;
825
+ return `${user}-${chainId.toString()}-${vault}-${offerHash}-${"buy_vault_v1_callback" /* BuyVaultV1Callback */}`.toLowerCase();
826
+ }
827
+ function generateDebtPoolId(parameters) {
828
+ const { user, chainId, obligationId: obligationId2 } = parameters;
829
+ return `${user}-${chainId.toString()}-${obligationId2}-debt`.toLowerCase();
830
+ }
831
+ function generateUserVaultPositionPoolId(parameters) {
832
+ const { user, chainId, vault } = parameters;
833
+ return `${user}-${chainId.toString()}-${vault}-user-vault-position`.toLowerCase();
834
+ }
835
+ function generateVaultPositionPoolId(parameters) {
836
+ const { vault, chainId, marketId } = parameters;
837
+ return `${vault}-${chainId.toString()}-${marketId}-vault-position`.toLowerCase();
838
+ }
839
+ function generateMarketLiquidityPoolId(parameters) {
840
+ const { chainId, marketId } = parameters;
841
+ return `${chainId.toString()}-${marketId}-market-liquidity`.toLowerCase();
980
842
  }
981
843
 
982
- // src/core/Chain.ts
983
- var Chain_exports = {};
984
- __export(Chain_exports, {
985
- ChainId: () => ChainId,
986
- InvalidBatchSizeError: () => InvalidBatchSizeError,
987
- InvalidBlockRangeError: () => InvalidBlockRangeError,
988
- InvalidBlockWindowError: () => InvalidBlockWindowError,
989
- MissingBlockNumberError: () => MissingBlockNumberError,
990
- chainIds: () => chainIds,
991
- chainNames: () => chainNames,
992
- chains: () => chains,
993
- getChain: () => getChain,
994
- getWhitelistedChains: () => getWhitelistedChains,
995
- streamLogs: () => streamLogs
844
+ // src/core/Maturity.ts
845
+ var Maturity_exports = {};
846
+ __export(Maturity_exports, {
847
+ InvalidDateError: () => InvalidDateError,
848
+ InvalidFormatError: () => InvalidFormatError,
849
+ InvalidOptionError: () => InvalidOptionError2,
850
+ MaturitySchema: () => MaturitySchema,
851
+ from: () => from3
996
852
  });
997
- var chainNames = ["ethereum", "base", "ethereum-virtual-testnet", "anvil"];
998
- var ChainId = {
999
- ETHEREUM: BigInt(chains$1.mainnet.id),
1000
- BASE: BigInt(chains$1.base.id),
1001
- "ETHEREUM-VIRTUAL-TESTNET": 109111114n,
1002
- ANVIL: 505050505n
1003
- // random id to not clash with other chains
1004
- };
1005
- var chainIds = new Set(Object.values(ChainId));
1006
- var chainNameLookup = new Map(Object.entries(ChainId).map(([key, value]) => [value, key]));
1007
- function getChain(chainId) {
1008
- const chainName = chainNameLookup.get(chainId)?.toLowerCase();
1009
- if (!chainName) {
1010
- return void 0;
1011
- }
1012
- return chains[chainName];
1013
- }
1014
- var getWhitelistedChains = () => {
1015
- return [chains.ethereum, chains.base, chains["ethereum-virtual-testnet"], chains.anvil];
1016
- };
1017
- var chains = {
1018
- ethereum: {
1019
- ...chains$1.mainnet,
1020
- id: ChainId.ETHEREUM,
1021
- name: "ethereum",
1022
- whitelistedAssets: new Set(
1023
- [
1024
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1025
- // USDC
1026
- "0x6B175474E89094C44Da98b954EedeAC495271d0F"
1027
- // DAI
1028
- ].map((address) => address.toLowerCase())
1029
- ),
1030
- morpho: "0x0000000000000000000000000000000000000000",
1031
- morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
1032
- mempool: {
1033
- address: "0x0000000000000000000000000000000000000000",
1034
- deploymentBlock: 23347674,
1035
- reindexBuffer: 10
1036
- },
1037
- vaultV1Factory: {
1038
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
1039
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
1040
- }
1041
- },
1042
- base: {
1043
- ...chains$1.base,
1044
- id: ChainId.BASE,
1045
- name: "base",
1046
- whitelistedAssets: new Set(
1047
- [
1048
- "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
1049
- // USDC
1050
- "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
1051
- // DAI
1052
- ].map((address) => address.toLowerCase())
1053
- ),
1054
- morpho: "0x0000000000000000000000000000000000000000",
1055
- morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
1056
- mempool: {
1057
- address: "0x0000000000000000000000000000000000000000",
1058
- deploymentBlock: 35449942,
1059
- reindexBuffer: 10
1060
- },
1061
- vaultV1Factory: {
1062
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
1063
- "v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
1064
- }
1065
- },
1066
- "ethereum-virtual-testnet": {
1067
- ...chains$1.mainnet,
1068
- id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
1069
- name: "ethereum-virtual-testnet",
1070
- whitelistedAssets: new Set(
1071
- [
1072
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1073
- // USDC
1074
- "0x6B175474E89094C44Da98b954EedeAC495271d0F"
1075
- // DAI
1076
- ].map((address) => address.toLowerCase())
1077
- ),
1078
- morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
1079
- // @TODO: This is mock Consumed contract, update with Terms once stable
1080
- morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
1081
- mempool: {
1082
- address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
1083
- deploymentBlock: 23224302,
1084
- reindexBuffer: 10
1085
- },
1086
- vaultV1Factory: {
1087
- "v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
1088
- "v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
853
+ var MaturitySchema = z6__namespace.number().int().refine(
854
+ (maturity) => {
855
+ try {
856
+ from3(maturity);
857
+ return true;
858
+ } catch (_e) {
859
+ return false;
1089
860
  }
1090
861
  },
1091
- anvil: {
1092
- ...chains$1.anvil,
1093
- id: ChainId.ANVIL,
1094
- name: "anvil",
1095
- whitelistedAssets: new Set(
1096
- [
1097
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1098
- // USDC
1099
- "0x6B175474E89094C44Da98b954EedeAC495271d0F"
1100
- // DAI
1101
- ].map((address) => address.toLowerCase())
1102
- ),
1103
- morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
1104
- morphoBlue: "0x0000000000000000000000000000000000000000",
1105
- // Set dynamically in tests
1106
- mempool: {
1107
- address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
1108
- deploymentBlock: 23223727,
1109
- reindexBuffer: 10
1110
- },
1111
- vaultV1Factory: {
1112
- "v1.0": "0x0000000000000000000000000000000000000000",
1113
- "v1.1": "0x0000000000000000000000000000000000000000"
862
+ {
863
+ error: (issue) => {
864
+ try {
865
+ const maturityDate = new Date(issue.input * 1e3);
866
+ 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).`;
867
+ } catch (_) {
868
+ 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).`;
869
+ }
1114
870
  }
1115
871
  }
872
+ ).transform((maturity) => maturity);
873
+ var MaturityOptions = {
874
+ end_of_week: () => endOfWeek(),
875
+ end_of_next_week: () => endOfNextWeek(),
876
+ end_of_month: () => endOfMonth(),
877
+ end_of_next_month: () => endOfNextMonth(),
878
+ end_of_quarter: () => endOfQuarter(),
879
+ end_of_next_quarter: () => endOfNextQuarter()
1116
880
  };
1117
- var MAX_BATCH_SIZE = 1e4;
1118
- var DEFAULT_BATCH_SIZE = 2500;
1119
- var MAX_BLOCK_WINDOW = 2e3;
1120
- var DEFAULT_BLOCK_WINDOW = 500;
1121
- async function* streamLogs(parameters) {
1122
- const {
1123
- client,
1124
- contractAddress,
1125
- event,
1126
- blockNumberGte,
1127
- blockNumberLte,
1128
- order = "desc",
1129
- options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
1130
- } = parameters;
1131
- if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
1132
- if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
1133
- if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
1134
- const latestBlock = (await actions.getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
1135
- let toBlock = 0n;
1136
- if (order === "asc")
1137
- toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
1138
- if (order === "desc")
1139
- toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
1140
- let fromBlock = 0n;
1141
- if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
1142
- if (order === "desc")
1143
- fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
1144
- if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
1145
- if (order === "desc") fromBlock = max(fromBlock, toBlock - BigInt(blockWindow));
1146
- if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
1147
- let streaming = true;
1148
- while (streaming) {
1149
- const logs = await actions.getLogs(client, {
1150
- address: contractAddress,
1151
- event,
1152
- fromBlock,
1153
- toBlock
1154
- });
1155
- logs.sort((a, b) => {
1156
- if (a.blockNumber !== b.blockNumber)
1157
- return order === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
1158
- if (a.transactionIndex !== b.transactionIndex)
1159
- return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
1160
- return order === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
1161
- });
1162
- for (const logBatch of batch(logs, maxBatchSize)) {
1163
- if (logBatch.length === 0) break;
1164
- yield {
1165
- logs: logBatch,
1166
- blockNumber: logBatch.length === maxBatchSize ? (
1167
- // if the batch is full, return the last block number, block numbers are always sorted
1168
- Number(logBatch[logBatch.length - 1]?.blockNumber)
1169
- ) : (
1170
- // if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
1171
- order === "asc" ? Number(toBlock) : Number(fromBlock)
1172
- )
1173
- };
1174
- }
1175
- streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
1176
- if (order === "asc") {
1177
- fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
1178
- toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
1179
- }
1180
- if (order === "desc") {
1181
- const lowerBound = BigInt(blockNumberGte || 0);
1182
- const windowSize = BigInt(blockWindow);
1183
- const nextToBlock = max(fromBlock - 1n, lowerBound);
1184
- const nextFromBlock = max(nextToBlock - windowSize, lowerBound);
1185
- toBlock = nextToBlock;
1186
- fromBlock = nextFromBlock;
1187
- }
881
+ function from3(ts) {
882
+ if (typeof ts === "string") {
883
+ if (ts in MaturityOptions) return MaturityOptions[ts]();
884
+ throw new InvalidOptionError2(ts);
1188
885
  }
1189
- yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
1190
- return;
886
+ if (typeof ts === "number" && ts > 1e12) throw new InvalidFormatError();
887
+ if (!Object.values(MaturityOptions).some((option) => option() === ts))
888
+ throw new InvalidDateError(ts);
889
+ return ts;
1191
890
  }
1192
- var InvalidBlockRangeError = class extends BaseError {
1193
- name = "Chain.InvalidBlockRangeError";
1194
- constructor(fromBlock, toBlock) {
1195
- super(
1196
- `Invalid block range while streaming data from chain. From block ${fromBlock} to block ${toBlock}.`
1197
- );
891
+ var endOfWeek = () => fridayOfWeek(0);
892
+ var endOfNextWeek = () => fridayOfWeek(1);
893
+ var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
894
+ var endOfNextMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth() + 1);
895
+ var endOfQuarter = () => lastFridayOfQuarter(0);
896
+ var endOfNextQuarter = () => lastFridayOfQuarter(1);
897
+ var fridayOfWeek = (weeksAhead = 0) => {
898
+ const now2 = /* @__PURE__ */ new Date();
899
+ const today15H = new Date(
900
+ Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth(), now2.getUTCDate(), 15)
901
+ );
902
+ let daysUntilFriday = (5 - today15H.getUTCDay() + 7) % 7;
903
+ if (daysUntilFriday === 0 && now2.getTime() >= today15H.getTime()) {
904
+ daysUntilFriday = 7;
1198
905
  }
906
+ const friday = new Date(today15H);
907
+ friday.setUTCDate(friday.getUTCDate() + daysUntilFriday + weeksAhead * 7);
908
+ return friday.getTime() / 1e3;
1199
909
  };
1200
- var InvalidBlockWindowError = class extends BaseError {
1201
- name = "Chain.InvalidBlockWindowError";
1202
- constructor(blockWindow) {
1203
- super(
1204
- `Invalid block window while streaming data from chain. Maximum is ${MAX_BLOCK_WINDOW}. Got ${blockWindow}.`
1205
- );
910
+ var lastFridayOfMonth = (year, month) => {
911
+ const lastDayOfMonth15H = new Date(Date.UTC(year, month + 1, 0, 15));
912
+ while (lastDayOfMonth15H.getUTCDay() !== 5) {
913
+ lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
1206
914
  }
915
+ const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
916
+ return maturity;
1207
917
  };
1208
- var InvalidBatchSizeError = class extends BaseError {
1209
- name = "Chain.InvalidBatchSizeError";
1210
- constructor(maxBatchSize) {
1211
- super(
1212
- `Invalid batch size while streaming data from chain. Maximum is ${MAX_BATCH_SIZE}. Got ${maxBatchSize}.`
1213
- );
1214
- }
918
+ var lastFridayOfQuarter = (quartersAhead = 0) => {
919
+ const now2 = /* @__PURE__ */ new Date();
920
+ const quarterIndex = Math.floor(now2.getUTCMonth() / 3) + quartersAhead;
921
+ const year = now2.getUTCFullYear() + Math.floor(quarterIndex / 4);
922
+ const quarter = quarterIndex % 4;
923
+ const lastMonth = quarter * 3 + 2;
924
+ return lastFridayOfMonth(year, lastMonth);
1215
925
  };
1216
- var MissingBlockNumberError = class extends BaseError {
1217
- name = "Chain.MissingBlockNumberError";
926
+ var InvalidFormatError = class extends BaseError {
927
+ name = "Maturity.InvalidFormatError";
1218
928
  constructor() {
1219
- super("Missing block number when streaming data from chain in ascending order.");
929
+ super("Invalid maturity format. Maturity should be expressed in seconds.");
1220
930
  }
1221
931
  };
1222
-
1223
- // src/core/Collateral.ts
1224
- var Collateral_exports = {};
1225
- __export(Collateral_exports, {
1226
- CollateralSchema: () => CollateralSchema,
1227
- CollateralsSchema: () => CollateralsSchema,
1228
- from: () => from2
1229
- });
1230
- var transformHex = (val, ctx) => {
1231
- if (viem.isHex(val)) return val;
1232
- ctx.addIssue({
1233
- code: "invalid_format",
1234
- input: val,
1235
- format: "hex",
1236
- error: "not a hex"
1237
- });
1238
- return z6__namespace.NEVER;
1239
- };
1240
- var transformAddress = (val, ctx) => {
1241
- if (viem.isAddress(val.toLowerCase())) return val.toLowerCase();
1242
- ctx.addIssue({
1243
- code: "invalid_format",
1244
- input: val,
1245
- format: "address",
1246
- error: "not a valid address"
1247
- });
1248
- return z6__namespace.NEVER;
1249
- };
1250
-
1251
- // src/core/LLTV.ts
1252
- var LLTV_exports = {};
1253
- __export(LLTV_exports, {
1254
- InvalidLLTVError: () => InvalidLLTVError,
1255
- InvalidOptionError: () => InvalidOptionError,
1256
- LLTVSchema: () => LLTVSchema,
1257
- Options: () => Options,
1258
- from: () => from
1259
- });
1260
- var Options = [0.385, 0.5, 0.625, 0.77, 0.86, 0.915, 0.945, 0.965, 0.98];
1261
- var LLTV_SCALED = Options.map((lltv) => BigInt(lltv * 10 ** 18));
1262
- function from(lltv) {
1263
- if (typeof lltv === "bigint" && !LLTV_SCALED.includes(lltv)) throw new InvalidLLTVError(lltv);
1264
- if (typeof lltv === "bigint") return lltv;
1265
- if (typeof lltv === "number" && !Options.includes(lltv)) throw new InvalidOptionError(lltv);
1266
- return BigInt(lltv * 10 ** 18);
1267
- }
1268
- var InvalidOptionError = class extends BaseError {
1269
- name = "LLTV.InvalidOptionError";
932
+ var InvalidDateError = class extends BaseError {
933
+ name = "Maturity.InvalidDateError";
1270
934
  constructor(input) {
1271
935
  super(
1272
- `Invalid LLTV option. Input: "${input}". Accepted values are: ${Options.map(
1273
- (option) => `"${option}"`
1274
- ).join(", ")}.`
936
+ `Invalid maturity date. Input: "${input}". Accepted values are: ${Object.values(
937
+ MaturityOptions
938
+ ).map((option) => `"${option()}"`).join(", ")}.`
1275
939
  );
1276
940
  }
1277
941
  };
1278
- var InvalidLLTVError = class extends BaseError {
1279
- name = "LLTV.InvalidLLTVError";
942
+ var InvalidOptionError2 = class extends BaseError {
943
+ name = "Maturity.InvalidOptionError";
1280
944
  constructor(input) {
1281
945
  super(
1282
- `Invalid LLTV. Input: "${input}". Accepted values are: ${LLTV_SCALED.map(
1283
- (option) => `"${option}"`
1284
- ).join(", ")}.`
946
+ `Invalid maturity option. Input: "${input}". Accepted values are: ${Object.keys(
947
+ MaturityOptions
948
+ ).map((option) => `"${option}"`).join(", ")}.`
1285
949
  );
1286
950
  }
1287
951
  };
1288
- var LLTVSchema = z6__namespace.bigint({ coerce: true }).refine(
1289
- (lltv) => {
1290
- try {
1291
- from(lltv);
1292
- return true;
1293
- } catch (_) {
1294
- return false;
1295
- }
1296
- },
1297
- {
1298
- error: () => {
1299
- 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)";
1300
- }
1301
- }
1302
- ).transform((lltv) => from(lltv));
1303
952
 
1304
- // src/core/Collateral.ts
1305
- var CollateralSchema = z6__namespace.object({
1306
- asset: z6__namespace.string().transform(transformAddress),
1307
- oracle: z6__namespace.string().transform(transformAddress),
1308
- lltv: LLTVSchema
953
+ // src/core/Obligation.ts
954
+ var Obligation_exports = {};
955
+ __export(Obligation_exports, {
956
+ CollateralsAreNotSortedError: () => CollateralsAreNotSortedError,
957
+ InvalidObligationError: () => InvalidObligationError,
958
+ ObligationSchema: () => ObligationSchema,
959
+ from: () => from4,
960
+ fromSnakeCase: () => fromSnakeCase2,
961
+ id: () => id,
962
+ random: () => random
1309
963
  });
1310
- var CollateralsSchema = z6__namespace.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine(
1311
- (collaterals) => {
1312
- for (let i = 1; i < collaterals.length; i++) {
1313
- if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
1314
- return false;
1315
- }
1316
- }
1317
- return true;
1318
- },
1319
- {
1320
- message: "Collaterals must be sorted alphabetically by address"
1321
- }
1322
- ).refine(
1323
- (collaterals) => {
1324
- const uniqueAssets = /* @__PURE__ */ new Set();
1325
- for (const collateral of collaterals) {
1326
- const assetAddress = collateral.asset.toLowerCase();
1327
- if (uniqueAssets.has(assetAddress)) {
1328
- return false;
1329
- }
1330
- uniqueAssets.add(assetAddress);
964
+
965
+ // src/utils/Format.ts
966
+ var Format_exports = {};
967
+ __export(Format_exports, {
968
+ fromSnakeCase: () => fromSnakeCase,
969
+ toSnakeCase: () => toSnakeCase
970
+ });
971
+ function toSnakeCase(obj) {
972
+ return stringifyBigint(
973
+ processObject(
974
+ obj,
975
+ (s2) => s2.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`),
976
+ (value) => typeof value === "string" && viem.isAddress(value.toLowerCase()) ? viem.getAddress(value.toLowerCase()) : value
977
+ )
978
+ );
979
+ }
980
+ function fromSnakeCase(obj) {
981
+ return processObject(
982
+ obj,
983
+ (s2) => viem.isAddress(s2.toLowerCase()) ? s2 : s2.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
984
+ (value) => typeof value === "string" && viem.isAddress(value.toLowerCase()) ? value.toLowerCase() : value
985
+ );
986
+ }
987
+ function processObject(obj, fnKey, fnValue) {
988
+ if (typeof obj !== "object" || obj === null) return obj;
989
+ if (Array.isArray(obj)) return obj.map((item) => processObject(item, fnKey, fnValue));
990
+ return Object.entries(obj).reduce(
991
+ (acc, [key, value]) => {
992
+ const newKey = fnKey(key);
993
+ acc[newKey] = typeof value === "object" && value !== null ? processObject(value, fnKey, fnValue) : fnValue(value);
994
+ return acc;
995
+ },
996
+ {}
997
+ );
998
+ }
999
+ function stringifyBigint(value) {
1000
+ if (typeof value === "bigint") return value.toString();
1001
+ if (Array.isArray(value)) return value.map(stringifyBigint);
1002
+ if (value && typeof value === "object") {
1003
+ const out = {};
1004
+ for (const [k, v] of Object.entries(value)) {
1005
+ out[k] = stringifyBigint(v);
1331
1006
  }
1332
- return true;
1333
- },
1334
- {
1335
- message: "Collaterals must not contain duplicate assets"
1007
+ return out;
1336
1008
  }
1337
- );
1338
- var from2 = (parameters) => {
1339
- return {
1340
- asset: parameters.asset.toLowerCase(),
1341
- lltv: from(parameters.lltv),
1342
- oracle: parameters.oracle.toLowerCase()
1343
- };
1344
- };
1009
+ return value;
1010
+ }
1345
1011
 
1346
- // src/core/Cursor.ts
1347
- var Cursor_exports = {};
1348
- __export(Cursor_exports, {
1349
- decode: () => decode,
1350
- encode: () => encode,
1351
- validate: () => validate
1012
+ // src/core/Obligation.ts
1013
+ var ObligationSchema = z6__namespace.object({
1014
+ chainId: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1015
+ loanToken: z6__namespace.string().transform(transformAddress),
1016
+ collaterals: CollateralsSchema,
1017
+ maturity: MaturitySchema
1352
1018
  });
1353
- function validate(cursor) {
1354
- if (!cursor || typeof cursor !== "object") {
1355
- throw new Error("Cursor must be an object");
1356
- }
1357
- const c = cursor;
1358
- if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
1359
- throw new Error(
1360
- `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
1361
- );
1019
+ function from4(parameters) {
1020
+ try {
1021
+ const parsedObligation = ObligationSchema.parse({
1022
+ ...parameters,
1023
+ maturity: from3(parameters.maturity)
1024
+ });
1025
+ return {
1026
+ chainId: parsedObligation.chainId,
1027
+ loanToken: parsedObligation.loanToken.toLowerCase(),
1028
+ collaterals: parsedObligation.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)),
1029
+ maturity: parsedObligation.maturity
1030
+ };
1031
+ } catch (error2) {
1032
+ throw new InvalidObligationError(error2);
1362
1033
  }
1363
- if (!["asc", "desc"].includes(c.dir)) {
1364
- throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
1034
+ }
1035
+ function fromSnakeCase2(input) {
1036
+ return from4(fromSnakeCase(input));
1037
+ }
1038
+ function id(obligation) {
1039
+ let lastAsset = "";
1040
+ for (const collateral of obligation.collaterals) {
1041
+ const newAsset = collateral.asset.toLowerCase();
1042
+ if (newAsset.localeCompare(lastAsset) < 0) throw new CollateralsAreNotSortedError();
1043
+ lastAsset = newAsset;
1365
1044
  }
1366
- if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
1367
- throw new Error(
1368
- `Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
1369
- );
1370
- }
1371
- const validations = {
1372
- rate: {
1373
- field: "rate",
1374
- type: "string",
1375
- pattern: /^\d+$/,
1376
- error: "numeric string"
1377
- },
1378
- amount: {
1379
- field: "assets",
1380
- type: "string",
1381
- pattern: /^\d+$/,
1382
- error: "numeric string"
1383
- },
1384
- maturity: {
1385
- field: "maturity",
1386
- type: "number",
1387
- validator: (val) => val > 0,
1388
- error: "positive number"
1389
- },
1390
- expiry: {
1391
- field: "expiry",
1392
- type: "number",
1393
- validator: (val) => val > 0,
1394
- error: "positive number"
1395
- }
1396
- };
1397
- const validation = validations[c.sort];
1398
- if (!validation) {
1399
- throw new Error(`Invalid sort field: ${c.sort}`);
1400
- }
1401
- const fieldValue = c[validation.field];
1402
- if (!fieldValue) {
1403
- throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
1045
+ return viem.keccak256(
1046
+ viem.encodeAbiParameters(
1047
+ [
1048
+ { type: "uint256" },
1049
+ { type: "address" },
1050
+ {
1051
+ type: "tuple[]",
1052
+ components: [
1053
+ { type: "address", name: "token" },
1054
+ { type: "uint256", name: "lltv" },
1055
+ { type: "address", name: "oracle" }
1056
+ ]
1057
+ },
1058
+ { type: "uint256" }
1059
+ ],
1060
+ [
1061
+ obligation.chainId,
1062
+ obligation.loanToken.toLowerCase(),
1063
+ obligation.collaterals.map((c) => ({
1064
+ token: c.asset.toLowerCase(),
1065
+ lltv: c.lltv,
1066
+ oracle: c.oracle.toLowerCase()
1067
+ })),
1068
+ BigInt(obligation.maturity)
1069
+ ]
1070
+ )
1071
+ );
1072
+ }
1073
+ function random() {
1074
+ return from4({
1075
+ chainId: 1n,
1076
+ loanToken: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1077
+ collaterals: [
1078
+ from2({
1079
+ asset: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1080
+ oracle: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1081
+ lltv: 0.965
1082
+ })
1083
+ ],
1084
+ maturity: from3("end_of_next_quarter")
1085
+ });
1086
+ }
1087
+ var InvalidObligationError = class extends BaseError {
1088
+ name = "Obligation.InvalidObligationError";
1089
+ constructor(error2) {
1090
+ super("Invalid obligation.", { cause: error2 });
1404
1091
  }
1405
- if (typeof fieldValue !== validation.type) {
1406
- throw new Error(
1407
- `${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
1408
- );
1092
+ };
1093
+ var CollateralsAreNotSortedError = class extends BaseError {
1094
+ name = "Obligation.CollateralsAreNotSortedError";
1095
+ constructor() {
1096
+ super("Collaterals are not sorted alphabetically by address.");
1409
1097
  }
1410
- if (validation.pattern && !validation.pattern.test(fieldValue)) {
1411
- throw new Error(
1412
- `Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
1413
- );
1098
+ };
1099
+
1100
+ // src/core/Offer.ts
1101
+ var Offer_exports = {};
1102
+ __export(Offer_exports, {
1103
+ AccountNotSetError: () => AccountNotSetError,
1104
+ InvalidOfferError: () => InvalidOfferError,
1105
+ OfferHashSchema: () => OfferHashSchema,
1106
+ OfferSchema: () => OfferSchema,
1107
+ consumedEvent: () => consumedEvent,
1108
+ decode: () => decode,
1109
+ domain: () => domain,
1110
+ encode: () => encode,
1111
+ from: () => from5,
1112
+ fromConsumedLog: () => fromConsumedLog,
1113
+ fromSnakeCase: () => fromSnakeCase3,
1114
+ hash: () => hash,
1115
+ obligationId: () => obligationId,
1116
+ random: () => random2,
1117
+ sign: () => sign,
1118
+ toSnakeCase: () => toSnakeCase2,
1119
+ types: () => types
1120
+ });
1121
+
1122
+ // src/utils/index.ts
1123
+ var utils_exports = {};
1124
+ __export(utils_exports, {
1125
+ BaseError: () => BaseError,
1126
+ Time: () => time_exports,
1127
+ batch: () => batch,
1128
+ batchMulticall: () => batchMulticall,
1129
+ fromSnakeCase: () => fromSnakeCase,
1130
+ lazy: () => lazy,
1131
+ max: () => max,
1132
+ min: () => min,
1133
+ poll: () => poll,
1134
+ retry: () => retry,
1135
+ toSnakeCase: () => toSnakeCase,
1136
+ wait: () => wait
1137
+ });
1138
+
1139
+ // src/utils/retry.ts
1140
+ var retry = async (fn, attempts = 3, delayMs = 50) => {
1141
+ let lastErr;
1142
+ for (let i = 0; i < attempts; i++) {
1143
+ try {
1144
+ return await fn();
1145
+ } catch (err) {
1146
+ lastErr = err;
1147
+ if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
1148
+ }
1414
1149
  }
1415
- if (validation.validator && !validation.validator(fieldValue)) {
1416
- throw new Error(
1417
- `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
1150
+ throw lastErr;
1151
+ };
1152
+
1153
+ // src/utils/batchMulticall.ts
1154
+ async function batchMulticall(parameters) {
1155
+ const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
1156
+ const results = [];
1157
+ for (const callsBatch of batch(calls, batchSize)) {
1158
+ const batchResults = await retry(
1159
+ () => client.multicall({
1160
+ allowFailure: false,
1161
+ contracts: callsBatch,
1162
+ ...blockNumber ? { blockNumber } : {}
1163
+ }),
1164
+ retryAttempts,
1165
+ retryDelayMs
1418
1166
  );
1167
+ results.push(...batchResults);
1419
1168
  }
1420
- if (c.page !== void 0) {
1421
- if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
1422
- throw new Error("Invalid page: must be a positive integer");
1169
+ return results;
1170
+ }
1171
+
1172
+ // src/utils/lazy.ts
1173
+ function lazy(pollFn) {
1174
+ return () => async function* () {
1175
+ let active = true;
1176
+ let resolveNext = null;
1177
+ const queue = [];
1178
+ const wait2 = () => new Promise((resolve) => {
1179
+ resolveNext = resolve;
1180
+ });
1181
+ const emit = (item) => {
1182
+ queue.push(item);
1183
+ resolveNext?.();
1184
+ resolveNext = null;
1185
+ };
1186
+ let unpoll = null;
1187
+ const stop = () => {
1188
+ active = false;
1189
+ unpoll?.();
1190
+ resolveNext?.();
1191
+ resolveNext = null;
1192
+ };
1193
+ unpoll = pollFn(emit, { stop });
1194
+ try {
1195
+ while (active) {
1196
+ if (queue.length === 0) await wait2();
1197
+ while (queue.length > 0 && active) yield queue.shift();
1198
+ }
1199
+ } finally {
1200
+ stop();
1423
1201
  }
1424
- }
1425
- return true;
1202
+ }();
1426
1203
  }
1427
- function encode(c) {
1428
- return jsBase64.Base64.encodeURL(JSON.stringify(c));
1204
+
1205
+ // src/utils/wait.ts
1206
+ async function wait(time) {
1207
+ return new Promise((res) => setTimeout(res, time));
1429
1208
  }
1430
- function decode(token) {
1431
- if (!token) return null;
1432
- const decoded = JSON.parse(jsBase64.Base64.decode(token));
1433
- validate(decoded);
1434
- return decoded;
1209
+
1210
+ // src/utils/poll.ts
1211
+ function poll(fn, { interval }) {
1212
+ let active = true;
1213
+ const unwatch = () => active = false;
1214
+ const watch2 = async () => {
1215
+ await wait(interval);
1216
+ const poll2 = async () => {
1217
+ if (!active) return;
1218
+ await fn({ unpoll: unwatch });
1219
+ await wait(interval);
1220
+ poll2();
1221
+ };
1222
+ poll2();
1223
+ };
1224
+ watch2();
1225
+ return unwatch;
1435
1226
  }
1436
1227
 
1437
- // src/core/Liquidity.ts
1438
- var Liquidity_exports = {};
1439
- __export(Liquidity_exports, {
1440
- calculateMaxDebt: () => calculateMaxDebt,
1441
- generateAllowancePoolId: () => generateAllowancePoolId,
1442
- generateBalancePoolId: () => generateBalancePoolId,
1443
- generateBuyVaultCallbackPoolId: () => generateBuyVaultCallbackPoolId,
1444
- generateDebtPoolId: () => generateDebtPoolId,
1445
- generateMarketLiquidityPoolId: () => generateMarketLiquidityPoolId,
1446
- generateObligationCollateralPoolId: () => generateObligationCollateralPoolId,
1447
- generateSellERC20CallbackPoolId: () => generateSellERC20CallbackPoolId,
1448
- generateUserVaultPositionPoolId: () => generateUserVaultPositionPoolId,
1449
- generateVaultPositionPoolId: () => generateVaultPositionPoolId
1228
+ // src/utils/time.ts
1229
+ var time_exports = {};
1230
+ __export(time_exports, {
1231
+ max: () => max2,
1232
+ now: () => now
1450
1233
  });
1451
- function calculateMaxDebt(amount, oraclePrice, lltv) {
1452
- const ORACLE_PRICE_SCALE = 10n ** 36n;
1453
- const PRECISION = 10n ** 18n;
1454
- const collateralQuoted = amount * oraclePrice / ORACLE_PRICE_SCALE;
1455
- const maxDebt = collateralQuoted * lltv / PRECISION;
1456
- return maxDebt;
1234
+ function now() {
1235
+ return Math.floor(Date.now() / 1e3);
1457
1236
  }
1458
- function generateBalancePoolId(parameters) {
1459
- const { user, chainId, token } = parameters;
1460
- return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
1461
- }
1462
- function generateAllowancePoolId(parameters) {
1463
- const { user, chainId, token } = parameters;
1464
- return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
1465
- }
1466
- function generateSellERC20CallbackPoolId(parameters) {
1467
- const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
1468
- return `${user}-${chainId.toString()}-${obligationId2}-${token}-${offerHash}-sell_erc20_callback`.toLowerCase();
1469
- }
1470
- function generateObligationCollateralPoolId(parameters) {
1471
- const { user, chainId, obligationId: obligationId2, token } = parameters;
1472
- return `${user}-${chainId.toString()}-${obligationId2}-${token}-obligation-collateral`.toLowerCase();
1473
- }
1474
- function generateBuyVaultCallbackPoolId(parameters) {
1475
- const { user, chainId, vault, offerHash } = parameters;
1476
- return `${user}-${chainId.toString()}-${vault}-${offerHash}-${"buy_vault_v1_callback" /* BuyVaultV1Callback */}`.toLowerCase();
1477
- }
1478
- function generateDebtPoolId(parameters) {
1479
- const { user, chainId, obligationId: obligationId2 } = parameters;
1480
- return `${user}-${chainId.toString()}-${obligationId2}-debt`.toLowerCase();
1481
- }
1482
- function generateUserVaultPositionPoolId(parameters) {
1483
- const { user, chainId, vault } = parameters;
1484
- return `${user}-${chainId.toString()}-${vault}-user-vault-position`.toLowerCase();
1485
- }
1486
- function generateVaultPositionPoolId(parameters) {
1487
- const { vault, chainId, marketId } = parameters;
1488
- return `${vault}-${chainId.toString()}-${marketId}-vault-position`.toLowerCase();
1489
- }
1490
- function generateMarketLiquidityPoolId(parameters) {
1491
- const { chainId, marketId } = parameters;
1492
- return `${chainId.toString()}-${marketId}-market-liquidity`.toLowerCase();
1237
+ function max2() {
1238
+ return 864e16;
1493
1239
  }
1494
1240
 
1495
- // src/core/Maturity.ts
1496
- var Maturity_exports = {};
1497
- __export(Maturity_exports, {
1498
- InvalidDateError: () => InvalidDateError,
1499
- InvalidFormatError: () => InvalidFormatError,
1500
- InvalidOptionError: () => InvalidOptionError2,
1501
- MaturitySchema: () => MaturitySchema,
1502
- from: () => from3
1503
- });
1504
- var MaturitySchema = z6__namespace.number().int().refine(
1505
- (maturity) => {
1506
- try {
1507
- from3(maturity);
1508
- return true;
1509
- } catch (_e) {
1510
- return false;
1511
- }
1512
- },
1513
- {
1514
- error: (issue) => {
1515
- try {
1516
- const maturityDate = new Date(issue.input * 1e3);
1517
- 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).`;
1518
- } catch (_) {
1519
- 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).`;
1520
- }
1521
- }
1522
- }
1523
- ).transform((maturity) => maturity);
1524
- var MaturityOptions = {
1525
- end_of_week: () => endOfWeek(),
1526
- end_of_next_week: () => endOfNextWeek(),
1527
- end_of_month: () => endOfMonth(),
1528
- end_of_next_month: () => endOfNextMonth(),
1529
- end_of_quarter: () => endOfQuarter(),
1530
- end_of_next_quarter: () => endOfNextQuarter()
1531
- };
1532
- function from3(ts) {
1533
- if (typeof ts === "string") {
1534
- if (ts in MaturityOptions) return MaturityOptions[ts]();
1535
- throw new InvalidOptionError2(ts);
1536
- }
1537
- if (typeof ts === "number" && ts > 1e12) throw new InvalidFormatError();
1538
- if (!Object.values(MaturityOptions).some((option) => option() === ts))
1539
- throw new InvalidDateError(ts);
1540
- return ts;
1541
- }
1542
- var endOfWeek = () => fridayOfWeek(0);
1543
- var endOfNextWeek = () => fridayOfWeek(1);
1544
- var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
1545
- var endOfNextMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth() + 1);
1546
- var endOfQuarter = () => lastFridayOfQuarter(0);
1547
- var endOfNextQuarter = () => lastFridayOfQuarter(1);
1548
- var fridayOfWeek = (weeksAhead = 0) => {
1549
- const now2 = /* @__PURE__ */ new Date();
1550
- const today15H = new Date(
1551
- Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth(), now2.getUTCDate(), 15)
1552
- );
1553
- let daysUntilFriday = (5 - today15H.getUTCDay() + 7) % 7;
1554
- if (daysUntilFriday === 0 && now2.getTime() >= today15H.getTime()) {
1555
- daysUntilFriday = 7;
1556
- }
1557
- const friday = new Date(today15H);
1558
- friday.setUTCDate(friday.getUTCDate() + daysUntilFriday + weeksAhead * 7);
1559
- return friday.getTime() / 1e3;
1560
- };
1561
- var lastFridayOfMonth = (year, month) => {
1562
- const lastDayOfMonth15H = new Date(Date.UTC(year, month + 1, 0, 15));
1563
- while (lastDayOfMonth15H.getUTCDay() !== 5) {
1564
- lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
1565
- }
1566
- const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
1567
- return maturity;
1568
- };
1569
- var lastFridayOfQuarter = (quartersAhead = 0) => {
1570
- const now2 = /* @__PURE__ */ new Date();
1571
- const quarterIndex = Math.floor(now2.getUTCMonth() / 3) + quartersAhead;
1572
- const year = now2.getUTCFullYear() + Math.floor(quarterIndex / 4);
1573
- const quarter = quarterIndex % 4;
1574
- const lastMonth = quarter * 3 + 2;
1575
- return lastFridayOfMonth(year, lastMonth);
1576
- };
1577
- var InvalidFormatError = class extends BaseError {
1578
- name = "Maturity.InvalidFormatError";
1579
- constructor() {
1580
- super("Invalid maturity format. Maturity should be expressed in seconds.");
1581
- }
1582
- };
1583
- var InvalidDateError = class extends BaseError {
1584
- name = "Maturity.InvalidDateError";
1585
- constructor(input) {
1586
- super(
1587
- `Invalid maturity date. Input: "${input}". Accepted values are: ${Object.values(
1588
- MaturityOptions
1589
- ).map((option) => `"${option()}"`).join(", ")}.`
1590
- );
1591
- }
1592
- };
1593
- var InvalidOptionError2 = class extends BaseError {
1594
- name = "Maturity.InvalidOptionError";
1595
- constructor(input) {
1596
- super(
1597
- `Invalid maturity option. Input: "${input}". Accepted values are: ${Object.keys(
1598
- MaturityOptions
1599
- ).map((option) => `"${option}"`).join(", ")}.`
1600
- );
1601
- }
1241
+ // src/core/Offer.ts
1242
+ var OfferHashSchema = z6__namespace.string().regex(/^0x[0-9a-fA-F]{64}$/, {
1243
+ message: "Hash must be a valid 32-byte hex string"
1244
+ }).transform(transformHex);
1245
+ var OfferSchema = (parameters) => {
1246
+ const { omitHash = false } = parameters || {};
1247
+ const now2 = time_exports.now();
1248
+ let base = z6__namespace.object({
1249
+ offering: z6__namespace.string().transform(transformAddress),
1250
+ assets: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1251
+ rate: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1252
+ maturity: MaturitySchema,
1253
+ expiry: z6__namespace.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
1254
+ start: z6__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1255
+ nonce: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1256
+ buy: z6__namespace.boolean(),
1257
+ chainId: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1258
+ loanToken: z6__namespace.string().transform(transformAddress),
1259
+ collaterals: CollateralsSchema,
1260
+ callback: z6__namespace.object({
1261
+ address: z6__namespace.string().transform(transformAddress),
1262
+ data: z6__namespace.string().transform(transformHex),
1263
+ gasLimit: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1264
+ }),
1265
+ consumed: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1266
+ blockNumber: z6__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1267
+ signature: z6__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1268
+ message: "Signature must be a valid 65-byte hex string"
1269
+ }).transform(transformHex).optional()
1270
+ });
1271
+ if (!omitHash) base = base.extend({ hash: OfferHashSchema });
1272
+ return base.refine((data) => data.start < data.expiry, {
1273
+ message: "Start must be before expiry",
1274
+ path: ["start"]
1275
+ }).refine((data) => data.expiry <= data.maturity, {
1276
+ message: "Expiry cannot be after maturity",
1277
+ path: ["expiry"]
1278
+ });
1602
1279
  };
1603
-
1604
- // src/core/Obligation.ts
1605
- var Obligation_exports = {};
1606
- __export(Obligation_exports, {
1607
- CollateralsAreNotSortedError: () => CollateralsAreNotSortedError,
1608
- InvalidObligationError: () => InvalidObligationError,
1609
- ObligationSchema: () => ObligationSchema,
1610
- from: () => from4,
1611
- fromSnakeCase: () => fromSnakeCase2,
1612
- id: () => id,
1613
- random: () => random
1614
- });
1615
- var ObligationSchema = z6__namespace.object({
1616
- chainId: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1617
- loanToken: z6__namespace.string().transform(transformAddress),
1618
- collaterals: CollateralsSchema,
1619
- maturity: MaturitySchema
1620
- });
1621
- function from4(parameters) {
1280
+ function from5(input) {
1622
1281
  try {
1623
- const parsedObligation = ObligationSchema.parse({
1624
- ...parameters,
1625
- maturity: from3(parameters.maturity)
1626
- });
1282
+ const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1283
+ const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
1627
1284
  return {
1628
- chainId: parsedObligation.chainId,
1629
- loanToken: parsedObligation.loanToken.toLowerCase(),
1630
- collaterals: parsedObligation.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)),
1631
- maturity: parsedObligation.maturity
1285
+ ...parsedOffer,
1286
+ hash: parsedHash
1632
1287
  };
1633
1288
  } catch (error2) {
1634
- throw new InvalidObligationError(error2);
1289
+ throw new InvalidOfferError(error2);
1635
1290
  }
1636
1291
  }
1637
- function fromSnakeCase2(input) {
1638
- return from4(fromSnakeCase(input));
1292
+ function fromSnakeCase3(input) {
1293
+ return from5(fromSnakeCase(input));
1639
1294
  }
1640
- function id(obligation) {
1641
- let lastAsset = "";
1642
- for (const collateral of obligation.collaterals) {
1643
- const newAsset = collateral.asset.toLowerCase();
1644
- if (newAsset.localeCompare(lastAsset) < 0) throw new CollateralsAreNotSortedError();
1645
- lastAsset = newAsset;
1646
- }
1647
- return viem.keccak256(
1648
- viem.encodeAbiParameters(
1649
- [
1650
- { type: "uint256" },
1651
- { type: "address" },
1652
- {
1653
- type: "tuple[]",
1654
- components: [
1655
- { type: "address", name: "token" },
1656
- { type: "uint256", name: "lltv" },
1657
- { type: "address", name: "oracle" }
1658
- ]
1659
- },
1660
- { type: "uint256" }
1661
- ],
1662
- [
1663
- obligation.chainId,
1664
- obligation.loanToken.toLowerCase(),
1665
- obligation.collaterals.map((c) => ({
1666
- token: c.asset.toLowerCase(),
1667
- lltv: c.lltv,
1668
- oracle: c.oracle.toLowerCase()
1669
- })),
1670
- BigInt(obligation.maturity)
1671
- ]
1672
- )
1673
- );
1295
+ function toSnakeCase2(offer) {
1296
+ return toSnakeCase(offer);
1674
1297
  }
1675
- function random() {
1676
- return from4({
1677
- chainId: 1n,
1678
- loanToken: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1679
- collaterals: [
1680
- from2({
1681
- asset: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1682
- oracle: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1683
- lltv: 0.965
1684
- })
1685
- ],
1686
- maturity: from3("end_of_next_quarter")
1687
- });
1688
- }
1689
- var InvalidObligationError = class extends BaseError {
1690
- name = "Obligation.InvalidObligationError";
1691
- constructor(error2) {
1692
- super("Invalid obligation.", { cause: error2 });
1693
- }
1694
- };
1695
- var CollateralsAreNotSortedError = class extends BaseError {
1696
- name = "Obligation.CollateralsAreNotSortedError";
1697
- constructor() {
1698
- super("Collaterals are not sorted alphabetically by address.");
1699
- }
1700
- };
1701
-
1702
- // src/core/Offer.ts
1703
- var Offer_exports = {};
1704
- __export(Offer_exports, {
1705
- AccountNotSetError: () => AccountNotSetError,
1706
- InvalidOfferError: () => InvalidOfferError,
1707
- OfferHashSchema: () => OfferHashSchema,
1708
- OfferSchema: () => OfferSchema,
1709
- consumedEvent: () => consumedEvent,
1710
- decode: () => decode2,
1711
- domain: () => domain,
1712
- encode: () => encode2,
1713
- from: () => from5,
1714
- fromConsumedLog: () => fromConsumedLog,
1715
- fromSnakeCase: () => fromSnakeCase3,
1716
- hash: () => hash,
1717
- obligationId: () => obligationId,
1718
- random: () => random2,
1719
- sign: () => sign,
1720
- toSnakeCase: () => toSnakeCase2,
1721
- types: () => types
1722
- });
1723
- var OfferHashSchema = z6__namespace.string().regex(/^0x[0-9a-fA-F]{64}$/, {
1724
- message: "Hash must be a valid 32-byte hex string"
1725
- }).transform(transformHex);
1726
- var OfferSchema = (parameters) => {
1727
- const { omitHash = false } = parameters || {};
1728
- const now2 = time_exports.now();
1729
- let base = z6__namespace.object({
1730
- offering: z6__namespace.string().transform(transformAddress),
1731
- assets: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1732
- rate: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1733
- maturity: MaturitySchema,
1734
- expiry: z6__namespace.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
1735
- start: z6__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1736
- nonce: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1737
- buy: z6__namespace.boolean(),
1738
- chainId: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1739
- loanToken: z6__namespace.string().transform(transformAddress),
1740
- collaterals: CollateralsSchema,
1741
- callback: z6__namespace.object({
1742
- address: z6__namespace.string().transform(transformAddress),
1743
- data: z6__namespace.string().transform(transformHex),
1744
- gasLimit: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1745
- }),
1746
- consumed: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256),
1747
- blockNumber: z6__namespace.number().int().max(Number.MAX_SAFE_INTEGER),
1748
- signature: z6__namespace.string().regex(/^0x[0-9a-fA-F]{130}$/, {
1749
- message: "Signature must be a valid 65-byte hex string"
1750
- }).transform(transformHex).optional()
1751
- });
1752
- if (!omitHash) base = base.extend({ hash: OfferHashSchema });
1753
- return base.refine((data) => data.start < data.expiry, {
1754
- message: "Start must be before expiry",
1755
- path: ["start"]
1756
- }).refine((data) => data.expiry <= data.maturity, {
1757
- message: "Expiry cannot be after maturity",
1758
- path: ["expiry"]
1759
- });
1760
- };
1761
- function from5(input) {
1762
- try {
1763
- const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
1764
- const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
1765
- return {
1766
- ...parsedOffer,
1767
- hash: parsedHash
1768
- };
1769
- } catch (error2) {
1770
- throw new InvalidOfferError(error2);
1771
- }
1772
- }
1773
- function fromSnakeCase3(input) {
1774
- return from5(fromSnakeCase(input));
1775
- }
1776
- function toSnakeCase2(offer) {
1777
- return toSnakeCase(offer);
1778
- }
1779
- function random2() {
1780
- const loanToken = accounts.privateKeyToAccount(accounts.generatePrivateKey()).address;
1781
- const maturity = from3("end_of_month");
1782
- const expiry = from3("end_of_week") - 1;
1783
- const lltv = from(0.965);
1298
+ function random2(config) {
1299
+ const chain = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
1300
+ const loanToken = config?.loanTokens ? config.loanTokens[Math.floor(Math.random() * config.loanTokens.length)] : accounts.privateKeyToAccount(accounts.generatePrivateKey()).address;
1301
+ const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [accounts.privateKeyToAccount(accounts.generatePrivateKey()).address];
1302
+ const collateralAsset = collateralCandidates[Math.floor(Math.random() * collateralCandidates.length)];
1303
+ const maturityOption = weightedChoice([
1304
+ ["end_of_month", 1],
1305
+ ["end_of_next_month", 1]
1306
+ ]);
1307
+ const maturity = config?.maturity ?? from3(maturityOption);
1308
+ const lltv = from(
1309
+ weightedChoice([
1310
+ [0.385, 1],
1311
+ [0.5, 1],
1312
+ [0.625, 2],
1313
+ [0.77, 8],
1314
+ [0.86, 10],
1315
+ [0.915, 8],
1316
+ [0.945, 6],
1317
+ [0.965, 4],
1318
+ [0.98, 2]
1319
+ ])
1320
+ );
1321
+ const buy = config?.buy !== void 0 ? config.buy : Math.random() > 0.5;
1322
+ const ONE = 1000000000000000000n;
1323
+ const qMin = buy ? 16 : 4;
1324
+ const qMax = buy ? 32 : 16;
1325
+ const len = qMax - qMin + 1;
1326
+ const ratePairs = Array.from(
1327
+ { length: len },
1328
+ (_, idx) => {
1329
+ const q = qMin + idx;
1330
+ const scaledRate = BigInt(q) * (ONE / 4n);
1331
+ const weight = buy ? 1 + idx : 1 + (len - 1 - idx);
1332
+ return [scaledRate, weight];
1333
+ }
1334
+ );
1335
+ const rate = config?.rate ?? weightedChoice(ratePairs);
1336
+ const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
1337
+ const unit = BigInt(10) ** BigInt(loanTokenDecimals);
1338
+ const amountBase = BigInt(100 + Math.floor(Math.random() * (1e6 - 100 + 1)));
1339
+ const assetsScaled = config?.assets ?? amountBase * unit;
1340
+ const consumed2 = config?.consumed !== void 0 ? config.consumed : Math.random() < 0.8 ? 0n : assetsScaled * BigInt(1 + Math.floor(Math.random() * 900)) / 1000n;
1341
+ const callbackBySide = (() => {
1342
+ if (buy) return { address: viem.zeroAddress, data: "0x", gasLimit: 0n };
1343
+ const sellCallbackAddress = WhitelistedCallbackAddresses["sell_erc20_callback" /* SellERC20Callback */][0].toLowerCase();
1344
+ const amount = assetsScaled * 1000000000000000000000n;
1345
+ const data = encodeSellERC20Callback({
1346
+ collaterals: [collateralAsset],
1347
+ amounts: [amount]
1348
+ });
1349
+ return { address: sellCallbackAddress, data, gasLimit: 500000n };
1350
+ })();
1784
1351
  const offer = from5({
1785
- offering: accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1786
- assets: BigInt(Math.floor(Math.random() * 1e6)),
1787
- rate: BigInt(Math.floor(Math.random() * 1e6)),
1352
+ offering: config?.offering ?? accounts.privateKeyToAccount(accounts.generatePrivateKey()).address,
1353
+ assets: assetsScaled,
1354
+ rate,
1788
1355
  maturity,
1789
- expiry,
1790
- start: expiry - 10,
1356
+ expiry: config?.expiry ?? maturity - 1,
1357
+ start: config?.start ?? maturity - 10,
1791
1358
  nonce: BigInt(Math.floor(Math.random() * 1e6)),
1792
- buy: Math.random() > 0.5,
1793
- chainId: 1n,
1359
+ buy,
1360
+ chainId: chain.id,
1794
1361
  loanToken,
1795
- collaterals: [
1362
+ collaterals: config?.collaterals ?? [
1796
1363
  from2({
1797
- asset: viem.zeroAddress,
1364
+ asset: collateralAsset,
1798
1365
  oracle: viem.zeroAddress,
1799
1366
  lltv
1800
1367
  })
1801
1368
  ],
1802
- callback: {
1803
- address: viem.zeroAddress,
1804
- data: "0x",
1805
- gasLimit: 0n
1806
- },
1807
- consumed: 0n,
1369
+ callback: config?.callback ?? callbackBySide,
1370
+ consumed: consumed2,
1808
1371
  blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
1809
1372
  });
1810
1373
  return offer;
1811
1374
  }
1375
+ var weightedChoice = (pairs) => {
1376
+ const total = pairs.reduce((sum, [, weight]) => sum + weight, 0);
1377
+ let roll = Math.random() * total;
1378
+ for (const [value, weight] of pairs) {
1379
+ roll -= weight;
1380
+ if (roll < 0) return value;
1381
+ }
1382
+ return pairs[0][0];
1383
+ };
1812
1384
  var domain = (chainId) => ({
1813
1385
  chainId,
1814
1386
  verifyingContract: viem.zeroAddress
@@ -1864,166 +1436,1084 @@ function sign(offer, wallet) {
1864
1436
  gasLimit: offer.callback.gasLimit
1865
1437
  }
1866
1438
  }
1867
- });
1439
+ });
1440
+ }
1441
+ function hash(offer) {
1442
+ return viem.hashTypedData({
1443
+ domain: domain(offer.chainId),
1444
+ message: {
1445
+ offering: offer.offering.toLowerCase(),
1446
+ assets: offer.assets,
1447
+ rate: offer.rate,
1448
+ maturity: BigInt(offer.maturity),
1449
+ expiry: BigInt(offer.expiry),
1450
+ nonce: offer.nonce,
1451
+ buy: offer.buy,
1452
+ loanToken: offer.loanToken.toLowerCase(),
1453
+ collaterals: offer.collaterals,
1454
+ callback: {
1455
+ address: offer.callback.address.toLowerCase(),
1456
+ data: offer.callback.data,
1457
+ gasLimit: offer.callback.gasLimit
1458
+ }
1459
+ },
1460
+ primaryType: "Offer",
1461
+ types
1462
+ });
1463
+ }
1464
+ function obligationId(offer) {
1465
+ return id(
1466
+ from4({
1467
+ chainId: offer.chainId,
1468
+ loanToken: offer.loanToken,
1469
+ collaterals: offer.collaterals,
1470
+ maturity: offer.maturity
1471
+ })
1472
+ );
1473
+ }
1474
+ var OfferAbi = [
1475
+ { name: "offering", type: "address" },
1476
+ { name: "assets", type: "uint256" },
1477
+ { name: "rate", type: "uint256" },
1478
+ { name: "maturity", type: "uint256" },
1479
+ { name: "expiry", type: "uint256" },
1480
+ { name: "nonce", type: "uint256" },
1481
+ { name: "buy", type: "bool" },
1482
+ { name: "chainId", type: "uint256" },
1483
+ { name: "loanToken", type: "address" },
1484
+ { name: "start", type: "uint256" },
1485
+ {
1486
+ name: "collaterals",
1487
+ type: "tuple[]",
1488
+ components: [
1489
+ { name: "asset", type: "address" },
1490
+ { name: "oracle", type: "address" },
1491
+ { name: "lltv", type: "uint256" }
1492
+ ]
1493
+ },
1494
+ {
1495
+ name: "callback",
1496
+ type: "tuple",
1497
+ components: [
1498
+ { name: "address", type: "address" },
1499
+ { name: "data", type: "bytes" },
1500
+ { name: "gasLimit", type: "uint256" }
1501
+ ]
1502
+ },
1503
+ { name: "signature", type: "bytes" }
1504
+ ];
1505
+ function encode(offer) {
1506
+ return viem.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 decode(data, blockNumber) {
1523
+ let decoded;
1524
+ try {
1525
+ decoded = viem.decodeAbiParameters(OfferAbi, data);
1526
+ } catch (error2) {
1527
+ throw new InvalidOfferError(error2);
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
+ name = "Offer.InvalidOfferError";
1581
+ constructor(error2) {
1582
+ super("Invalid offer.", { cause: error2 });
1583
+ }
1584
+ };
1585
+ var AccountNotSetError = class extends BaseError {
1586
+ name = "Offer.AccountNotSetError";
1587
+ constructor() {
1588
+ super("Account not set.");
1589
+ }
1590
+ };
1591
+
1592
+ // src/core/Quote.ts
1593
+ var Quote_exports = {};
1594
+ __export(Quote_exports, {
1595
+ InvalidQuoteError: () => InvalidQuoteError,
1596
+ QuoteSchema: () => QuoteSchema,
1597
+ from: () => from6,
1598
+ fromSnakeCase: () => fromSnakeCase4,
1599
+ random: () => random3
1600
+ });
1601
+ var QuoteSchema = z6__namespace.object({
1602
+ obligationId: z6__namespace.string().transform(transformHex),
1603
+ ask: z6__namespace.object({
1604
+ rate: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1605
+ }),
1606
+ bid: z6__namespace.object({
1607
+ rate: z6__namespace.bigint({ coerce: true }).min(0n).max(viem.maxUint256)
1608
+ })
1609
+ });
1610
+ function from6(parameters) {
1611
+ try {
1612
+ const parsedQuote = QuoteSchema.parse(parameters);
1613
+ return {
1614
+ obligationId: parsedQuote.obligationId,
1615
+ ask: parsedQuote.ask,
1616
+ bid: parsedQuote.bid
1617
+ };
1618
+ } catch (error2) {
1619
+ throw new InvalidQuoteError(error2);
1620
+ }
1621
+ }
1622
+ function fromSnakeCase4(snake) {
1623
+ return from6(fromSnakeCase(snake));
1624
+ }
1625
+ function random3() {
1626
+ return from6({
1627
+ obligationId: Obligation_exports.id(Obligation_exports.random()),
1628
+ ask: {
1629
+ rate: BigInt(Math.floor(Math.random() * 1e6))
1630
+ },
1631
+ bid: {
1632
+ rate: BigInt(Math.floor(Math.random() * 1e6))
1633
+ }
1634
+ });
1635
+ }
1636
+ var InvalidQuoteError = class extends BaseError {
1637
+ name = "Quote.InvalidQuoteError";
1638
+ constructor(error2) {
1639
+ super("Invalid quote.", { cause: error2 });
1640
+ }
1641
+ };
1642
+
1643
+ // src/core/types.ts
1644
+ var BrandTypeId = Symbol.for("mempool/Brand");
1645
+ var CollectorHealth = v4.z.object({
1646
+ name: v4.z.string(),
1647
+ chain_id: v4.z.number(),
1648
+ block_number: v4.z.number().nullable(),
1649
+ updated_at: v4.z.string().nullable(),
1650
+ lag: v4.z.number().nullable(),
1651
+ status: v4.z.enum(["live", "lagging", "unknown"])
1652
+ });
1653
+ var CollectorsHealthResponse = v4.z.array(CollectorHealth);
1654
+ var ChainHealth = v4.z.object({
1655
+ chain_id: v4.z.number(),
1656
+ block_number: v4.z.number(),
1657
+ updated_at: v4.z.string()
1658
+ });
1659
+ var ChainsHealthResponse = v4.z.array(ChainHealth);
1660
+ var RouterStatusResponse = v4.z.object({
1661
+ status: v4.z.enum(["live", "syncing"])
1662
+ });
1663
+
1664
+ // src/stores/utils/Cursor.ts
1665
+ var Cursor_exports = {};
1666
+ __export(Cursor_exports, {
1667
+ decode: () => decode2,
1668
+ encode: () => encode2,
1669
+ validate: () => validate
1670
+ });
1671
+ function validate(cursor) {
1672
+ if (!cursor || typeof cursor !== "object") {
1673
+ throw new Error("Cursor must be an object");
1674
+ }
1675
+ const c = cursor;
1676
+ if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
1677
+ throw new Error(
1678
+ `Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
1679
+ );
1680
+ }
1681
+ if (!["asc", "desc"].includes(c.dir)) {
1682
+ throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
1683
+ }
1684
+ if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
1685
+ throw new Error(
1686
+ `Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
1687
+ );
1688
+ }
1689
+ const validations = {
1690
+ rate: {
1691
+ field: "rate",
1692
+ type: "string",
1693
+ pattern: /^\d+$/,
1694
+ error: "numeric string"
1695
+ },
1696
+ amount: {
1697
+ field: "assets",
1698
+ type: "string",
1699
+ pattern: /^\d+$/,
1700
+ error: "numeric string"
1701
+ },
1702
+ maturity: {
1703
+ field: "maturity",
1704
+ type: "number",
1705
+ validator: (val) => val > 0,
1706
+ error: "positive number"
1707
+ },
1708
+ expiry: {
1709
+ field: "expiry",
1710
+ type: "number",
1711
+ validator: (val) => val > 0,
1712
+ error: "positive number"
1713
+ }
1714
+ };
1715
+ const validation = validations[c.sort];
1716
+ if (!validation) {
1717
+ throw new Error(`Invalid sort field: ${c.sort}`);
1718
+ }
1719
+ const fieldValue = c[validation.field];
1720
+ if (!fieldValue) {
1721
+ throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
1722
+ }
1723
+ if (typeof fieldValue !== validation.type) {
1724
+ throw new Error(
1725
+ `${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
1726
+ );
1727
+ }
1728
+ if (validation.pattern && !validation.pattern.test(fieldValue)) {
1729
+ throw new Error(
1730
+ `Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
1731
+ );
1732
+ }
1733
+ if (validation.validator && !validation.validator(fieldValue)) {
1734
+ throw new Error(
1735
+ `Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
1736
+ );
1737
+ }
1738
+ if (c.page !== void 0) {
1739
+ if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
1740
+ throw new Error("Invalid page: must be a positive integer");
1741
+ }
1742
+ }
1743
+ return true;
1744
+ }
1745
+ function encode2(c) {
1746
+ return jsBase64.Base64.encodeURL(JSON.stringify(c));
1747
+ }
1748
+ function decode2(token) {
1749
+ if (!token) return null;
1750
+ const decoded = JSON.parse(jsBase64.Base64.decode(token));
1751
+ validate(decoded);
1752
+ return decoded;
1753
+ }
1754
+
1755
+ // src/api/Schema/requests.ts
1756
+ var MAX_LIMIT = 100;
1757
+ var DEFAULT_LIMIT = 20;
1758
+ var PaginationQueryParams = z6__namespace.object({
1759
+ cursor: z6__namespace.string().optional().refine(
1760
+ (val) => {
1761
+ if (!val) return true;
1762
+ try {
1763
+ const decoded = Cursor_exports.decode(val);
1764
+ return decoded !== null;
1765
+ } catch (_error) {
1766
+ return false;
1767
+ }
1768
+ },
1769
+ {
1770
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1771
+ }
1772
+ ).meta({
1773
+ description: "Pagination cursor in base64url-encoded format",
1774
+ example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
1775
+ }),
1776
+ limit: z6__namespace.string().regex(/^[1-9]\d*$/, {
1777
+ message: "Limit must be a positive integer"
1778
+ }).transform((val) => Number.parseInt(val, 10)).pipe(
1779
+ z6__namespace.number().max(MAX_LIMIT, {
1780
+ message: `Limit cannot exceed ${MAX_LIMIT}`
1781
+ })
1782
+ ).optional().default(DEFAULT_LIMIT).meta({
1783
+ description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
1784
+ example: 10
1785
+ })
1786
+ });
1787
+ var GetOffersQueryParams = z6__namespace.object({
1788
+ ...PaginationQueryParams.shape,
1789
+ side: z6__namespace.enum(["buy", "sell"]).meta({
1790
+ description: "Side of the offer.",
1791
+ example: "buy"
1792
+ }),
1793
+ obligation_id: z6__namespace.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
1794
+ description: "Offers obligation id",
1795
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1796
+ })
1797
+ });
1798
+ var GetObligationsQueryParams = z6__namespace.object({
1799
+ ...PaginationQueryParams.shape,
1800
+ cursor: z6__namespace.string().optional().meta({
1801
+ description: "Obligation id cursor",
1802
+ example: "0x1234567890123456789012345678901234567890123456789012345678901234"
1803
+ })
1804
+ });
1805
+ var schemas = {
1806
+ get_offers: GetOffersQueryParams,
1807
+ get_obligations: GetObligationsQueryParams
1808
+ };
1809
+ function parse(action, query) {
1810
+ return schemas[action].parse(query);
1811
+ }
1812
+ function safeParse(action, query, error2) {
1813
+ return schemas[action].safeParse(query, {
1814
+ error: error2
1815
+ });
1816
+ }
1817
+
1818
+ // src/api/Schema/openapi.ts
1819
+ var timestampExample = "2024-01-01T12:00:00.000Z";
1820
+ var cursorExample = "eyJvZmZzZXQiOjEwMH0";
1821
+ function makeSuccessResponse(parameters) {
1822
+ const { dataSchema, dataDescription, dataExample, cursor } = parameters;
1823
+ const withDataMeta = dataDescription ? dataSchema.meta({ description: dataDescription }) : dataSchema;
1824
+ return v4.z.object({
1825
+ status: v4.z.literal("success"),
1826
+ cursor: v4.z.string().nullable(),
1827
+ data: v4.z.any(),
1828
+ meta: v4.z.object({
1829
+ timestamp: v4.z.string()
1830
+ })
1831
+ }).extend({
1832
+ data: withDataMeta
1833
+ }).meta({
1834
+ example: {
1835
+ status: "success",
1836
+ cursor,
1837
+ data: dataExample,
1838
+ meta: { timestamp: timestampExample }
1839
+ }
1840
+ });
1841
+ }
1842
+ var OffersSuccessResponseSchema = makeSuccessResponse({
1843
+ dataSchema: v4.z.array(v4.z.any()),
1844
+ dataDescription: "Offers matching the provided filters.",
1845
+ dataExample: [toSnakeCase(Offer_exports.random())],
1846
+ cursor: cursorExample
1847
+ });
1848
+ var ObligationsSuccessResponseSchema = makeSuccessResponse({
1849
+ dataSchema: v4.z.array(v4.z.any()),
1850
+ dataDescription: "Obligations known to the router.",
1851
+ dataExample: [toSnakeCase(Obligation_exports.random())],
1852
+ cursor: cursorExample
1853
+ });
1854
+ var RouterStatusSuccessResponseSchema = makeSuccessResponse({
1855
+ dataSchema: RouterStatusResponse,
1856
+ dataDescription: "Aggregated router status.",
1857
+ dataExample: { status: "live" },
1858
+ cursor: null
1859
+ });
1860
+ var CollectorsHealthSuccessResponseSchema = makeSuccessResponse({
1861
+ dataSchema: CollectorsHealthResponse,
1862
+ dataDescription: "Collectors health details and sync status.",
1863
+ dataExample: [
1864
+ {
1865
+ name: "mempool_offers",
1866
+ chain_id: "1",
1867
+ block_number: 21345678,
1868
+ updated_at: "2024-01-01T12:00:00.000Z",
1869
+ lag: 0,
1870
+ status: "live"
1871
+ }
1872
+ ],
1873
+ cursor: null
1874
+ });
1875
+ var ChainsHealthSuccessResponseSchema = makeSuccessResponse({
1876
+ dataSchema: ChainsHealthResponse,
1877
+ dataDescription: "Latest processed block per chain.",
1878
+ dataExample: [
1879
+ {
1880
+ chain_id: "1",
1881
+ block_number: 21345678,
1882
+ updated_at: "2024-01-01T12:00:00.000Z"
1883
+ }
1884
+ ],
1885
+ cursor: null
1886
+ });
1887
+ var errorResponseSchema = v4.z.object({
1888
+ status: v4.z.literal("error"),
1889
+ error: v4.z.object({
1890
+ code: v4.z.string(),
1891
+ message: v4.z.string(),
1892
+ details: v4.z.any().optional()
1893
+ }),
1894
+ meta: v4.z.object({
1895
+ timestamp: v4.z.string()
1896
+ })
1897
+ }).meta({
1898
+ description: "Error response wrapper.",
1899
+ example: {
1900
+ status: "error",
1901
+ error: {
1902
+ code: "VALIDATION_ERROR",
1903
+ message: "Invalid cursor format. Must be a valid base64url-encoded cursor object",
1904
+ details: [
1905
+ {
1906
+ field: "cursor",
1907
+ issue: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
1908
+ }
1909
+ ]
1910
+ },
1911
+ meta: {
1912
+ timestamp: timestampExample
1913
+ }
1914
+ }
1915
+ });
1916
+ var paths = {
1917
+ "/v1/offers": {
1918
+ get: {
1919
+ summary: "Offers",
1920
+ description: "Find offers that match specific criteria",
1921
+ tags: ["Offers"],
1922
+ requestParams: {
1923
+ query: GetOffersQueryParams
1924
+ },
1925
+ responses: {
1926
+ 200: {
1927
+ description: "Success",
1928
+ content: {
1929
+ "application/json": {
1930
+ schema: OffersSuccessResponseSchema
1931
+ }
1932
+ }
1933
+ },
1934
+ 400: {
1935
+ description: "Bad Request",
1936
+ content: {
1937
+ "application/json": {
1938
+ schema: errorResponseSchema
1939
+ }
1940
+ }
1941
+ }
1942
+ }
1943
+ }
1944
+ },
1945
+ "/v1/obligations": {
1946
+ get: {
1947
+ summary: "Obligations",
1948
+ description: "List obligations with pagination support",
1949
+ tags: ["Obligations"],
1950
+ requestParams: {
1951
+ query: GetObligationsQueryParams
1952
+ },
1953
+ responses: {
1954
+ 200: {
1955
+ description: "Success",
1956
+ content: {
1957
+ "application/json": {
1958
+ schema: ObligationsSuccessResponseSchema
1959
+ }
1960
+ }
1961
+ },
1962
+ 400: {
1963
+ description: "Bad Request",
1964
+ content: {
1965
+ "application/json": {
1966
+ schema: errorResponseSchema
1967
+ }
1968
+ }
1969
+ }
1970
+ }
1971
+ }
1972
+ },
1973
+ "/v1/health": {
1974
+ get: {
1975
+ summary: "Router status",
1976
+ description: "Retrieve the aggregated status of the router.",
1977
+ tags: ["Health"],
1978
+ responses: {
1979
+ 200: {
1980
+ description: "Success",
1981
+ content: {
1982
+ "application/json": {
1983
+ schema: RouterStatusSuccessResponseSchema
1984
+ }
1985
+ }
1986
+ }
1987
+ }
1988
+ }
1989
+ },
1990
+ "/v1/health/collectors": {
1991
+ get: {
1992
+ summary: "Collectors health",
1993
+ description: "Retrieve the block numbers processed by collectors and their sync status.",
1994
+ tags: ["Health"],
1995
+ responses: {
1996
+ 200: {
1997
+ description: "Success",
1998
+ content: {
1999
+ "application/json": {
2000
+ schema: CollectorsHealthSuccessResponseSchema
2001
+ }
2002
+ }
2003
+ }
2004
+ }
2005
+ }
2006
+ },
2007
+ "/v1/health/chains": {
2008
+ get: {
2009
+ summary: "Chains health",
2010
+ description: "Retrieve the latest block processed for each chain.",
2011
+ tags: ["Health"],
2012
+ responses: {
2013
+ 200: {
2014
+ description: "Success",
2015
+ content: {
2016
+ "application/json": {
2017
+ schema: ChainsHealthSuccessResponseSchema
2018
+ }
2019
+ }
2020
+ }
2021
+ }
2022
+ }
2023
+ }
2024
+ };
2025
+ var OpenApi = zodOpenapi.createDocument({
2026
+ openapi: "3.1.0",
2027
+ info: {
2028
+ title: "Router API",
2029
+ version: "1.0.0",
2030
+ description: "API for the Morpho Router"
2031
+ },
2032
+ tags: [
2033
+ {
2034
+ name: "Offers"
2035
+ },
2036
+ {
2037
+ name: "Obligations"
2038
+ },
2039
+ {
2040
+ name: "Health"
2041
+ }
2042
+ ],
2043
+ servers: [
2044
+ {
2045
+ url: "https://router.morpho.dev",
2046
+ description: "Production server"
2047
+ },
2048
+ {
2049
+ url: "http://localhost:7891",
2050
+ description: "Local development server"
2051
+ }
2052
+ ],
2053
+ paths
2054
+ });
2055
+
2056
+ // src/api/Controllers/getDocs.ts
2057
+ function getSwaggerJson() {
2058
+ return OpenApi;
2059
+ }
2060
+ function getDocsHtml() {
2061
+ const html = `<!DOCTYPE html>
2062
+ <html>
2063
+ <head>
2064
+ <meta charset="UTF-8">
2065
+ <title>Router API Docs (Scalar)</title>
2066
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
2067
+ <style>
2068
+ html, body { margin: 0; height: 100%; }
2069
+ api-reference { height: 100%; width: 100%; }
2070
+ </style>
2071
+ </head>
2072
+ <body>
2073
+ <div id="api-container" style="height:100%;width:100%;"></div>
2074
+ <script>
2075
+ window.addEventListener('load', function () {
2076
+ const spec = ${JSON.stringify(OpenApi)};
2077
+ Scalar.createApiReference('#api-container', { spec: { content: spec } });
2078
+ });
2079
+ </script>
2080
+ </body>
2081
+ </html>`;
2082
+ return html;
2083
+ }
2084
+
2085
+ // src/services/Health.ts
2086
+ var Health_exports = {};
2087
+ __export(Health_exports, {
2088
+ create: () => create9
2089
+ });
2090
+
2091
+ // src/collectors/index.ts
2092
+ var collectors_exports = {};
2093
+ __export(collectors_exports, {
2094
+ batch: () => batch2,
2095
+ create: () => create2,
2096
+ createBuilder: () => createBuilder,
2097
+ fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
2098
+ fetchCollateralAndDebt: () => fetchCollateralAndDebt,
2099
+ fetchOraclePrices: () => fetchOraclePrices,
2100
+ fetchUserVaultMarketLiquidity: () => fetchUserVaultMarketLiquidity,
2101
+ morpho: () => morpho,
2102
+ names: () => names,
2103
+ run: () => run,
2104
+ single: () => single,
2105
+ start: () => start
2106
+ });
2107
+
2108
+ // src/services/Logger.ts
2109
+ var Logger_exports = {};
2110
+ __export(Logger_exports, {
2111
+ LogLevelValues: () => LogLevelValues,
2112
+ defaultLogger: () => defaultLogger,
2113
+ getLogger: () => getLogger,
2114
+ runWithLogger: () => runWithLogger,
2115
+ silentLogger: () => silentLogger
2116
+ });
2117
+ var LogLevelValues = [
2118
+ "trace",
2119
+ "debug",
2120
+ "info",
2121
+ "warn",
2122
+ "error",
2123
+ "fatal",
2124
+ "silent"
2125
+ ];
2126
+ function defaultLogger(minLevel, pretty) {
2127
+ const threshold = minLevel ?? process.env.ROUTER_LOG_LEVEL ?? "info";
2128
+ const prettyEnabled = typeof pretty === "boolean" ? pretty : String(process.env.ROUTER_LOG_PRETTY ?? "false").toLowerCase() === "true";
2129
+ const levelIndexByName = LogLevelValues.reduce(
2130
+ (acc, lvl, idx) => {
2131
+ acc[lvl] = idx;
2132
+ return acc;
2133
+ },
2134
+ {}
2135
+ );
2136
+ const isEnabled = (methodLevel) => levelIndexByName[methodLevel] >= levelIndexByName[threshold];
2137
+ const wrap = (consoleMethod, methodLevel) => isEnabled(methodLevel) ? (entry) => {
2138
+ if (!prettyEnabled) {
2139
+ console[consoleMethod](viem.stringify({ level: methodLevel, ...entry }));
2140
+ return;
2141
+ }
2142
+ const { msg, ...rest } = entry;
2143
+ const stack = typeof rest.stack === "string" ? rest.stack : void 0;
2144
+ if (stack) delete rest.stack;
2145
+ const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
2146
+ const level = methodLevel.toUpperCase();
2147
+ const extras = Object.entries(rest).map(([k, v]) => `${k}=${formatValue(v)}`).join(" ");
2148
+ const line = extras.length > 0 ? `${timestamp2} [${level}] ${msg} ${extras}` : `${timestamp2} [${level}] ${msg}`;
2149
+ console[consoleMethod](line);
2150
+ if (stack) {
2151
+ console[consoleMethod](stack);
2152
+ }
2153
+ } : () => {
2154
+ };
2155
+ return {
2156
+ trace: wrap("trace", "trace"),
2157
+ debug: wrap("debug", "debug"),
2158
+ info: wrap("info", "info"),
2159
+ warn: wrap("warn", "warn"),
2160
+ error: wrap("error", "error"),
2161
+ fatal: wrap("error", "fatal")
2162
+ };
1868
2163
  }
1869
- function hash(offer) {
1870
- return viem.hashTypedData({
1871
- domain: domain(offer.chainId),
1872
- message: {
1873
- offering: offer.offering.toLowerCase(),
1874
- assets: offer.assets,
1875
- rate: offer.rate,
1876
- maturity: BigInt(offer.maturity),
1877
- expiry: BigInt(offer.expiry),
1878
- nonce: offer.nonce,
1879
- buy: offer.buy,
1880
- loanToken: offer.loanToken.toLowerCase(),
1881
- collaterals: offer.collaterals,
1882
- callback: {
1883
- address: offer.callback.address.toLowerCase(),
1884
- data: offer.callback.data,
1885
- gasLimit: offer.callback.gasLimit
1886
- }
1887
- },
1888
- primaryType: "Offer",
1889
- types
1890
- });
2164
+ function silentLogger() {
2165
+ const noop = () => {
2166
+ };
2167
+ return { trace: noop, debug: noop, info: noop, warn: noop, error: noop, fatal: noop };
1891
2168
  }
1892
- function obligationId(offer) {
1893
- return id(
1894
- from4({
1895
- chainId: offer.chainId,
1896
- loanToken: offer.loanToken,
1897
- collaterals: offer.collaterals,
1898
- maturity: offer.maturity
1899
- })
1900
- );
2169
+ var loggerContext = new async_hooks.AsyncLocalStorage();
2170
+ function runWithLogger(logger, fn) {
2171
+ return loggerContext.run(logger, fn);
1901
2172
  }
1902
- var OfferAbi = [
1903
- { name: "offering", type: "address" },
1904
- { name: "assets", type: "uint256" },
1905
- { name: "rate", type: "uint256" },
1906
- { name: "maturity", type: "uint256" },
1907
- { name: "expiry", type: "uint256" },
1908
- { name: "nonce", type: "uint256" },
1909
- { name: "buy", type: "bool" },
1910
- { name: "chainId", type: "uint256" },
1911
- { name: "loanToken", type: "address" },
1912
- { name: "start", type: "uint256" },
1913
- {
1914
- name: "collaterals",
1915
- type: "tuple[]",
1916
- components: [
1917
- { name: "asset", type: "address" },
1918
- { name: "oracle", type: "address" },
1919
- { name: "lltv", type: "uint256" }
1920
- ]
1921
- },
1922
- {
1923
- name: "callback",
1924
- type: "tuple",
1925
- components: [
1926
- { name: "address", type: "address" },
1927
- { name: "data", type: "bytes" },
1928
- { name: "gasLimit", type: "uint256" }
1929
- ]
1930
- },
1931
- { name: "signature", type: "bytes" }
1932
- ];
1933
- function encode2(offer) {
1934
- return viem.encodeAbiParameters(OfferAbi, [
1935
- offer.offering,
1936
- offer.assets,
1937
- offer.rate,
1938
- BigInt(offer.maturity),
1939
- BigInt(offer.expiry),
1940
- offer.nonce,
1941
- offer.buy,
1942
- offer.chainId,
1943
- offer.loanToken,
1944
- BigInt(offer.start),
1945
- offer.collaterals,
1946
- offer.callback,
1947
- offer.signature ?? "0x"
1948
- ]);
2173
+ function getLogger() {
2174
+ return loggerContext.getStore() ?? defaultLogger();
1949
2175
  }
1950
- function decode2(data, blockNumber) {
1951
- let decoded;
2176
+ function formatValue(value) {
2177
+ if (value === null || value === void 0 || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
2178
+ return String(value);
2179
+ }
2180
+ if (typeof value === "string") {
2181
+ if (value.includes(" ")) return JSON.stringify(value);
2182
+ return value;
2183
+ }
1952
2184
  try {
1953
- decoded = viem.decodeAbiParameters(OfferAbi, data);
1954
- } catch (error2) {
1955
- throw new InvalidOfferError(error2);
2185
+ return viem.stringify(value);
2186
+ } catch {
2187
+ try {
2188
+ return JSON.stringify(value);
2189
+ } catch {
2190
+ return String(value);
2191
+ }
1956
2192
  }
1957
- const offer = from5({
1958
- offering: decoded[0],
1959
- assets: decoded[1],
1960
- rate: decoded[2],
1961
- maturity: from3(Number(decoded[3])),
1962
- expiry: Number(decoded[4]),
1963
- nonce: decoded[5],
1964
- buy: decoded[6],
1965
- chainId: decoded[7],
1966
- loanToken: decoded[8],
1967
- start: Number(decoded[9]),
1968
- collaterals: decoded[10].map((c) => {
1969
- return from2({
1970
- asset: c.asset,
1971
- oracle: c.oracle,
1972
- lltv: c.lltv
2193
+ }
2194
+
2195
+ // src/services/RouterAdmin.ts
2196
+ function create(parameters) {
2197
+ const collector = "admin";
2198
+ const {
2199
+ client,
2200
+ chain,
2201
+ withTransaction,
2202
+ options: { maxBatchSize = 25, maxBlockNumber } = {}
2203
+ } = parameters;
2204
+ const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
2205
+ let finalizedBlock = null;
2206
+ let unfinalizedBlocks = [];
2207
+ const logger = getLogger();
2208
+ let isMaxBlockNumberReached = false;
2209
+ let tick = 0;
2210
+ return {
2211
+ syncBlock: async () => {
2212
+ if (isMaxBlockNumberReached) return;
2213
+ const head = await client.getBlock({
2214
+ blockTag: "latest",
2215
+ includeTransactions: false
2216
+ });
2217
+ await withTransaction(async ({ chainStore, collectorStore }) => {
2218
+ const { epoch, blockNumber: latestSavedBlockNumber } = await chainStore.getBlockNumber(
2219
+ chain.id
2220
+ );
2221
+ if (maxBlockNumberBI !== void 0 && head.number >= maxBlockNumberBI) {
2222
+ logger.info({
2223
+ msg: `Head is greater than max block number`,
2224
+ collector,
2225
+ chainId: chain.id,
2226
+ block_number: head.number,
2227
+ max_block_number: maxBlockNumber
2228
+ });
2229
+ isMaxBlockNumberReached = true;
2230
+ await chainStore.saveBlockNumber({
2231
+ chainId: chain.id,
2232
+ blockNumber: maxBlockNumber,
2233
+ epoch: epoch + 1n
2234
+ });
2235
+ await Promise.all(
2236
+ names.map(
2237
+ async (collectorName) => collectorStore.saveBlockNumber({
2238
+ collectorName,
2239
+ chainId: chain.id,
2240
+ blockNumber: maxBlockNumber,
2241
+ epoch: epoch + 1n
2242
+ })
2243
+ )
2244
+ );
2245
+ return;
2246
+ }
2247
+ finalizedBlock = await fetchFinalizedBlock({
2248
+ tick,
2249
+ client,
2250
+ chain,
2251
+ logger,
2252
+ collector,
2253
+ unfinalizedBlocks,
2254
+ previousFinalizedBlock: finalizedBlock
2255
+ });
2256
+ tick++;
2257
+ let {
2258
+ block: returnedBlock,
2259
+ didReorgHappened,
2260
+ unfinalizedBlocks: newUnfinalizedBlocks
2261
+ } = await reconcile({
2262
+ client,
2263
+ block: head,
2264
+ unfinalizedBlocks,
2265
+ finalizedBlock,
2266
+ logger,
2267
+ collector,
2268
+ chain,
2269
+ maxBatchSize
2270
+ });
2271
+ unfinalizedBlocks = newUnfinalizedBlocks;
2272
+ const blockNumber = Number(returnedBlock.number);
2273
+ didReorgHappened = didReorgHappened || blockNumber < latestSavedBlockNumber;
2274
+ await chainStore.saveBlockNumber({
2275
+ chainId: chain.id,
2276
+ blockNumber,
2277
+ epoch: didReorgHappened ? epoch + 1n : epoch
2278
+ });
2279
+ if (didReorgHappened) {
2280
+ await Promise.all(
2281
+ names.map(
2282
+ async (collectorName) => collectorStore.saveBlockNumber({
2283
+ collectorName,
2284
+ chainId: chain.id,
2285
+ blockNumber,
2286
+ epoch: epoch + 1n
2287
+ })
2288
+ )
2289
+ );
2290
+ }
1973
2291
  });
1974
- }),
1975
- callback: {
1976
- address: decoded[11].address,
1977
- data: decoded[11].data,
1978
- gasLimit: decoded[11].gasLimit
1979
- },
1980
- consumed: 0n,
1981
- blockNumber: Number(blockNumber),
1982
- ...decoded[12] === "0x" ? {} : { signature: decoded[12] }
1983
- });
1984
- return offer;
2292
+ }
2293
+ };
1985
2294
  }
1986
- var consumedEvent = {
1987
- type: "event",
1988
- name: "Consumed",
1989
- inputs: [
1990
- { name: "user", type: "address", indexed: true, internalType: "address" },
1991
- { name: "nonce", type: "uint256", indexed: true, internalType: "uint256" },
1992
- { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }
1993
- ],
1994
- anonymous: false
2295
+ var commonAncestor = (block, unfinalizedBlocks) => {
2296
+ const parent = unfinalizedBlocks.find((b) => b.hash === block.parentHash);
2297
+ if (parent) return parent;
2298
+ return null;
1995
2299
  };
1996
- function fromConsumedLog(parameters) {
1997
- const { blockNumber, logIndex, chainId, transactionHash, user, nonce, amount } = parameters;
2300
+ var fetchFinalizedBlock = async (parameters) => {
2301
+ let { tick, client, chain, logger, collector, unfinalizedBlocks, previousFinalizedBlock } = parameters;
2302
+ let finalizedBlock = previousFinalizedBlock;
2303
+ if (tick % 20 === 0 || previousFinalizedBlock === null) {
2304
+ finalizedBlock = await client.getBlock({
2305
+ blockTag: "finalized",
2306
+ includeTransactions: false
2307
+ });
2308
+ if (finalizedBlock === null || finalizedBlock.number === null) {
2309
+ const msg = "Failed to get finalized block";
2310
+ logger.fatal({ collector, chainId: chain.id, msg });
2311
+ throw new Error(msg);
2312
+ }
2313
+ unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number >= finalizedBlock.number);
2314
+ }
2315
+ if (finalizedBlock.number === null || finalizedBlock.hash === null || finalizedBlock.parentHash === null) {
2316
+ const msg = "Failed to get finalized block";
2317
+ logger.fatal({ collector, chainId: chain.id, msg });
2318
+ throw new Error(msg);
2319
+ }
1998
2320
  return {
1999
- id: `${blockNumber.toString()}-${logIndex.toString()}-${chainId}-${transactionHash}`,
2000
- chainId: BigInt(chainId),
2001
- offering: user,
2002
- nonce,
2003
- amount,
2004
- blockNumber: Number(blockNumber)
2321
+ hash: finalizedBlock.hash,
2322
+ number: finalizedBlock.number,
2323
+ parentHash: finalizedBlock.parentHash
2005
2324
  };
2006
- }
2007
- var InvalidOfferError = class extends BaseError {
2008
- name = "Offer.InvalidOfferError";
2009
- constructor(error2) {
2010
- super("Invalid offer.", { cause: error2 });
2011
- }
2012
2325
  };
2013
- var AccountNotSetError = class extends BaseError {
2014
- name = "Offer.AccountNotSetError";
2015
- constructor() {
2016
- super("Account not set.");
2326
+ var reconcile = async (parameters) => {
2327
+ let { client, block, unfinalizedBlocks, finalizedBlock, logger, collector, chain, maxBatchSize } = parameters;
2328
+ if (block.hash === null || block.number === null || block.parentHash === null)
2329
+ throw new Error("Failed to get block");
2330
+ const latestBlock = unfinalizedBlocks[unfinalizedBlocks.length - 1];
2331
+ if (latestBlock === void 0) {
2332
+ const newBlock2 = {
2333
+ hash: block.hash,
2334
+ number: block.number,
2335
+ parentHash: block.parentHash
2336
+ };
2337
+ unfinalizedBlocks.push(newBlock2);
2338
+ return { block: newBlock2, didReorgHappened: false, unfinalizedBlocks };
2339
+ }
2340
+ if (latestBlock.hash === block.hash)
2341
+ return { block: latestBlock, didReorgHappened: false, unfinalizedBlocks };
2342
+ if (latestBlock.number >= block.number) {
2343
+ const ancestor = commonAncestor(block, unfinalizedBlocks) || finalizedBlock;
2344
+ logger.info({
2345
+ msg: `Reorg detected`,
2346
+ collector,
2347
+ chain_id: chain.id,
2348
+ ancestor: ancestor.number,
2349
+ block_range: [latestBlock.number, block.number]
2350
+ });
2351
+ unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number <= ancestor.number);
2352
+ return { block: ancestor, didReorgHappened: true, unfinalizedBlocks };
2353
+ }
2354
+ if (latestBlock.number + 1n < block.number) {
2355
+ logger.debug({
2356
+ collector,
2357
+ chain_id: chain.id,
2358
+ block_range: [latestBlock.number, block.number],
2359
+ msg: `Missing blocks`
2360
+ });
2361
+ const missingBlockNumbers = (() => {
2362
+ const missingBlockNumbers2 = [];
2363
+ let start2 = latestBlock.number + 1n;
2364
+ const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
2365
+ while (start2 < threshold) {
2366
+ missingBlockNumbers2.push(start2);
2367
+ start2 = start2 + 1n;
2368
+ }
2369
+ return missingBlockNumbers2;
2370
+ })();
2371
+ const missingBlocks = await Promise.all(
2372
+ missingBlockNumbers.map(
2373
+ (blockNumber) => retry(async () => await client.getBlock({ blockNumber, includeTransactions: false }))
2374
+ )
2375
+ );
2376
+ for (const missingBlock of missingBlocks) {
2377
+ const { block: returnedBlock, didReorgHappened } = await reconcile({
2378
+ client,
2379
+ block: missingBlock,
2380
+ unfinalizedBlocks,
2381
+ finalizedBlock,
2382
+ logger,
2383
+ collector,
2384
+ chain,
2385
+ maxBatchSize
2386
+ });
2387
+ if (returnedBlock.number !== missingBlock.number)
2388
+ return { block: returnedBlock, didReorgHappened, unfinalizedBlocks };
2389
+ }
2390
+ return reconcile({
2391
+ client,
2392
+ block,
2393
+ unfinalizedBlocks,
2394
+ finalizedBlock,
2395
+ logger,
2396
+ collector,
2397
+ chain,
2398
+ maxBatchSize
2399
+ });
2400
+ }
2401
+ if (block.parentHash !== latestBlock.hash) {
2402
+ const ancestor = commonAncestor(block, unfinalizedBlocks) || finalizedBlock;
2403
+ logger.info({
2404
+ msg: `Reorg detected`,
2405
+ collector,
2406
+ chain_id: chain.id,
2407
+ ancestor: ancestor.number,
2408
+ block_range: [latestBlock.number, block.number]
2409
+ });
2410
+ unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number <= ancestor.number);
2411
+ return { block: ancestor, didReorgHappened: true, unfinalizedBlocks };
2017
2412
  }
2413
+ const newBlock = {
2414
+ hash: block.hash,
2415
+ number: block.number,
2416
+ parentHash: block.parentHash
2417
+ };
2418
+ unfinalizedBlocks.push(newBlock);
2419
+ return { block: newBlock, didReorgHappened: false, unfinalizedBlocks };
2018
2420
  };
2019
2421
 
2020
- // src/core/types.ts
2021
- var BrandTypeId = Symbol.for("mempool/Brand");
2422
+ // src/collectors/Collector.ts
2423
+ var names = [
2424
+ "mempool_offers",
2425
+ "consumed_events",
2426
+ "buy_with_empty_callback_liquidity",
2427
+ "buy_vault_v1_callback_liquidity",
2428
+ "sell_erc20_callback_liquidity"
2429
+ ];
2430
+ function create2({
2431
+ name,
2432
+ collect,
2433
+ client,
2434
+ chain,
2435
+ withTransaction,
2436
+ collectorStore,
2437
+ options
2438
+ }) {
2439
+ const admin = create({
2440
+ client,
2441
+ chain,
2442
+ withTransaction,
2443
+ options
2444
+ });
2445
+ return {
2446
+ name,
2447
+ chain,
2448
+ collect: lazy((emit) => {
2449
+ const collector = name;
2450
+ const logger = getLogger();
2451
+ logger.info({
2452
+ msg: `Collector started`,
2453
+ collector,
2454
+ chain_id: chain.id,
2455
+ interval: options.interval
2456
+ });
2457
+ return poll(
2458
+ async () => {
2459
+ try {
2460
+ let { blockNumber: lastBlockNumber, epoch } = await collectorStore.getBlockNumber({
2461
+ collectorName: name,
2462
+ chainId: chain.id
2463
+ });
2464
+ await admin.syncBlock();
2465
+ lastBlockNumber = await collect({
2466
+ chain,
2467
+ client,
2468
+ collector: name,
2469
+ epoch,
2470
+ lastBlockNumber,
2471
+ withTransaction
2472
+ });
2473
+ emit(lastBlockNumber);
2474
+ } catch (err) {
2475
+ const isError = err instanceof Error;
2476
+ logger.error({
2477
+ msg: "Collector error",
2478
+ collector,
2479
+ chain_id: chain.id,
2480
+ error: isError ? err.message : String(err),
2481
+ stack: isError ? err.stack : void 0
2482
+ });
2483
+ }
2484
+ },
2485
+ { interval: options.interval }
2486
+ );
2487
+ })
2488
+ };
2489
+ }
2490
+ function start(collector) {
2491
+ let stopped = false;
2492
+ const it = collector.collect();
2493
+ (async () => {
2494
+ while (!stopped) await it.next();
2495
+ await it.return();
2496
+ })();
2497
+ return () => {
2498
+ stopped = true;
2499
+ };
2500
+ }
2022
2501
 
2023
- // src/mempool/MempoolEVMClient.ts
2502
+ // src/mempool/index.ts
2503
+ var mempool_exports = {};
2504
+ __export(mempool_exports, {
2505
+ ChainIdMismatchError: () => ChainIdMismatchError,
2506
+ ViemClientError: () => ViemClientError,
2507
+ WalletAccountNotSetError: () => WalletAccountNotSetError,
2508
+ add: () => add,
2509
+ connect: () => connect,
2510
+ from: () => from7,
2511
+ get: () => get,
2512
+ watch: () => watch
2513
+ });
2024
2514
  var DEFAULT_BATCH_SIZE2 = 100;
2025
2515
  var DEFAULT_BLOCK_WINDOW2 = 100;
2026
- function from6(parameters) {
2516
+ function from7(parameters) {
2027
2517
  const config = {
2028
2518
  client: parameters.client,
2029
2519
  mempoolAddress: parameters.mempoolAddress,
@@ -2165,7 +2655,7 @@ var ChainIdMismatchError = class extends BaseError {
2165
2655
 
2166
2656
  // src/mempool/MempoolClient.ts
2167
2657
  function connect(parameters) {
2168
- return from6(parameters);
2658
+ return from7(parameters);
2169
2659
  }
2170
2660
 
2171
2661
  // src/services/Services.ts
@@ -2173,7 +2663,7 @@ var Services_exports = {};
2173
2663
  __export(Services_exports, {
2174
2664
  createStores: () => createStores,
2175
2665
  createWithTransaction: () => createWithTransaction,
2176
- from: () => from7
2666
+ from: () => from8
2177
2667
  });
2178
2668
 
2179
2669
  // src/indexer/RouterIndexer.ts
@@ -3080,9 +3570,67 @@ function create7(config) {
3080
3570
  })
3081
3571
  );
3082
3572
  }
3083
- const returnedItems = Array.from(items.values());
3084
- const nextCursor = returnedItems.length === limit && returnedItems.length > 0 ? result[result.length - 1].obligationId : null;
3085
- return { obligations: returnedItems, nextCursor };
3573
+ const returnedItems = Array.from(items.values());
3574
+ const nextCursor = returnedItems.length === limit && returnedItems.length > 0 ? result[result.length - 1].obligationId : null;
3575
+ return { obligations: returnedItems, nextCursor };
3576
+ },
3577
+ getQuotes: async (parameters) => {
3578
+ const { obligationIds } = parameters;
3579
+ if (obligationIds.length === 0) return [];
3580
+ const now2 = time_exports.now();
3581
+ const sumConsumed = db.select({
3582
+ consumed: drizzleOrm.sql`COALESCE(SUM(${consumed.consumed}), 0)`.as("consumed")
3583
+ }).from(consumed).where(
3584
+ drizzleOrm.and(
3585
+ drizzleOrm.eq(consumed.offering, offers.offering),
3586
+ drizzleOrm.eq(consumed.nonce, offers.nonce),
3587
+ drizzleOrm.eq(consumed.chainId, offers.chainId)
3588
+ )
3589
+ ).as("sum_consumed");
3590
+ const remaining = drizzleOrm.sql`(${offers.assets} - COALESCE(${sumConsumed.consumed}, 0))`;
3591
+ const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
3592
+ obligationId: offers.obligationId,
3593
+ rate: offers.rate
3594
+ }).from(offers).leftJoinLateral(sumConsumed, drizzleOrm.sql`true`).where(
3595
+ drizzleOrm.and(
3596
+ drizzleOrm.inArray(offers.obligationId, obligationIds),
3597
+ drizzleOrm.gte(offers.expiry, now2),
3598
+ drizzleOrm.gte(offers.maturity, now2),
3599
+ drizzleOrm.eq(offers.buy, side === "buy"),
3600
+ drizzleOrm.sql`${remaining} > 0`
3601
+ )
3602
+ ).orderBy(
3603
+ offers.obligationId,
3604
+ // lower rates first for buy, higher rates first for sell
3605
+ side === "buy" ? drizzleOrm.asc(offers.rate) : drizzleOrm.desc(offers.rate)
3606
+ );
3607
+ const [bestBuys, bestSells] = await Promise.all([
3608
+ query({ side: "buy" }),
3609
+ query({ side: "sell" })
3610
+ ]);
3611
+ const quotes = /* @__PURE__ */ new Map();
3612
+ for (const row of bestSells) {
3613
+ quotes.set(row.obligationId, {
3614
+ ask: { rate: row.rate },
3615
+ bid: { rate: 0n }
3616
+ });
3617
+ }
3618
+ for (const row of bestBuys) {
3619
+ const quote = quotes.get(row.obligationId);
3620
+ if (!quote) {
3621
+ quotes.set(row.obligationId, {
3622
+ ask: { rate: 0n },
3623
+ bid: { rate: row.rate }
3624
+ });
3625
+ continue;
3626
+ }
3627
+ quote.bid = { rate: row.rate };
3628
+ }
3629
+ return Array.from(quotes.entries()).map(([id2, quote]) => {
3630
+ return Quote_exports.from({ obligationId: id2, ask: quote.ask, bid: quote.bid });
3631
+ }).sort((a, b) => {
3632
+ return a.obligationId.localeCompare(b.obligationId);
3633
+ });
3086
3634
  }
3087
3635
  };
3088
3636
  }
@@ -3810,7 +4358,7 @@ async function clean(pg) {
3810
4358
  }
3811
4359
 
3812
4360
  // src/services/Services.ts
3813
- function from7(config) {
4361
+ function from8(config) {
3814
4362
  const {
3815
4363
  chain,
3816
4364
  rpcUrl,
@@ -5462,7 +6010,7 @@ function handleZodError(error2) {
5462
6010
  return new ValidationError("Validation failed", formattedErrors);
5463
6011
  }
5464
6012
 
5465
- // src/api/Api/Controllers/getHealth.ts
6013
+ // src/api/Controllers/getHealth.ts
5466
6014
  async function getHealth(healthService) {
5467
6015
  const logger = Logger_exports.getLogger();
5468
6016
  try {
@@ -5527,269 +6075,31 @@ async function getHealthCollectors(healthService) {
5527
6075
  return error(err);
5528
6076
  }
5529
6077
  }
5530
- var CollectorHealth = v4.z.object({
5531
- name: v4.z.string(),
5532
- chain_id: v4.z.number(),
5533
- block_number: v4.z.number().nullable(),
5534
- updated_at: v4.z.string().nullable(),
5535
- lag: v4.z.number().nullable(),
5536
- status: v4.z.enum(["live", "lagging", "unknown"])
5537
- });
5538
- var CollectorsHealthResponse = v4.z.object({
5539
- collectors: v4.z.array(CollectorHealth)
5540
- });
5541
- var ChainHealth = v4.z.object({
5542
- chain_id: v4.z.number(),
5543
- block_number: v4.z.number(),
5544
- updated_at: v4.z.string()
5545
- });
5546
- var ChainsHealthResponse = v4.z.object({
5547
- chains: v4.z.array(ChainHealth)
5548
- });
5549
- var RouterStatusResponse = v4.z.object({
5550
- status: v4.z.enum(["live", "syncing"])
5551
- });
5552
6078
 
5553
- // src/api/Api/Schema/ObligationResponse.ts
6079
+ // src/api/Schema/ObligationResponse.ts
5554
6080
  var ObligationResponse_exports = {};
5555
6081
  __export(ObligationResponse_exports, {
5556
- from: () => from8
6082
+ from: () => from9
5557
6083
  });
5558
- function from8(obligation) {
5559
- return toSnakeCase({ id: Obligation_exports.id(obligation), ...obligation });
6084
+ function from9(obligation, quote) {
6085
+ return toSnakeCase({
6086
+ id: quote.obligationId,
6087
+ ...obligation,
6088
+ ask: quote.ask,
6089
+ bid: quote.bid
6090
+ });
5560
6091
  }
5561
6092
 
5562
- // src/api/Api/Schema/OfferResponse.ts
6093
+ // src/api/Schema/OfferResponse.ts
5563
6094
  var OfferResponse_exports = {};
5564
6095
  __export(OfferResponse_exports, {
5565
- from: () => from9
6096
+ from: () => from10
5566
6097
  });
5567
- function from9(offer) {
6098
+ function from10(offer) {
5568
6099
  return toSnakeCase(offer);
5569
6100
  }
5570
- var MAX_LIMIT = 100;
5571
- var DEFAULT_LIMIT = 20;
5572
- var PaginationQueryParams = z6__namespace.object({
5573
- cursor: z6__namespace.string().optional().refine(
5574
- (val) => {
5575
- if (!val) return true;
5576
- try {
5577
- const decoded = Cursor_exports.decode(val);
5578
- return decoded !== null;
5579
- } catch (_error) {
5580
- return false;
5581
- }
5582
- },
5583
- {
5584
- message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
5585
- }
5586
- ).meta({
5587
- description: "Pagination cursor in base64url-encoded format",
5588
- example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
5589
- }),
5590
- limit: z6__namespace.string().regex(/^[1-9]\d*$/, {
5591
- message: "Limit must be a positive integer"
5592
- }).transform((val) => Number.parseInt(val, 10)).pipe(
5593
- z6__namespace.number().max(MAX_LIMIT, {
5594
- message: `Limit cannot exceed ${MAX_LIMIT}`
5595
- })
5596
- ).optional().default(DEFAULT_LIMIT).meta({
5597
- description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
5598
- example: 10
5599
- })
5600
- });
5601
- var GetOffersQueryParams = z6__namespace.object({
5602
- ...PaginationQueryParams.shape,
5603
- side: z6__namespace.enum(["buy", "sell"]).meta({
5604
- description: "Side of the offer.",
5605
- example: "buy"
5606
- }),
5607
- obligation_id: z6__namespace.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
5608
- description: "Offers obligation id",
5609
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
5610
- })
5611
- });
5612
- var GetObligationsQueryParams = z6__namespace.object({
5613
- ...PaginationQueryParams.shape,
5614
- cursor: z6__namespace.string().optional().meta({
5615
- description: "Obligation id cursor",
5616
- example: "0x1234567890123456789012345678901234567890123456789012345678901234"
5617
- })
5618
- });
5619
- var schemas = {
5620
- get_offers: GetOffersQueryParams,
5621
- get_obligations: GetObligationsQueryParams
5622
- };
5623
- function parse(action, query) {
5624
- return schemas[action].parse(query);
5625
- }
5626
- function safeParse(action, query, error2) {
5627
- return schemas[action].safeParse(query, {
5628
- error: error2
5629
- });
5630
- }
5631
-
5632
- // src/api/Api/Schema/openapi.ts
5633
- var successResponseSchema = v4.z.object({
5634
- status: v4.z.literal("success"),
5635
- cursor: v4.z.string().nullable(),
5636
- data: v4.z.array(v4.z.any()),
5637
- meta: v4.z.object({
5638
- timestamp: v4.z.string()
5639
- })
5640
- });
5641
- var errorResponseSchema = v4.z.object({
5642
- status: v4.z.literal("error"),
5643
- error: v4.z.object({
5644
- code: v4.z.string(),
5645
- message: v4.z.string(),
5646
- details: v4.z.any().optional()
5647
- }),
5648
- meta: v4.z.object({
5649
- timestamp: v4.z.string()
5650
- })
5651
- });
5652
- var paths = {
5653
- "/v1/offers": {
5654
- get: {
5655
- summary: "Offers",
5656
- description: "Find offers that match specific criteria",
5657
- tags: ["Offers"],
5658
- requestParams: {
5659
- query: GetOffersQueryParams
5660
- },
5661
- responses: {
5662
- 200: {
5663
- description: "Success",
5664
- content: {
5665
- "application/json": {
5666
- schema: successResponseSchema
5667
- }
5668
- }
5669
- },
5670
- 400: {
5671
- description: "Bad Request",
5672
- content: {
5673
- "application/json": {
5674
- schema: errorResponseSchema
5675
- }
5676
- }
5677
- }
5678
- }
5679
- }
5680
- },
5681
- "/v1/obligations": {
5682
- get: {
5683
- summary: "Obligations",
5684
- description: "List obligations with pagination support",
5685
- tags: ["Obligations"],
5686
- requestParams: {
5687
- query: GetObligationsQueryParams
5688
- },
5689
- responses: {
5690
- 200: {
5691
- description: "Success",
5692
- content: {
5693
- "application/json": {
5694
- schema: successResponseSchema
5695
- }
5696
- }
5697
- },
5698
- 400: {
5699
- description: "Bad Request",
5700
- content: {
5701
- "application/json": {
5702
- schema: errorResponseSchema
5703
- }
5704
- }
5705
- }
5706
- }
5707
- }
5708
- },
5709
- "/v1/health": {
5710
- get: {
5711
- summary: "Router status",
5712
- description: "Retrieve the aggregated status of the router.",
5713
- tags: ["Health"],
5714
- responses: {
5715
- 200: {
5716
- description: "Success",
5717
- content: {
5718
- "application/json": {
5719
- schema: RouterStatusResponse
5720
- }
5721
- }
5722
- }
5723
- }
5724
- }
5725
- },
5726
- "/v1/health/collectors": {
5727
- get: {
5728
- summary: "Collectors health",
5729
- description: "Retrieve the block numbers processed by collectors and their sync status.",
5730
- tags: ["Health"],
5731
- responses: {
5732
- 200: {
5733
- description: "Success",
5734
- content: {
5735
- "application/json": {
5736
- schema: CollectorsHealthResponse
5737
- }
5738
- }
5739
- }
5740
- }
5741
- }
5742
- },
5743
- "/v1/health/chains": {
5744
- get: {
5745
- summary: "Chains health",
5746
- description: "Retrieve the latest block processed for each chain.",
5747
- tags: ["Health"],
5748
- responses: {
5749
- 200: {
5750
- description: "Success",
5751
- content: {
5752
- "application/json": {
5753
- schema: ChainsHealthResponse
5754
- }
5755
- }
5756
- }
5757
- }
5758
- }
5759
- }
5760
- };
5761
- var OpenApi = zodOpenapi.createDocument({
5762
- openapi: "3.1.0",
5763
- info: {
5764
- title: "Router API",
5765
- version: "1.0.0",
5766
- description: "API for the Morpho Router"
5767
- },
5768
- tags: [
5769
- {
5770
- name: "Offers"
5771
- },
5772
- {
5773
- name: "Obligations"
5774
- },
5775
- {
5776
- name: "Health"
5777
- }
5778
- ],
5779
- servers: [
5780
- {
5781
- url: "https://router.morpho.dev",
5782
- description: "Production server"
5783
- },
5784
- {
5785
- url: "http://localhost:8081",
5786
- description: "Local development server"
5787
- }
5788
- ],
5789
- paths
5790
- });
5791
6101
 
5792
- // src/api/Api/Controllers/getObligations.ts
6102
+ // src/api/Controllers/getObligations.ts
5793
6103
  async function getObligations(queryParameters, store) {
5794
6104
  const logger = Logger_exports.getLogger();
5795
6105
  const result = safeParse("get_obligations", queryParameters, (issue) => issue.message);
@@ -5800,8 +6110,20 @@ async function getObligations(queryParameters, store) {
5800
6110
  cursor: query.cursor,
5801
6111
  limit: query.limit
5802
6112
  });
6113
+ const quotes = await store.getQuotes({
6114
+ obligationIds: obligations2.map((o) => Obligation_exports.id(o))
6115
+ });
5803
6116
  return success({
5804
- data: obligations2.map((o) => ObligationResponse_exports.from(o)),
6117
+ data: obligations2.map(
6118
+ (o) => ObligationResponse_exports.from(
6119
+ o,
6120
+ quotes.find((q) => q.obligationId === Obligation_exports.id(o)) ?? {
6121
+ obligationId: Obligation_exports.id(o),
6122
+ ask: { rate: 0n },
6123
+ bid: { rate: 0n }
6124
+ }
6125
+ )
6126
+ ),
5805
6127
  cursor: nextCursor ?? null
5806
6128
  });
5807
6129
  } catch (err) {
@@ -5815,7 +6137,7 @@ async function getObligations(queryParameters, store) {
5815
6137
  }
5816
6138
  }
5817
6139
 
5818
- // src/api/Api/Controllers/getOffers.ts
6140
+ // src/api/Controllers/getOffers.ts
5819
6141
  async function getOffers(queryParameters, store) {
5820
6142
  const logger = Logger_exports.getLogger();
5821
6143
  const result = safeParse("get_offers", queryParameters, (issue) => issue.message);
@@ -5843,7 +6165,7 @@ async function getOffers(queryParameters, store) {
5843
6165
  }
5844
6166
  }
5845
6167
 
5846
- // src/api/Api/Api.ts
6168
+ // src/api/Api.ts
5847
6169
  function create8(config) {
5848
6170
  return {
5849
6171
  serve: ({ port }) => {
@@ -5875,13 +6197,20 @@ function serve(parameters) {
5875
6197
  const { statusCode, body } = await getHealthChains(healthService);
5876
6198
  return c.json(body, statusCode);
5877
6199
  });
6200
+ app.get(
6201
+ "/docs/swagger.json",
6202
+ (c) => c.text(JSON.stringify(getSwaggerJson()), 200, {
6203
+ "Content-Type": "application/json; charset=utf-8"
6204
+ })
6205
+ );
6206
+ app.get("/docs", (c) => c.html(getDocsHtml(), 200));
5878
6207
  nodeServer.serve({
5879
6208
  fetch: app.fetch,
5880
6209
  port: parameters.port
5881
6210
  });
5882
6211
  }
5883
6212
 
5884
- // src/api/Client.ts
6213
+ // src/client/Client.ts
5885
6214
  var Client_exports = {};
5886
6215
  __export(Client_exports, {
5887
6216
  HttpForbiddenError: () => HttpForbiddenError,
@@ -5936,8 +6265,16 @@ async function getObligations2(config, parameters) {
5936
6265
  if (parameters?.limit !== void 0) {
5937
6266
  url.searchParams.set("limit", parameters.limit.toString());
5938
6267
  }
5939
- const { cursor: returnedCursor, data: obligationsSnake } = await getApi(config, url);
5940
- const obligations2 = obligationsSnake.map(Obligation_exports.fromSnakeCase);
6268
+ const { cursor: returnedCursor, data: items } = await getApi(config, url);
6269
+ const obligations2 = items.map((item) => {
6270
+ const obligation = Obligation_exports.fromSnakeCase(item);
6271
+ const { obligationId: _, ...returned } = {
6272
+ id: () => Obligation_exports.id(obligation),
6273
+ ...obligation,
6274
+ ...Quote_exports.fromSnakeCase({ obligation_id: item.id, ask: item.ask, bid: item.bid })
6275
+ };
6276
+ return returned;
6277
+ });
5941
6278
  return {
5942
6279
  cursor: returnedCursor,
5943
6280
  obligations: obligations2
@@ -6054,7 +6391,8 @@ exports.OfferStore = OfferStore_exports;
6054
6391
  exports.OffersSchema = schema_exports;
6055
6392
  exports.OpenApi = OpenApi;
6056
6393
  exports.PG = PG_exports;
6057
- exports.RouterApi = Api_exports2;
6394
+ exports.Quote = Quote_exports;
6395
+ exports.RouterApi = RouterApi_exports;
6058
6396
  exports.RouterClient = Client_exports;
6059
6397
  exports.RouterIndexer = RouterIndexer_exports;
6060
6398
  exports.RouterStatusResponse = RouterStatusResponse;