@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.
- package/README.md +6 -6
- package/dist/cli.js +970 -565
- package/dist/cli.js.map +1 -1
- package/dist/index.browser.d.cts +182 -79
- package/dist/index.browser.d.ts +182 -79
- package/dist/index.browser.js +443 -235
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +444 -237
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.cts +235 -124
- package/dist/index.node.d.ts +235 -124
- package/dist/index.node.js +2361 -2023
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +2361 -2024
- package/dist/index.node.mjs.map +1 -1
- package/package.json +6 -5
package/dist/cli.js
CHANGED
|
@@ -10,17 +10,17 @@ import { spawn } from 'child_process';
|
|
|
10
10
|
import 'fs';
|
|
11
11
|
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
|
|
12
12
|
import * as z6 from 'zod';
|
|
13
|
-
import { Base64 } from 'js-base64';
|
|
14
13
|
import { readFile } from 'fs/promises';
|
|
15
14
|
import dotenv from 'dotenv';
|
|
16
15
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
17
16
|
import { serve as serve$1 } from '@hono/node-server';
|
|
18
17
|
import { Hono } from 'hono';
|
|
19
18
|
import { cors } from 'hono/cors';
|
|
20
|
-
import { asc, desc, and, eq, gt, gte, sql, lte, inArray } from 'drizzle-orm';
|
|
21
|
-
import { pgSchema, integer, varchar, bigint, timestamp, text, boolean, numeric, index, primaryKey, uniqueIndex } from 'drizzle-orm/pg-core';
|
|
22
19
|
import { z } from 'zod/v4';
|
|
23
20
|
import { createDocument } from 'zod-openapi';
|
|
21
|
+
import { Base64 } from 'js-base64';
|
|
22
|
+
import { asc, desc, and, eq, gt, gte, sql, lte, inArray } from 'drizzle-orm';
|
|
23
|
+
import { pgSchema, integer, varchar, bigint, timestamp, text, boolean, numeric, index, primaryKey, uniqueIndex } from 'drizzle-orm/pg-core';
|
|
24
24
|
import { PGlite } from '@electric-sql/pglite';
|
|
25
25
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
26
26
|
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
|
@@ -41,7 +41,7 @@ var __export = (target, all) => {
|
|
|
41
41
|
// package.json
|
|
42
42
|
var package_default = {
|
|
43
43
|
name: "@morpho-dev/router",
|
|
44
|
-
version: "0.1.
|
|
44
|
+
version: "0.1.18",
|
|
45
45
|
description: "Router package for Morpho protocol"};
|
|
46
46
|
|
|
47
47
|
// src/core/Chain.ts
|
|
@@ -142,8 +142,12 @@ var chains = {
|
|
|
142
142
|
[
|
|
143
143
|
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
144
144
|
// USDC
|
|
145
|
-
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
|
145
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
146
146
|
// DAI
|
|
147
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
148
|
+
// WETH
|
|
149
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
150
|
+
// WBTC
|
|
147
151
|
].map((address) => address.toLowerCase())
|
|
148
152
|
),
|
|
149
153
|
morpho: "0x0000000000000000000000000000000000000000",
|
|
@@ -166,8 +170,12 @@ var chains = {
|
|
|
166
170
|
[
|
|
167
171
|
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
168
172
|
// USDC
|
|
169
|
-
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
|
|
173
|
+
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
|
170
174
|
// DAI
|
|
175
|
+
"0x4200000000000000000000000000000000000006",
|
|
176
|
+
// WETH
|
|
177
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
178
|
+
// WBTC
|
|
171
179
|
].map((address) => address.toLowerCase())
|
|
172
180
|
),
|
|
173
181
|
morpho: "0x0000000000000000000000000000000000000000",
|
|
@@ -190,8 +198,12 @@ var chains = {
|
|
|
190
198
|
[
|
|
191
199
|
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
192
200
|
// USDC
|
|
193
|
-
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
|
201
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
194
202
|
// DAI
|
|
203
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
204
|
+
// WETH
|
|
205
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
206
|
+
// WBTC
|
|
195
207
|
].map((address) => address.toLowerCase())
|
|
196
208
|
),
|
|
197
209
|
morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
|
|
@@ -215,8 +227,12 @@ var chains = {
|
|
|
215
227
|
[
|
|
216
228
|
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
217
229
|
// USDC
|
|
218
|
-
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
|
230
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
219
231
|
// DAI
|
|
232
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
233
|
+
// WETH
|
|
234
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
235
|
+
// WBTC
|
|
220
236
|
].map((address) => address.toLowerCase())
|
|
221
237
|
),
|
|
222
238
|
morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
|
|
@@ -727,97 +743,6 @@ var from2 = (parameters) => {
|
|
|
727
743
|
};
|
|
728
744
|
};
|
|
729
745
|
|
|
730
|
-
// src/core/Cursor.ts
|
|
731
|
-
var Cursor_exports = {};
|
|
732
|
-
__export(Cursor_exports, {
|
|
733
|
-
decode: () => decode,
|
|
734
|
-
encode: () => encode,
|
|
735
|
-
validate: () => validate
|
|
736
|
-
});
|
|
737
|
-
function validate(cursor) {
|
|
738
|
-
if (!cursor || typeof cursor !== "object") {
|
|
739
|
-
throw new Error("Cursor must be an object");
|
|
740
|
-
}
|
|
741
|
-
const c = cursor;
|
|
742
|
-
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
743
|
-
throw new Error(
|
|
744
|
-
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
|
-
if (!["asc", "desc"].includes(c.dir)) {
|
|
748
|
-
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
749
|
-
}
|
|
750
|
-
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
751
|
-
throw new Error(
|
|
752
|
-
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
753
|
-
);
|
|
754
|
-
}
|
|
755
|
-
const validations = {
|
|
756
|
-
rate: {
|
|
757
|
-
field: "rate",
|
|
758
|
-
type: "string",
|
|
759
|
-
pattern: /^\d+$/,
|
|
760
|
-
error: "numeric string"
|
|
761
|
-
},
|
|
762
|
-
amount: {
|
|
763
|
-
field: "assets",
|
|
764
|
-
type: "string",
|
|
765
|
-
pattern: /^\d+$/,
|
|
766
|
-
error: "numeric string"
|
|
767
|
-
},
|
|
768
|
-
maturity: {
|
|
769
|
-
field: "maturity",
|
|
770
|
-
type: "number",
|
|
771
|
-
validator: (val) => val > 0,
|
|
772
|
-
error: "positive number"
|
|
773
|
-
},
|
|
774
|
-
expiry: {
|
|
775
|
-
field: "expiry",
|
|
776
|
-
type: "number",
|
|
777
|
-
validator: (val) => val > 0,
|
|
778
|
-
error: "positive number"
|
|
779
|
-
}
|
|
780
|
-
};
|
|
781
|
-
const validation = validations[c.sort];
|
|
782
|
-
if (!validation) {
|
|
783
|
-
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
784
|
-
}
|
|
785
|
-
const fieldValue = c[validation.field];
|
|
786
|
-
if (!fieldValue) {
|
|
787
|
-
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
788
|
-
}
|
|
789
|
-
if (typeof fieldValue !== validation.type) {
|
|
790
|
-
throw new Error(
|
|
791
|
-
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
792
|
-
);
|
|
793
|
-
}
|
|
794
|
-
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
795
|
-
throw new Error(
|
|
796
|
-
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
if (validation.validator && !validation.validator(fieldValue)) {
|
|
800
|
-
throw new Error(
|
|
801
|
-
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
if (c.page !== void 0) {
|
|
805
|
-
if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
|
|
806
|
-
throw new Error("Invalid page: must be a positive integer");
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return true;
|
|
810
|
-
}
|
|
811
|
-
function encode(c) {
|
|
812
|
-
return Base64.encodeURL(JSON.stringify(c));
|
|
813
|
-
}
|
|
814
|
-
function decode(token) {
|
|
815
|
-
if (!token) return null;
|
|
816
|
-
const decoded = JSON.parse(Base64.decode(token));
|
|
817
|
-
validate(decoded);
|
|
818
|
-
return decoded;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
746
|
// src/core/Liquidity.ts
|
|
822
747
|
var Liquidity_exports = {};
|
|
823
748
|
__export(Liquidity_exports, {
|
|
@@ -1133,9 +1058,9 @@ __export(Offer_exports, {
|
|
|
1133
1058
|
OfferHashSchema: () => OfferHashSchema,
|
|
1134
1059
|
OfferSchema: () => OfferSchema,
|
|
1135
1060
|
consumedEvent: () => consumedEvent,
|
|
1136
|
-
decode: () =>
|
|
1061
|
+
decode: () => decode,
|
|
1137
1062
|
domain: () => domain,
|
|
1138
|
-
encode: () =>
|
|
1063
|
+
encode: () => encode,
|
|
1139
1064
|
from: () => from5,
|
|
1140
1065
|
fromConsumedLog: () => fromConsumedLog,
|
|
1141
1066
|
fromSnakeCase: () => fromSnakeCase3,
|
|
@@ -1306,39 +1231,92 @@ function fromSnakeCase3(input) {
|
|
|
1306
1231
|
function toSnakeCase2(offer) {
|
|
1307
1232
|
return toSnakeCase(offer);
|
|
1308
1233
|
}
|
|
1309
|
-
function random2() {
|
|
1310
|
-
const
|
|
1311
|
-
const
|
|
1312
|
-
const
|
|
1313
|
-
const
|
|
1234
|
+
function random2(config) {
|
|
1235
|
+
const chain = config?.chains ? config.chains[Math.floor(Math.random() * config.chains.length)] : chains.ethereum;
|
|
1236
|
+
const loanToken = config?.loanTokens ? config.loanTokens[Math.floor(Math.random() * config.loanTokens.length)] : privateKeyToAccount(generatePrivateKey()).address;
|
|
1237
|
+
const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [privateKeyToAccount(generatePrivateKey()).address];
|
|
1238
|
+
const collateralAsset = collateralCandidates[Math.floor(Math.random() * collateralCandidates.length)];
|
|
1239
|
+
const maturityOption = weightedChoice([
|
|
1240
|
+
["end_of_month", 1],
|
|
1241
|
+
["end_of_next_month", 1]
|
|
1242
|
+
]);
|
|
1243
|
+
const maturity = config?.maturity ?? from3(maturityOption);
|
|
1244
|
+
const lltv = from(
|
|
1245
|
+
weightedChoice([
|
|
1246
|
+
[0.385, 1],
|
|
1247
|
+
[0.5, 1],
|
|
1248
|
+
[0.625, 2],
|
|
1249
|
+
[0.77, 8],
|
|
1250
|
+
[0.86, 10],
|
|
1251
|
+
[0.915, 8],
|
|
1252
|
+
[0.945, 6],
|
|
1253
|
+
[0.965, 4],
|
|
1254
|
+
[0.98, 2]
|
|
1255
|
+
])
|
|
1256
|
+
);
|
|
1257
|
+
const buy = config?.buy !== void 0 ? config.buy : Math.random() > 0.5;
|
|
1258
|
+
const ONE = 1000000000000000000n;
|
|
1259
|
+
const qMin = buy ? 16 : 4;
|
|
1260
|
+
const qMax = buy ? 32 : 16;
|
|
1261
|
+
const len = qMax - qMin + 1;
|
|
1262
|
+
const ratePairs = Array.from(
|
|
1263
|
+
{ length: len },
|
|
1264
|
+
(_, idx) => {
|
|
1265
|
+
const q = qMin + idx;
|
|
1266
|
+
const scaledRate = BigInt(q) * (ONE / 4n);
|
|
1267
|
+
const weight = buy ? 1 + idx : 1 + (len - 1 - idx);
|
|
1268
|
+
return [scaledRate, weight];
|
|
1269
|
+
}
|
|
1270
|
+
);
|
|
1271
|
+
const rate = config?.rate ?? weightedChoice(ratePairs);
|
|
1272
|
+
const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
|
|
1273
|
+
const unit = BigInt(10) ** BigInt(loanTokenDecimals);
|
|
1274
|
+
const amountBase = BigInt(100 + Math.floor(Math.random() * (1e6 - 100 + 1)));
|
|
1275
|
+
const assetsScaled = config?.assets ?? amountBase * unit;
|
|
1276
|
+
const consumed2 = config?.consumed !== void 0 ? config.consumed : Math.random() < 0.8 ? 0n : assetsScaled * BigInt(1 + Math.floor(Math.random() * 900)) / 1000n;
|
|
1277
|
+
const callbackBySide = (() => {
|
|
1278
|
+
if (buy) return { address: zeroAddress, data: "0x", gasLimit: 0n };
|
|
1279
|
+
const sellCallbackAddress = WhitelistedCallbackAddresses["sell_erc20_callback" /* SellERC20Callback */][0].toLowerCase();
|
|
1280
|
+
const amount = assetsScaled * 1000000000000000000000n;
|
|
1281
|
+
const data = encodeSellERC20Callback({
|
|
1282
|
+
collaterals: [collateralAsset],
|
|
1283
|
+
amounts: [amount]
|
|
1284
|
+
});
|
|
1285
|
+
return { address: sellCallbackAddress, data, gasLimit: 500000n };
|
|
1286
|
+
})();
|
|
1314
1287
|
const offer = from5({
|
|
1315
|
-
offering: privateKeyToAccount(generatePrivateKey()).address,
|
|
1316
|
-
assets:
|
|
1317
|
-
rate
|
|
1288
|
+
offering: config?.offering ?? privateKeyToAccount(generatePrivateKey()).address,
|
|
1289
|
+
assets: assetsScaled,
|
|
1290
|
+
rate,
|
|
1318
1291
|
maturity,
|
|
1319
|
-
expiry,
|
|
1320
|
-
start:
|
|
1292
|
+
expiry: config?.expiry ?? maturity - 1,
|
|
1293
|
+
start: config?.start ?? maturity - 10,
|
|
1321
1294
|
nonce: BigInt(Math.floor(Math.random() * 1e6)),
|
|
1322
|
-
buy
|
|
1323
|
-
chainId:
|
|
1295
|
+
buy,
|
|
1296
|
+
chainId: chain.id,
|
|
1324
1297
|
loanToken,
|
|
1325
|
-
collaterals: [
|
|
1298
|
+
collaterals: config?.collaterals ?? [
|
|
1326
1299
|
from2({
|
|
1327
|
-
asset:
|
|
1300
|
+
asset: collateralAsset,
|
|
1328
1301
|
oracle: zeroAddress,
|
|
1329
1302
|
lltv
|
|
1330
1303
|
})
|
|
1331
1304
|
],
|
|
1332
|
-
callback:
|
|
1333
|
-
|
|
1334
|
-
data: "0x",
|
|
1335
|
-
gasLimit: 0n
|
|
1336
|
-
},
|
|
1337
|
-
consumed: 0n,
|
|
1305
|
+
callback: config?.callback ?? callbackBySide,
|
|
1306
|
+
consumed: consumed2,
|
|
1338
1307
|
blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
|
|
1339
1308
|
});
|
|
1340
1309
|
return offer;
|
|
1341
1310
|
}
|
|
1311
|
+
var weightedChoice = (pairs) => {
|
|
1312
|
+
const total = pairs.reduce((sum, [, weight]) => sum + weight, 0);
|
|
1313
|
+
let roll = Math.random() * total;
|
|
1314
|
+
for (const [value, weight] of pairs) {
|
|
1315
|
+
roll -= weight;
|
|
1316
|
+
if (roll < 0) return value;
|
|
1317
|
+
}
|
|
1318
|
+
return pairs[0][0];
|
|
1319
|
+
};
|
|
1342
1320
|
var domain = (chainId) => ({
|
|
1343
1321
|
chainId,
|
|
1344
1322
|
verifyingContract: zeroAddress
|
|
@@ -1460,7 +1438,7 @@ var OfferAbi = [
|
|
|
1460
1438
|
},
|
|
1461
1439
|
{ name: "signature", type: "bytes" }
|
|
1462
1440
|
];
|
|
1463
|
-
function
|
|
1441
|
+
function encode(offer) {
|
|
1464
1442
|
return encodeAbiParameters(OfferAbi, [
|
|
1465
1443
|
offer.offering,
|
|
1466
1444
|
offer.assets,
|
|
@@ -1477,7 +1455,7 @@ function encode2(offer) {
|
|
|
1477
1455
|
offer.signature ?? "0x"
|
|
1478
1456
|
]);
|
|
1479
1457
|
}
|
|
1480
|
-
function
|
|
1458
|
+
function decode(data, blockNumber) {
|
|
1481
1459
|
let decoded;
|
|
1482
1460
|
try {
|
|
1483
1461
|
decoded = decodeAbiParameters(OfferAbi, data);
|
|
@@ -1547,6 +1525,57 @@ var AccountNotSetError = class extends BaseError {
|
|
|
1547
1525
|
}
|
|
1548
1526
|
};
|
|
1549
1527
|
|
|
1528
|
+
// src/core/Quote.ts
|
|
1529
|
+
var Quote_exports = {};
|
|
1530
|
+
__export(Quote_exports, {
|
|
1531
|
+
InvalidQuoteError: () => InvalidQuoteError,
|
|
1532
|
+
QuoteSchema: () => QuoteSchema,
|
|
1533
|
+
from: () => from6,
|
|
1534
|
+
fromSnakeCase: () => fromSnakeCase4,
|
|
1535
|
+
random: () => random3
|
|
1536
|
+
});
|
|
1537
|
+
var QuoteSchema = z6.object({
|
|
1538
|
+
obligationId: z6.string().transform(transformHex),
|
|
1539
|
+
ask: z6.object({
|
|
1540
|
+
rate: z6.bigint({ coerce: true }).min(0n).max(maxUint256)
|
|
1541
|
+
}),
|
|
1542
|
+
bid: z6.object({
|
|
1543
|
+
rate: z6.bigint({ coerce: true }).min(0n).max(maxUint256)
|
|
1544
|
+
})
|
|
1545
|
+
});
|
|
1546
|
+
function from6(parameters) {
|
|
1547
|
+
try {
|
|
1548
|
+
const parsedQuote = QuoteSchema.parse(parameters);
|
|
1549
|
+
return {
|
|
1550
|
+
obligationId: parsedQuote.obligationId,
|
|
1551
|
+
ask: parsedQuote.ask,
|
|
1552
|
+
bid: parsedQuote.bid
|
|
1553
|
+
};
|
|
1554
|
+
} catch (error2) {
|
|
1555
|
+
throw new InvalidQuoteError(error2);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
function fromSnakeCase4(snake) {
|
|
1559
|
+
return from6(fromSnakeCase(snake));
|
|
1560
|
+
}
|
|
1561
|
+
function random3() {
|
|
1562
|
+
return from6({
|
|
1563
|
+
obligationId: Obligation_exports.id(Obligation_exports.random()),
|
|
1564
|
+
ask: {
|
|
1565
|
+
rate: BigInt(Math.floor(Math.random() * 1e6))
|
|
1566
|
+
},
|
|
1567
|
+
bid: {
|
|
1568
|
+
rate: BigInt(Math.floor(Math.random() * 1e6))
|
|
1569
|
+
}
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
var InvalidQuoteError = class extends BaseError {
|
|
1573
|
+
name = "Quote.InvalidQuoteError";
|
|
1574
|
+
constructor(error2) {
|
|
1575
|
+
super("Invalid quote.", { cause: error2 });
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1550
1579
|
// src/evm/EVM.ts
|
|
1551
1580
|
var users = [
|
|
1552
1581
|
privateKeyToAccount(
|
|
@@ -1663,11 +1692,16 @@ function defaultLogger(minLevel, pretty) {
|
|
|
1663
1692
|
return;
|
|
1664
1693
|
}
|
|
1665
1694
|
const { msg, ...rest } = entry;
|
|
1695
|
+
const stack = typeof rest.stack === "string" ? rest.stack : void 0;
|
|
1696
|
+
if (stack) delete rest.stack;
|
|
1666
1697
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
1667
1698
|
const level = methodLevel.toUpperCase();
|
|
1668
1699
|
const extras = Object.entries(rest).map(([k, v]) => `${k}=${formatValue(v)}`).join(" ");
|
|
1669
1700
|
const line = extras.length > 0 ? `${timestamp2} [${level}] ${msg} ${extras}` : `${timestamp2} [${level}] ${msg}`;
|
|
1670
1701
|
console[consoleMethod](line);
|
|
1702
|
+
if (stack) {
|
|
1703
|
+
console[consoleMethod](stack);
|
|
1704
|
+
}
|
|
1671
1705
|
} : () => {
|
|
1672
1706
|
};
|
|
1673
1707
|
return {
|
|
@@ -1709,30 +1743,466 @@ function formatValue(value) {
|
|
|
1709
1743
|
}
|
|
1710
1744
|
}
|
|
1711
1745
|
}
|
|
1746
|
+
var CollectorHealth = z.object({
|
|
1747
|
+
name: z.string(),
|
|
1748
|
+
chain_id: z.number(),
|
|
1749
|
+
block_number: z.number().nullable(),
|
|
1750
|
+
updated_at: z.string().nullable(),
|
|
1751
|
+
lag: z.number().nullable(),
|
|
1752
|
+
status: z.enum(["live", "lagging", "unknown"])
|
|
1753
|
+
});
|
|
1754
|
+
var CollectorsHealthResponse = z.array(CollectorHealth);
|
|
1755
|
+
var ChainHealth = z.object({
|
|
1756
|
+
chain_id: z.number(),
|
|
1757
|
+
block_number: z.number(),
|
|
1758
|
+
updated_at: z.string()
|
|
1759
|
+
});
|
|
1760
|
+
var ChainsHealthResponse = z.array(ChainHealth);
|
|
1761
|
+
var RouterStatusResponse = z.object({
|
|
1762
|
+
status: z.enum(["live", "syncing"])
|
|
1763
|
+
});
|
|
1712
1764
|
|
|
1713
|
-
// src/
|
|
1714
|
-
var
|
|
1715
|
-
__export(
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
|
|
1720
|
-
fetchCollateralAndDebt: () => fetchCollateralAndDebt,
|
|
1721
|
-
fetchOraclePrices: () => fetchOraclePrices,
|
|
1722
|
-
fetchUserVaultMarketLiquidity: () => fetchUserVaultMarketLiquidity,
|
|
1723
|
-
morpho: () => morpho,
|
|
1724
|
-
names: () => names,
|
|
1725
|
-
run: () => run,
|
|
1726
|
-
single: () => single,
|
|
1727
|
-
start: () => start
|
|
1765
|
+
// src/stores/utils/Cursor.ts
|
|
1766
|
+
var Cursor_exports = {};
|
|
1767
|
+
__export(Cursor_exports, {
|
|
1768
|
+
decode: () => decode2,
|
|
1769
|
+
encode: () => encode2,
|
|
1770
|
+
validate: () => validate
|
|
1728
1771
|
});
|
|
1772
|
+
function validate(cursor) {
|
|
1773
|
+
if (!cursor || typeof cursor !== "object") {
|
|
1774
|
+
throw new Error("Cursor must be an object");
|
|
1775
|
+
}
|
|
1776
|
+
const c = cursor;
|
|
1777
|
+
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
1778
|
+
throw new Error(
|
|
1779
|
+
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
if (!["asc", "desc"].includes(c.dir)) {
|
|
1783
|
+
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
1784
|
+
}
|
|
1785
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
1786
|
+
throw new Error(
|
|
1787
|
+
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
const validations = {
|
|
1791
|
+
rate: {
|
|
1792
|
+
field: "rate",
|
|
1793
|
+
type: "string",
|
|
1794
|
+
pattern: /^\d+$/,
|
|
1795
|
+
error: "numeric string"
|
|
1796
|
+
},
|
|
1797
|
+
amount: {
|
|
1798
|
+
field: "assets",
|
|
1799
|
+
type: "string",
|
|
1800
|
+
pattern: /^\d+$/,
|
|
1801
|
+
error: "numeric string"
|
|
1802
|
+
},
|
|
1803
|
+
maturity: {
|
|
1804
|
+
field: "maturity",
|
|
1805
|
+
type: "number",
|
|
1806
|
+
validator: (val) => val > 0,
|
|
1807
|
+
error: "positive number"
|
|
1808
|
+
},
|
|
1809
|
+
expiry: {
|
|
1810
|
+
field: "expiry",
|
|
1811
|
+
type: "number",
|
|
1812
|
+
validator: (val) => val > 0,
|
|
1813
|
+
error: "positive number"
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
const validation = validations[c.sort];
|
|
1817
|
+
if (!validation) {
|
|
1818
|
+
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
1819
|
+
}
|
|
1820
|
+
const fieldValue = c[validation.field];
|
|
1821
|
+
if (!fieldValue) {
|
|
1822
|
+
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
1823
|
+
}
|
|
1824
|
+
if (typeof fieldValue !== validation.type) {
|
|
1825
|
+
throw new Error(
|
|
1826
|
+
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
1830
|
+
throw new Error(
|
|
1831
|
+
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
if (validation.validator && !validation.validator(fieldValue)) {
|
|
1835
|
+
throw new Error(
|
|
1836
|
+
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
if (c.page !== void 0) {
|
|
1840
|
+
if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
|
|
1841
|
+
throw new Error("Invalid page: must be a positive integer");
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
return true;
|
|
1845
|
+
}
|
|
1846
|
+
function encode2(c) {
|
|
1847
|
+
return Base64.encodeURL(JSON.stringify(c));
|
|
1848
|
+
}
|
|
1849
|
+
function decode2(token) {
|
|
1850
|
+
if (!token) return null;
|
|
1851
|
+
const decoded = JSON.parse(Base64.decode(token));
|
|
1852
|
+
validate(decoded);
|
|
1853
|
+
return decoded;
|
|
1854
|
+
}
|
|
1729
1855
|
|
|
1730
|
-
// src/
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1856
|
+
// src/api/Schema/requests.ts
|
|
1857
|
+
var MAX_LIMIT = 100;
|
|
1858
|
+
var DEFAULT_LIMIT = 20;
|
|
1859
|
+
var PaginationQueryParams = z6.object({
|
|
1860
|
+
cursor: z6.string().optional().refine(
|
|
1861
|
+
(val) => {
|
|
1862
|
+
if (!val) return true;
|
|
1863
|
+
try {
|
|
1864
|
+
const decoded = Cursor_exports.decode(val);
|
|
1865
|
+
return decoded !== null;
|
|
1866
|
+
} catch (_error) {
|
|
1867
|
+
return false;
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
|
|
1872
|
+
}
|
|
1873
|
+
).meta({
|
|
1874
|
+
description: "Pagination cursor in base64url-encoded format",
|
|
1875
|
+
example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
|
|
1876
|
+
}),
|
|
1877
|
+
limit: z6.string().regex(/^[1-9]\d*$/, {
|
|
1878
|
+
message: "Limit must be a positive integer"
|
|
1879
|
+
}).transform((val) => Number.parseInt(val, 10)).pipe(
|
|
1880
|
+
z6.number().max(MAX_LIMIT, {
|
|
1881
|
+
message: `Limit cannot exceed ${MAX_LIMIT}`
|
|
1882
|
+
})
|
|
1883
|
+
).optional().default(DEFAULT_LIMIT).meta({
|
|
1884
|
+
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
|
|
1885
|
+
example: 10
|
|
1886
|
+
})
|
|
1887
|
+
});
|
|
1888
|
+
var GetOffersQueryParams = z6.object({
|
|
1889
|
+
...PaginationQueryParams.shape,
|
|
1890
|
+
side: z6.enum(["buy", "sell"]).meta({
|
|
1891
|
+
description: "Side of the offer.",
|
|
1892
|
+
example: "buy"
|
|
1893
|
+
}),
|
|
1894
|
+
obligation_id: z6.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({
|
|
1895
|
+
description: "Offers obligation id",
|
|
1896
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
1897
|
+
})
|
|
1898
|
+
});
|
|
1899
|
+
var GetObligationsQueryParams = z6.object({
|
|
1900
|
+
...PaginationQueryParams.shape,
|
|
1901
|
+
cursor: z6.string().optional().meta({
|
|
1902
|
+
description: "Obligation id cursor",
|
|
1903
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
1904
|
+
})
|
|
1905
|
+
});
|
|
1906
|
+
var schemas = {
|
|
1907
|
+
get_offers: GetOffersQueryParams,
|
|
1908
|
+
get_obligations: GetObligationsQueryParams
|
|
1909
|
+
};
|
|
1910
|
+
function safeParse(action, query, error2) {
|
|
1911
|
+
return schemas[action].safeParse(query, {
|
|
1912
|
+
error: error2
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// src/api/Schema/openapi.ts
|
|
1917
|
+
var timestampExample = "2024-01-01T12:00:00.000Z";
|
|
1918
|
+
var cursorExample = "eyJvZmZzZXQiOjEwMH0";
|
|
1919
|
+
function makeSuccessResponse(parameters) {
|
|
1920
|
+
const { dataSchema, dataDescription, dataExample, cursor } = parameters;
|
|
1921
|
+
const withDataMeta = dataDescription ? dataSchema.meta({ description: dataDescription }) : dataSchema;
|
|
1922
|
+
return z.object({
|
|
1923
|
+
status: z.literal("success"),
|
|
1924
|
+
cursor: z.string().nullable(),
|
|
1925
|
+
data: z.any(),
|
|
1926
|
+
meta: z.object({
|
|
1927
|
+
timestamp: z.string()
|
|
1928
|
+
})
|
|
1929
|
+
}).extend({
|
|
1930
|
+
data: withDataMeta
|
|
1931
|
+
}).meta({
|
|
1932
|
+
example: {
|
|
1933
|
+
status: "success",
|
|
1934
|
+
cursor,
|
|
1935
|
+
data: dataExample,
|
|
1936
|
+
meta: { timestamp: timestampExample }
|
|
1937
|
+
}
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
var OffersSuccessResponseSchema = makeSuccessResponse({
|
|
1941
|
+
dataSchema: z.array(z.any()),
|
|
1942
|
+
dataDescription: "Offers matching the provided filters.",
|
|
1943
|
+
dataExample: [toSnakeCase(Offer_exports.random())],
|
|
1944
|
+
cursor: cursorExample
|
|
1945
|
+
});
|
|
1946
|
+
var ObligationsSuccessResponseSchema = makeSuccessResponse({
|
|
1947
|
+
dataSchema: z.array(z.any()),
|
|
1948
|
+
dataDescription: "Obligations known to the router.",
|
|
1949
|
+
dataExample: [toSnakeCase(Obligation_exports.random())],
|
|
1950
|
+
cursor: cursorExample
|
|
1951
|
+
});
|
|
1952
|
+
var RouterStatusSuccessResponseSchema = makeSuccessResponse({
|
|
1953
|
+
dataSchema: RouterStatusResponse,
|
|
1954
|
+
dataDescription: "Aggregated router status.",
|
|
1955
|
+
dataExample: { status: "live" },
|
|
1956
|
+
cursor: null
|
|
1957
|
+
});
|
|
1958
|
+
var CollectorsHealthSuccessResponseSchema = makeSuccessResponse({
|
|
1959
|
+
dataSchema: CollectorsHealthResponse,
|
|
1960
|
+
dataDescription: "Collectors health details and sync status.",
|
|
1961
|
+
dataExample: [
|
|
1962
|
+
{
|
|
1963
|
+
name: "mempool_offers",
|
|
1964
|
+
chain_id: "1",
|
|
1965
|
+
block_number: 21345678,
|
|
1966
|
+
updated_at: "2024-01-01T12:00:00.000Z",
|
|
1967
|
+
lag: 0,
|
|
1968
|
+
status: "live"
|
|
1969
|
+
}
|
|
1970
|
+
],
|
|
1971
|
+
cursor: null
|
|
1972
|
+
});
|
|
1973
|
+
var ChainsHealthSuccessResponseSchema = makeSuccessResponse({
|
|
1974
|
+
dataSchema: ChainsHealthResponse,
|
|
1975
|
+
dataDescription: "Latest processed block per chain.",
|
|
1976
|
+
dataExample: [
|
|
1977
|
+
{
|
|
1978
|
+
chain_id: "1",
|
|
1979
|
+
block_number: 21345678,
|
|
1980
|
+
updated_at: "2024-01-01T12:00:00.000Z"
|
|
1981
|
+
}
|
|
1982
|
+
],
|
|
1983
|
+
cursor: null
|
|
1984
|
+
});
|
|
1985
|
+
var errorResponseSchema = z.object({
|
|
1986
|
+
status: z.literal("error"),
|
|
1987
|
+
error: z.object({
|
|
1988
|
+
code: z.string(),
|
|
1989
|
+
message: z.string(),
|
|
1990
|
+
details: z.any().optional()
|
|
1991
|
+
}),
|
|
1992
|
+
meta: z.object({
|
|
1993
|
+
timestamp: z.string()
|
|
1994
|
+
})
|
|
1995
|
+
}).meta({
|
|
1996
|
+
description: "Error response wrapper.",
|
|
1997
|
+
example: {
|
|
1998
|
+
status: "error",
|
|
1999
|
+
error: {
|
|
2000
|
+
code: "VALIDATION_ERROR",
|
|
2001
|
+
message: "Invalid cursor format. Must be a valid base64url-encoded cursor object",
|
|
2002
|
+
details: [
|
|
2003
|
+
{
|
|
2004
|
+
field: "cursor",
|
|
2005
|
+
issue: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
|
|
2006
|
+
}
|
|
2007
|
+
]
|
|
2008
|
+
},
|
|
2009
|
+
meta: {
|
|
2010
|
+
timestamp: timestampExample
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
var paths = {
|
|
2015
|
+
"/v1/offers": {
|
|
2016
|
+
get: {
|
|
2017
|
+
summary: "Offers",
|
|
2018
|
+
description: "Find offers that match specific criteria",
|
|
2019
|
+
tags: ["Offers"],
|
|
2020
|
+
requestParams: {
|
|
2021
|
+
query: GetOffersQueryParams
|
|
2022
|
+
},
|
|
2023
|
+
responses: {
|
|
2024
|
+
200: {
|
|
2025
|
+
description: "Success",
|
|
2026
|
+
content: {
|
|
2027
|
+
"application/json": {
|
|
2028
|
+
schema: OffersSuccessResponseSchema
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
},
|
|
2032
|
+
400: {
|
|
2033
|
+
description: "Bad Request",
|
|
2034
|
+
content: {
|
|
2035
|
+
"application/json": {
|
|
2036
|
+
schema: errorResponseSchema
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
},
|
|
2043
|
+
"/v1/obligations": {
|
|
2044
|
+
get: {
|
|
2045
|
+
summary: "Obligations",
|
|
2046
|
+
description: "List obligations with pagination support",
|
|
2047
|
+
tags: ["Obligations"],
|
|
2048
|
+
requestParams: {
|
|
2049
|
+
query: GetObligationsQueryParams
|
|
2050
|
+
},
|
|
2051
|
+
responses: {
|
|
2052
|
+
200: {
|
|
2053
|
+
description: "Success",
|
|
2054
|
+
content: {
|
|
2055
|
+
"application/json": {
|
|
2056
|
+
schema: ObligationsSuccessResponseSchema
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
},
|
|
2060
|
+
400: {
|
|
2061
|
+
description: "Bad Request",
|
|
2062
|
+
content: {
|
|
2063
|
+
"application/json": {
|
|
2064
|
+
schema: errorResponseSchema
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
},
|
|
2071
|
+
"/v1/health": {
|
|
2072
|
+
get: {
|
|
2073
|
+
summary: "Router status",
|
|
2074
|
+
description: "Retrieve the aggregated status of the router.",
|
|
2075
|
+
tags: ["Health"],
|
|
2076
|
+
responses: {
|
|
2077
|
+
200: {
|
|
2078
|
+
description: "Success",
|
|
2079
|
+
content: {
|
|
2080
|
+
"application/json": {
|
|
2081
|
+
schema: RouterStatusSuccessResponseSchema
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
},
|
|
2088
|
+
"/v1/health/collectors": {
|
|
2089
|
+
get: {
|
|
2090
|
+
summary: "Collectors health",
|
|
2091
|
+
description: "Retrieve the block numbers processed by collectors and their sync status.",
|
|
2092
|
+
tags: ["Health"],
|
|
2093
|
+
responses: {
|
|
2094
|
+
200: {
|
|
2095
|
+
description: "Success",
|
|
2096
|
+
content: {
|
|
2097
|
+
"application/json": {
|
|
2098
|
+
schema: CollectorsHealthSuccessResponseSchema
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
},
|
|
2105
|
+
"/v1/health/chains": {
|
|
2106
|
+
get: {
|
|
2107
|
+
summary: "Chains health",
|
|
2108
|
+
description: "Retrieve the latest block processed for each chain.",
|
|
2109
|
+
tags: ["Health"],
|
|
2110
|
+
responses: {
|
|
2111
|
+
200: {
|
|
2112
|
+
description: "Success",
|
|
2113
|
+
content: {
|
|
2114
|
+
"application/json": {
|
|
2115
|
+
schema: ChainsHealthSuccessResponseSchema
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
var OpenApi = createDocument({
|
|
2124
|
+
openapi: "3.1.0",
|
|
2125
|
+
info: {
|
|
2126
|
+
title: "Router API",
|
|
2127
|
+
version: "1.0.0",
|
|
2128
|
+
description: "API for the Morpho Router"
|
|
2129
|
+
},
|
|
2130
|
+
tags: [
|
|
2131
|
+
{
|
|
2132
|
+
name: "Offers"
|
|
2133
|
+
},
|
|
2134
|
+
{
|
|
2135
|
+
name: "Obligations"
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
name: "Health"
|
|
2139
|
+
}
|
|
2140
|
+
],
|
|
2141
|
+
servers: [
|
|
2142
|
+
{
|
|
2143
|
+
url: "https://router.morpho.dev",
|
|
2144
|
+
description: "Production server"
|
|
2145
|
+
},
|
|
2146
|
+
{
|
|
2147
|
+
url: "http://localhost:7891",
|
|
2148
|
+
description: "Local development server"
|
|
2149
|
+
}
|
|
2150
|
+
],
|
|
2151
|
+
paths
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
// src/api/Controllers/getDocs.ts
|
|
2155
|
+
function getSwaggerJson() {
|
|
2156
|
+
return OpenApi;
|
|
2157
|
+
}
|
|
2158
|
+
function getDocsHtml() {
|
|
2159
|
+
const html = `<!DOCTYPE html>
|
|
2160
|
+
<html>
|
|
2161
|
+
<head>
|
|
2162
|
+
<meta charset="UTF-8">
|
|
2163
|
+
<title>Router API Docs (Scalar)</title>
|
|
2164
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
2165
|
+
<style>
|
|
2166
|
+
html, body { margin: 0; height: 100%; }
|
|
2167
|
+
api-reference { height: 100%; width: 100%; }
|
|
2168
|
+
</style>
|
|
2169
|
+
</head>
|
|
2170
|
+
<body>
|
|
2171
|
+
<div id="api-container" style="height:100%;width:100%;"></div>
|
|
2172
|
+
<script>
|
|
2173
|
+
window.addEventListener('load', function () {
|
|
2174
|
+
const spec = ${JSON.stringify(OpenApi)};
|
|
2175
|
+
Scalar.createApiReference('#api-container', { spec: { content: spec } });
|
|
2176
|
+
});
|
|
2177
|
+
</script>
|
|
2178
|
+
</body>
|
|
2179
|
+
</html>`;
|
|
2180
|
+
return html;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// src/collectors/index.ts
|
|
2184
|
+
var collectors_exports = {};
|
|
2185
|
+
__export(collectors_exports, {
|
|
2186
|
+
batch: () => batch2,
|
|
2187
|
+
create: () => create2,
|
|
2188
|
+
createBuilder: () => createBuilder,
|
|
2189
|
+
fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
|
|
2190
|
+
fetchCollateralAndDebt: () => fetchCollateralAndDebt,
|
|
2191
|
+
fetchOraclePrices: () => fetchOraclePrices,
|
|
2192
|
+
fetchUserVaultMarketLiquidity: () => fetchUserVaultMarketLiquidity,
|
|
2193
|
+
morpho: () => morpho,
|
|
2194
|
+
names: () => names,
|
|
2195
|
+
run: () => run,
|
|
2196
|
+
single: () => single,
|
|
2197
|
+
start: () => start
|
|
2198
|
+
});
|
|
2199
|
+
|
|
2200
|
+
// src/services/RouterAdmin.ts
|
|
2201
|
+
function create(parameters) {
|
|
2202
|
+
const collector = "admin";
|
|
2203
|
+
const {
|
|
2204
|
+
client,
|
|
2205
|
+
chain,
|
|
1736
2206
|
withTransaction,
|
|
1737
2207
|
options: { maxBatchSize = 25, maxBlockNumber } = {}
|
|
1738
2208
|
} = parameters;
|
|
@@ -1991,20 +2461,31 @@ function create2({
|
|
|
1991
2461
|
});
|
|
1992
2462
|
return poll(
|
|
1993
2463
|
async () => {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2464
|
+
try {
|
|
2465
|
+
let { blockNumber: lastBlockNumber, epoch } = await collectorStore.getBlockNumber({
|
|
2466
|
+
collectorName: name,
|
|
2467
|
+
chainId: chain.id
|
|
2468
|
+
});
|
|
2469
|
+
await admin.syncBlock();
|
|
2470
|
+
lastBlockNumber = await collect({
|
|
2471
|
+
chain,
|
|
2472
|
+
client,
|
|
2473
|
+
collector: name,
|
|
2474
|
+
epoch,
|
|
2475
|
+
lastBlockNumber,
|
|
2476
|
+
withTransaction
|
|
2477
|
+
});
|
|
2478
|
+
emit(lastBlockNumber);
|
|
2479
|
+
} catch (err) {
|
|
2480
|
+
const isError = err instanceof Error;
|
|
2481
|
+
logger.error({
|
|
2482
|
+
msg: "Collector error",
|
|
2483
|
+
collector,
|
|
2484
|
+
chain_id: chain.id,
|
|
2485
|
+
error: isError ? err.message : String(err),
|
|
2486
|
+
stack: isError ? err.stack : void 0
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2008
2489
|
},
|
|
2009
2490
|
{ interval: options.interval }
|
|
2010
2491
|
);
|
|
@@ -2024,7 +2505,7 @@ function start(collector) {
|
|
|
2024
2505
|
}
|
|
2025
2506
|
var DEFAULT_BATCH_SIZE2 = 100;
|
|
2026
2507
|
var DEFAULT_BLOCK_WINDOW2 = 100;
|
|
2027
|
-
function
|
|
2508
|
+
function from7(parameters) {
|
|
2028
2509
|
const config = {
|
|
2029
2510
|
client: parameters.client,
|
|
2030
2511
|
mempoolAddress: parameters.mempoolAddress,
|
|
@@ -2166,7 +2647,7 @@ var ChainIdMismatchError = class extends BaseError {
|
|
|
2166
2647
|
|
|
2167
2648
|
// src/mempool/MempoolClient.ts
|
|
2168
2649
|
function connect(parameters) {
|
|
2169
|
-
return
|
|
2650
|
+
return from7(parameters);
|
|
2170
2651
|
}
|
|
2171
2652
|
|
|
2172
2653
|
// src/stores/CollectorStore.ts
|
|
@@ -3952,385 +4433,150 @@ function handleAPIError(error2) {
|
|
|
3952
4433
|
code: error2.code,
|
|
3953
4434
|
message: error2.message,
|
|
3954
4435
|
...error2.details && typeof error2.details === "object" ? { details: error2.details } : {}
|
|
3955
|
-
},
|
|
3956
|
-
meta: {
|
|
3957
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
};
|
|
3961
|
-
}
|
|
3962
|
-
var APIError = class extends Error {
|
|
3963
|
-
constructor(statusCode, message, code, details) {
|
|
3964
|
-
super(message);
|
|
3965
|
-
this.statusCode = statusCode;
|
|
3966
|
-
this.code = code;
|
|
3967
|
-
this.details = details;
|
|
3968
|
-
this.name = "APIError";
|
|
3969
|
-
}
|
|
3970
|
-
};
|
|
3971
|
-
var ValidationError = class extends APIError {
|
|
3972
|
-
constructor(message, details) {
|
|
3973
|
-
super(400 /* BAD_REQUEST */, message, "VALIDATION_ERROR", details);
|
|
3974
|
-
}
|
|
3975
|
-
};
|
|
3976
|
-
var InternalServerError = class extends APIError {
|
|
3977
|
-
constructor(message = "Internal server error") {
|
|
3978
|
-
super(500 /* INTERNAL_SERVER_ERROR */, message, "INTERNAL_SERVER_ERROR");
|
|
3979
|
-
}
|
|
3980
|
-
};
|
|
3981
|
-
var BadRequestError = class extends APIError {
|
|
3982
|
-
constructor(message = "Invalid JSON format", details) {
|
|
3983
|
-
super(400 /* BAD_REQUEST */, message, "BAD_REQUEST", details);
|
|
3984
|
-
}
|
|
3985
|
-
};
|
|
3986
|
-
function handleZodError(error2) {
|
|
3987
|
-
const formattedErrors = error2.issues.map((err) => {
|
|
3988
|
-
const field = err.path.join(".");
|
|
3989
|
-
let issue = err.message;
|
|
3990
|
-
if (err.code === "invalid_type") {
|
|
3991
|
-
if (err.message.includes("received undefined")) {
|
|
3992
|
-
issue = `${field} is required`;
|
|
3993
|
-
} else {
|
|
3994
|
-
issue = err.message;
|
|
3995
|
-
}
|
|
3996
|
-
} else if (err.code === "invalid_format" && err.format === "regex") {
|
|
3997
|
-
issue = err.message;
|
|
3998
|
-
} else if (err.code === "invalid_format") {
|
|
3999
|
-
issue = `${field} has an invalid format`;
|
|
4000
|
-
}
|
|
4001
|
-
return {
|
|
4002
|
-
field,
|
|
4003
|
-
issue
|
|
4004
|
-
};
|
|
4005
|
-
});
|
|
4006
|
-
return new ValidationError("Validation failed", formattedErrors);
|
|
4007
|
-
}
|
|
4008
|
-
|
|
4009
|
-
// src/api/Api/Controllers/getHealth.ts
|
|
4010
|
-
async function getHealth(healthService) {
|
|
4011
|
-
const logger = Logger_exports.getLogger();
|
|
4012
|
-
try {
|
|
4013
|
-
const status = await healthService.getStatus();
|
|
4014
|
-
return success({ data: toSnakeCase({ status }) });
|
|
4015
|
-
} catch (err) {
|
|
4016
|
-
logger.error({
|
|
4017
|
-
err,
|
|
4018
|
-
msg: "Error getting health status",
|
|
4019
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
4020
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
4021
|
-
});
|
|
4022
|
-
return error(err);
|
|
4023
|
-
}
|
|
4024
|
-
}
|
|
4025
|
-
async function getHealthChains(healthService) {
|
|
4026
|
-
const logger = Logger_exports.getLogger();
|
|
4027
|
-
try {
|
|
4028
|
-
const chains3 = await healthService.getChains();
|
|
4029
|
-
return success({
|
|
4030
|
-
data: chains3.map(
|
|
4031
|
-
({ chainId, blockNumber, updatedAt }) => toSnakeCase({
|
|
4032
|
-
chainId,
|
|
4033
|
-
blockNumber,
|
|
4034
|
-
updatedAt
|
|
4035
|
-
})
|
|
4036
|
-
)
|
|
4037
|
-
});
|
|
4038
|
-
} catch (err) {
|
|
4039
|
-
logger.error({
|
|
4040
|
-
err,
|
|
4041
|
-
msg: "Error getting health status for chains",
|
|
4042
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
4043
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
4044
|
-
});
|
|
4045
|
-
return error(err);
|
|
4046
|
-
}
|
|
4047
|
-
}
|
|
4048
|
-
async function getHealthCollectors(healthService) {
|
|
4049
|
-
const logger = Logger_exports.getLogger();
|
|
4050
|
-
try {
|
|
4051
|
-
const collectors2 = await healthService.getCollectors();
|
|
4052
|
-
return success({
|
|
4053
|
-
data: collectors2.map(
|
|
4054
|
-
({ name, chainId, blockNumber, updatedAt, lag, status }) => toSnakeCase({
|
|
4055
|
-
name,
|
|
4056
|
-
chainId,
|
|
4057
|
-
blockNumber,
|
|
4058
|
-
updatedAt,
|
|
4059
|
-
lag,
|
|
4060
|
-
status
|
|
4061
|
-
})
|
|
4062
|
-
)
|
|
4063
|
-
});
|
|
4064
|
-
} catch (err) {
|
|
4065
|
-
logger.error({
|
|
4066
|
-
err,
|
|
4067
|
-
msg: "Error getting health status for collectors",
|
|
4068
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
4069
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
4070
|
-
});
|
|
4071
|
-
return error(err);
|
|
4072
|
-
}
|
|
4073
|
-
}
|
|
4074
|
-
var CollectorHealth = z.object({
|
|
4075
|
-
name: z.string(),
|
|
4076
|
-
chain_id: z.number(),
|
|
4077
|
-
block_number: z.number().nullable(),
|
|
4078
|
-
updated_at: z.string().nullable(),
|
|
4079
|
-
lag: z.number().nullable(),
|
|
4080
|
-
status: z.enum(["live", "lagging", "unknown"])
|
|
4081
|
-
});
|
|
4082
|
-
var CollectorsHealthResponse = z.object({
|
|
4083
|
-
collectors: z.array(CollectorHealth)
|
|
4084
|
-
});
|
|
4085
|
-
var ChainHealth = z.object({
|
|
4086
|
-
chain_id: z.number(),
|
|
4087
|
-
block_number: z.number(),
|
|
4088
|
-
updated_at: z.string()
|
|
4089
|
-
});
|
|
4090
|
-
var ChainsHealthResponse = z.object({
|
|
4091
|
-
chains: z.array(ChainHealth)
|
|
4092
|
-
});
|
|
4093
|
-
var RouterStatusResponse = z.object({
|
|
4094
|
-
status: z.enum(["live", "syncing"])
|
|
4095
|
-
});
|
|
4096
|
-
|
|
4097
|
-
// src/api/Api/Schema/ObligationResponse.ts
|
|
4098
|
-
var ObligationResponse_exports = {};
|
|
4099
|
-
__export(ObligationResponse_exports, {
|
|
4100
|
-
from: () => from7
|
|
4101
|
-
});
|
|
4102
|
-
function from7(obligation) {
|
|
4103
|
-
return toSnakeCase({ id: Obligation_exports.id(obligation), ...obligation });
|
|
4104
|
-
}
|
|
4105
|
-
|
|
4106
|
-
// src/api/Api/Schema/OfferResponse.ts
|
|
4107
|
-
var OfferResponse_exports = {};
|
|
4108
|
-
__export(OfferResponse_exports, {
|
|
4109
|
-
from: () => from8
|
|
4110
|
-
});
|
|
4111
|
-
function from8(offer) {
|
|
4112
|
-
return toSnakeCase(offer);
|
|
4113
|
-
}
|
|
4114
|
-
var MAX_LIMIT = 100;
|
|
4115
|
-
var DEFAULT_LIMIT = 20;
|
|
4116
|
-
var PaginationQueryParams = z6.object({
|
|
4117
|
-
cursor: z6.string().optional().refine(
|
|
4118
|
-
(val) => {
|
|
4119
|
-
if (!val) return true;
|
|
4120
|
-
try {
|
|
4121
|
-
const decoded = Cursor_exports.decode(val);
|
|
4122
|
-
return decoded !== null;
|
|
4123
|
-
} catch (_error) {
|
|
4124
|
-
return false;
|
|
4125
|
-
}
|
|
4126
|
-
},
|
|
4127
|
-
{
|
|
4128
|
-
message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
|
|
4129
|
-
}
|
|
4130
|
-
).meta({
|
|
4131
|
-
description: "Pagination cursor in base64url-encoded format",
|
|
4132
|
-
example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
|
|
4133
|
-
}),
|
|
4134
|
-
limit: z6.string().regex(/^[1-9]\d*$/, {
|
|
4135
|
-
message: "Limit must be a positive integer"
|
|
4136
|
-
}).transform((val) => Number.parseInt(val, 10)).pipe(
|
|
4137
|
-
z6.number().max(MAX_LIMIT, {
|
|
4138
|
-
message: `Limit cannot exceed ${MAX_LIMIT}`
|
|
4139
|
-
})
|
|
4140
|
-
).optional().default(DEFAULT_LIMIT).meta({
|
|
4141
|
-
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
|
|
4142
|
-
example: 10
|
|
4143
|
-
})
|
|
4144
|
-
});
|
|
4145
|
-
var GetOffersQueryParams = z6.object({
|
|
4146
|
-
...PaginationQueryParams.shape,
|
|
4147
|
-
side: z6.enum(["buy", "sell"]).meta({
|
|
4148
|
-
description: "Side of the offer.",
|
|
4149
|
-
example: "buy"
|
|
4150
|
-
}),
|
|
4151
|
-
obligation_id: z6.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({
|
|
4152
|
-
description: "Offers obligation id",
|
|
4153
|
-
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
4154
|
-
})
|
|
4155
|
-
});
|
|
4156
|
-
var GetObligationsQueryParams = z6.object({
|
|
4157
|
-
...PaginationQueryParams.shape,
|
|
4158
|
-
cursor: z6.string().optional().meta({
|
|
4159
|
-
description: "Obligation id cursor",
|
|
4160
|
-
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
4161
|
-
})
|
|
4162
|
-
});
|
|
4163
|
-
var schemas = {
|
|
4164
|
-
get_offers: GetOffersQueryParams,
|
|
4165
|
-
get_obligations: GetObligationsQueryParams
|
|
4166
|
-
};
|
|
4167
|
-
function safeParse(action, query, error2) {
|
|
4168
|
-
return schemas[action].safeParse(query, {
|
|
4169
|
-
error: error2
|
|
4170
|
-
});
|
|
4171
|
-
}
|
|
4172
|
-
|
|
4173
|
-
// src/api/Api/Schema/openapi.ts
|
|
4174
|
-
var successResponseSchema = z.object({
|
|
4175
|
-
status: z.literal("success"),
|
|
4176
|
-
cursor: z.string().nullable(),
|
|
4177
|
-
data: z.array(z.any()),
|
|
4178
|
-
meta: z.object({
|
|
4179
|
-
timestamp: z.string()
|
|
4180
|
-
})
|
|
4181
|
-
});
|
|
4182
|
-
var errorResponseSchema = z.object({
|
|
4183
|
-
status: z.literal("error"),
|
|
4184
|
-
error: z.object({
|
|
4185
|
-
code: z.string(),
|
|
4186
|
-
message: z.string(),
|
|
4187
|
-
details: z.any().optional()
|
|
4188
|
-
}),
|
|
4189
|
-
meta: z.object({
|
|
4190
|
-
timestamp: z.string()
|
|
4191
|
-
})
|
|
4192
|
-
});
|
|
4193
|
-
var paths = {
|
|
4194
|
-
"/v1/offers": {
|
|
4195
|
-
get: {
|
|
4196
|
-
summary: "Offers",
|
|
4197
|
-
description: "Find offers that match specific criteria",
|
|
4198
|
-
tags: ["Offers"],
|
|
4199
|
-
requestParams: {
|
|
4200
|
-
query: GetOffersQueryParams
|
|
4201
|
-
},
|
|
4202
|
-
responses: {
|
|
4203
|
-
200: {
|
|
4204
|
-
description: "Success",
|
|
4205
|
-
content: {
|
|
4206
|
-
"application/json": {
|
|
4207
|
-
schema: successResponseSchema
|
|
4208
|
-
}
|
|
4209
|
-
}
|
|
4210
|
-
},
|
|
4211
|
-
400: {
|
|
4212
|
-
description: "Bad Request",
|
|
4213
|
-
content: {
|
|
4214
|
-
"application/json": {
|
|
4215
|
-
schema: errorResponseSchema
|
|
4216
|
-
}
|
|
4217
|
-
}
|
|
4218
|
-
}
|
|
4219
|
-
}
|
|
4220
|
-
}
|
|
4221
|
-
},
|
|
4222
|
-
"/v1/obligations": {
|
|
4223
|
-
get: {
|
|
4224
|
-
summary: "Obligations",
|
|
4225
|
-
description: "List obligations with pagination support",
|
|
4226
|
-
tags: ["Obligations"],
|
|
4227
|
-
requestParams: {
|
|
4228
|
-
query: GetObligationsQueryParams
|
|
4229
|
-
},
|
|
4230
|
-
responses: {
|
|
4231
|
-
200: {
|
|
4232
|
-
description: "Success",
|
|
4233
|
-
content: {
|
|
4234
|
-
"application/json": {
|
|
4235
|
-
schema: successResponseSchema
|
|
4236
|
-
}
|
|
4237
|
-
}
|
|
4238
|
-
},
|
|
4239
|
-
400: {
|
|
4240
|
-
description: "Bad Request",
|
|
4241
|
-
content: {
|
|
4242
|
-
"application/json": {
|
|
4243
|
-
schema: errorResponseSchema
|
|
4244
|
-
}
|
|
4245
|
-
}
|
|
4246
|
-
}
|
|
4247
|
-
}
|
|
4248
|
-
}
|
|
4249
|
-
},
|
|
4250
|
-
"/v1/health": {
|
|
4251
|
-
get: {
|
|
4252
|
-
summary: "Router status",
|
|
4253
|
-
description: "Retrieve the aggregated status of the router.",
|
|
4254
|
-
tags: ["Health"],
|
|
4255
|
-
responses: {
|
|
4256
|
-
200: {
|
|
4257
|
-
description: "Success",
|
|
4258
|
-
content: {
|
|
4259
|
-
"application/json": {
|
|
4260
|
-
schema: RouterStatusResponse
|
|
4261
|
-
}
|
|
4262
|
-
}
|
|
4263
|
-
}
|
|
4264
|
-
}
|
|
4265
|
-
}
|
|
4266
|
-
},
|
|
4267
|
-
"/v1/health/collectors": {
|
|
4268
|
-
get: {
|
|
4269
|
-
summary: "Collectors health",
|
|
4270
|
-
description: "Retrieve the block numbers processed by collectors and their sync status.",
|
|
4271
|
-
tags: ["Health"],
|
|
4272
|
-
responses: {
|
|
4273
|
-
200: {
|
|
4274
|
-
description: "Success",
|
|
4275
|
-
content: {
|
|
4276
|
-
"application/json": {
|
|
4277
|
-
schema: CollectorsHealthResponse
|
|
4278
|
-
}
|
|
4279
|
-
}
|
|
4280
|
-
}
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
},
|
|
4284
|
-
"/v1/health/chains": {
|
|
4285
|
-
get: {
|
|
4286
|
-
summary: "Chains health",
|
|
4287
|
-
description: "Retrieve the latest block processed for each chain.",
|
|
4288
|
-
tags: ["Health"],
|
|
4289
|
-
responses: {
|
|
4290
|
-
200: {
|
|
4291
|
-
description: "Success",
|
|
4292
|
-
content: {
|
|
4293
|
-
"application/json": {
|
|
4294
|
-
schema: ChainsHealthResponse
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4436
|
+
},
|
|
4437
|
+
meta: {
|
|
4438
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4298
4439
|
}
|
|
4299
4440
|
}
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
var APIError = class extends Error {
|
|
4444
|
+
constructor(statusCode, message, code, details) {
|
|
4445
|
+
super(message);
|
|
4446
|
+
this.statusCode = statusCode;
|
|
4447
|
+
this.code = code;
|
|
4448
|
+
this.details = details;
|
|
4449
|
+
this.name = "APIError";
|
|
4300
4450
|
}
|
|
4301
4451
|
};
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
{
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4452
|
+
var ValidationError = class extends APIError {
|
|
4453
|
+
constructor(message, details) {
|
|
4454
|
+
super(400 /* BAD_REQUEST */, message, "VALIDATION_ERROR", details);
|
|
4455
|
+
}
|
|
4456
|
+
};
|
|
4457
|
+
var InternalServerError = class extends APIError {
|
|
4458
|
+
constructor(message = "Internal server error") {
|
|
4459
|
+
super(500 /* INTERNAL_SERVER_ERROR */, message, "INTERNAL_SERVER_ERROR");
|
|
4460
|
+
}
|
|
4461
|
+
};
|
|
4462
|
+
var BadRequestError = class extends APIError {
|
|
4463
|
+
constructor(message = "Invalid JSON format", details) {
|
|
4464
|
+
super(400 /* BAD_REQUEST */, message, "BAD_REQUEST", details);
|
|
4465
|
+
}
|
|
4466
|
+
};
|
|
4467
|
+
function handleZodError(error2) {
|
|
4468
|
+
const formattedErrors = error2.issues.map((err) => {
|
|
4469
|
+
const field = err.path.join(".");
|
|
4470
|
+
let issue = err.message;
|
|
4471
|
+
if (err.code === "invalid_type") {
|
|
4472
|
+
if (err.message.includes("received undefined")) {
|
|
4473
|
+
issue = `${field} is required`;
|
|
4474
|
+
} else {
|
|
4475
|
+
issue = err.message;
|
|
4476
|
+
}
|
|
4477
|
+
} else if (err.code === "invalid_format" && err.format === "regex") {
|
|
4478
|
+
issue = err.message;
|
|
4479
|
+
} else if (err.code === "invalid_format") {
|
|
4480
|
+
issue = `${field} has an invalid format`;
|
|
4328
4481
|
}
|
|
4329
|
-
|
|
4330
|
-
|
|
4482
|
+
return {
|
|
4483
|
+
field,
|
|
4484
|
+
issue
|
|
4485
|
+
};
|
|
4486
|
+
});
|
|
4487
|
+
return new ValidationError("Validation failed", formattedErrors);
|
|
4488
|
+
}
|
|
4489
|
+
|
|
4490
|
+
// src/api/Controllers/getHealth.ts
|
|
4491
|
+
async function getHealth(healthService) {
|
|
4492
|
+
const logger = Logger_exports.getLogger();
|
|
4493
|
+
try {
|
|
4494
|
+
const status = await healthService.getStatus();
|
|
4495
|
+
return success({ data: toSnakeCase({ status }) });
|
|
4496
|
+
} catch (err) {
|
|
4497
|
+
logger.error({
|
|
4498
|
+
err,
|
|
4499
|
+
msg: "Error getting health status",
|
|
4500
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
4501
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
4502
|
+
});
|
|
4503
|
+
return error(err);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
async function getHealthChains(healthService) {
|
|
4507
|
+
const logger = Logger_exports.getLogger();
|
|
4508
|
+
try {
|
|
4509
|
+
const chains3 = await healthService.getChains();
|
|
4510
|
+
return success({
|
|
4511
|
+
data: chains3.map(
|
|
4512
|
+
({ chainId, blockNumber, updatedAt }) => toSnakeCase({
|
|
4513
|
+
chainId,
|
|
4514
|
+
blockNumber,
|
|
4515
|
+
updatedAt
|
|
4516
|
+
})
|
|
4517
|
+
)
|
|
4518
|
+
});
|
|
4519
|
+
} catch (err) {
|
|
4520
|
+
logger.error({
|
|
4521
|
+
err,
|
|
4522
|
+
msg: "Error getting health status for chains",
|
|
4523
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
4524
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
4525
|
+
});
|
|
4526
|
+
return error(err);
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
async function getHealthCollectors(healthService) {
|
|
4530
|
+
const logger = Logger_exports.getLogger();
|
|
4531
|
+
try {
|
|
4532
|
+
const collectors2 = await healthService.getCollectors();
|
|
4533
|
+
return success({
|
|
4534
|
+
data: collectors2.map(
|
|
4535
|
+
({ name, chainId, blockNumber, updatedAt, lag, status }) => toSnakeCase({
|
|
4536
|
+
name,
|
|
4537
|
+
chainId,
|
|
4538
|
+
blockNumber,
|
|
4539
|
+
updatedAt,
|
|
4540
|
+
lag,
|
|
4541
|
+
status
|
|
4542
|
+
})
|
|
4543
|
+
)
|
|
4544
|
+
});
|
|
4545
|
+
} catch (err) {
|
|
4546
|
+
logger.error({
|
|
4547
|
+
err,
|
|
4548
|
+
msg: "Error getting health status for collectors",
|
|
4549
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
4550
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
4551
|
+
});
|
|
4552
|
+
return error(err);
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
// src/api/Schema/ObligationResponse.ts
|
|
4557
|
+
var ObligationResponse_exports = {};
|
|
4558
|
+
__export(ObligationResponse_exports, {
|
|
4559
|
+
from: () => from8
|
|
4560
|
+
});
|
|
4561
|
+
function from8(obligation, quote) {
|
|
4562
|
+
return toSnakeCase({
|
|
4563
|
+
id: quote.obligationId,
|
|
4564
|
+
...obligation,
|
|
4565
|
+
ask: quote.ask,
|
|
4566
|
+
bid: quote.bid
|
|
4567
|
+
});
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4570
|
+
// src/api/Schema/OfferResponse.ts
|
|
4571
|
+
var OfferResponse_exports = {};
|
|
4572
|
+
__export(OfferResponse_exports, {
|
|
4573
|
+
from: () => from9
|
|
4331
4574
|
});
|
|
4575
|
+
function from9(offer) {
|
|
4576
|
+
return toSnakeCase(offer);
|
|
4577
|
+
}
|
|
4332
4578
|
|
|
4333
|
-
// src/api/
|
|
4579
|
+
// src/api/Controllers/getObligations.ts
|
|
4334
4580
|
async function getObligations(queryParameters, store) {
|
|
4335
4581
|
const logger = Logger_exports.getLogger();
|
|
4336
4582
|
const result = safeParse("get_obligations", queryParameters, (issue) => issue.message);
|
|
@@ -4341,8 +4587,20 @@ async function getObligations(queryParameters, store) {
|
|
|
4341
4587
|
cursor: query.cursor,
|
|
4342
4588
|
limit: query.limit
|
|
4343
4589
|
});
|
|
4590
|
+
const quotes = await store.getQuotes({
|
|
4591
|
+
obligationIds: obligations2.map((o) => Obligation_exports.id(o))
|
|
4592
|
+
});
|
|
4344
4593
|
return success({
|
|
4345
|
-
data: obligations2.map(
|
|
4594
|
+
data: obligations2.map(
|
|
4595
|
+
(o) => ObligationResponse_exports.from(
|
|
4596
|
+
o,
|
|
4597
|
+
quotes.find((q) => q.obligationId === Obligation_exports.id(o)) ?? {
|
|
4598
|
+
obligationId: Obligation_exports.id(o),
|
|
4599
|
+
ask: { rate: 0n },
|
|
4600
|
+
bid: { rate: 0n }
|
|
4601
|
+
}
|
|
4602
|
+
)
|
|
4603
|
+
),
|
|
4346
4604
|
cursor: nextCursor ?? null
|
|
4347
4605
|
});
|
|
4348
4606
|
} catch (err) {
|
|
@@ -4356,7 +4614,7 @@ async function getObligations(queryParameters, store) {
|
|
|
4356
4614
|
}
|
|
4357
4615
|
}
|
|
4358
4616
|
|
|
4359
|
-
// src/api/
|
|
4617
|
+
// src/api/Controllers/getOffers.ts
|
|
4360
4618
|
async function getOffers(queryParameters, store) {
|
|
4361
4619
|
const logger = Logger_exports.getLogger();
|
|
4362
4620
|
const result = safeParse("get_offers", queryParameters, (issue) => issue.message);
|
|
@@ -4384,7 +4642,7 @@ async function getOffers(queryParameters, store) {
|
|
|
4384
4642
|
}
|
|
4385
4643
|
}
|
|
4386
4644
|
|
|
4387
|
-
// src/api/Api
|
|
4645
|
+
// src/api/Api.ts
|
|
4388
4646
|
function create5(config) {
|
|
4389
4647
|
return {
|
|
4390
4648
|
serve: ({ port }) => {
|
|
@@ -4416,6 +4674,13 @@ function serve2(parameters) {
|
|
|
4416
4674
|
const { statusCode, body } = await getHealthChains(healthService);
|
|
4417
4675
|
return c.json(body, statusCode);
|
|
4418
4676
|
});
|
|
4677
|
+
app.get(
|
|
4678
|
+
"/docs/swagger.json",
|
|
4679
|
+
(c) => c.text(JSON.stringify(getSwaggerJson()), 200, {
|
|
4680
|
+
"Content-Type": "application/json; charset=utf-8"
|
|
4681
|
+
})
|
|
4682
|
+
);
|
|
4683
|
+
app.get("/docs", (c) => c.html(getDocsHtml(), 200));
|
|
4419
4684
|
serve$1({
|
|
4420
4685
|
fetch: app.fetch,
|
|
4421
4686
|
port: parameters.port
|
|
@@ -5036,6 +5301,64 @@ function create8(config) {
|
|
|
5036
5301
|
const returnedItems = Array.from(items.values());
|
|
5037
5302
|
const nextCursor = returnedItems.length === limit && returnedItems.length > 0 ? result[result.length - 1].obligationId : null;
|
|
5038
5303
|
return { obligations: returnedItems, nextCursor };
|
|
5304
|
+
},
|
|
5305
|
+
getQuotes: async (parameters) => {
|
|
5306
|
+
const { obligationIds } = parameters;
|
|
5307
|
+
if (obligationIds.length === 0) return [];
|
|
5308
|
+
const now2 = time_exports.now();
|
|
5309
|
+
const sumConsumed = db.select({
|
|
5310
|
+
consumed: sql`COALESCE(SUM(${consumed.consumed}), 0)`.as("consumed")
|
|
5311
|
+
}).from(consumed).where(
|
|
5312
|
+
and(
|
|
5313
|
+
eq(consumed.offering, offers.offering),
|
|
5314
|
+
eq(consumed.nonce, offers.nonce),
|
|
5315
|
+
eq(consumed.chainId, offers.chainId)
|
|
5316
|
+
)
|
|
5317
|
+
).as("sum_consumed");
|
|
5318
|
+
const remaining = sql`(${offers.assets} - COALESCE(${sumConsumed.consumed}, 0))`;
|
|
5319
|
+
const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
|
|
5320
|
+
obligationId: offers.obligationId,
|
|
5321
|
+
rate: offers.rate
|
|
5322
|
+
}).from(offers).leftJoinLateral(sumConsumed, sql`true`).where(
|
|
5323
|
+
and(
|
|
5324
|
+
inArray(offers.obligationId, obligationIds),
|
|
5325
|
+
gte(offers.expiry, now2),
|
|
5326
|
+
gte(offers.maturity, now2),
|
|
5327
|
+
eq(offers.buy, side === "buy"),
|
|
5328
|
+
sql`${remaining} > 0`
|
|
5329
|
+
)
|
|
5330
|
+
).orderBy(
|
|
5331
|
+
offers.obligationId,
|
|
5332
|
+
// lower rates first for buy, higher rates first for sell
|
|
5333
|
+
side === "buy" ? asc(offers.rate) : desc(offers.rate)
|
|
5334
|
+
);
|
|
5335
|
+
const [bestBuys, bestSells] = await Promise.all([
|
|
5336
|
+
query({ side: "buy" }),
|
|
5337
|
+
query({ side: "sell" })
|
|
5338
|
+
]);
|
|
5339
|
+
const quotes = /* @__PURE__ */ new Map();
|
|
5340
|
+
for (const row of bestSells) {
|
|
5341
|
+
quotes.set(row.obligationId, {
|
|
5342
|
+
ask: { rate: row.rate },
|
|
5343
|
+
bid: { rate: 0n }
|
|
5344
|
+
});
|
|
5345
|
+
}
|
|
5346
|
+
for (const row of bestBuys) {
|
|
5347
|
+
const quote = quotes.get(row.obligationId);
|
|
5348
|
+
if (!quote) {
|
|
5349
|
+
quotes.set(row.obligationId, {
|
|
5350
|
+
ask: { rate: 0n },
|
|
5351
|
+
bid: { rate: row.rate }
|
|
5352
|
+
});
|
|
5353
|
+
continue;
|
|
5354
|
+
}
|
|
5355
|
+
quote.bid = { rate: row.rate };
|
|
5356
|
+
}
|
|
5357
|
+
return Array.from(quotes.entries()).map(([id2, quote]) => {
|
|
5358
|
+
return Quote_exports.from({ obligationId: id2, ask: quote.ask, bid: quote.bid });
|
|
5359
|
+
}).sort((a, b) => {
|
|
5360
|
+
return a.obligationId.localeCompare(b.obligationId);
|
|
5361
|
+
});
|
|
5039
5362
|
}
|
|
5040
5363
|
};
|
|
5041
5364
|
}
|
|
@@ -5777,7 +6100,7 @@ function create9(params) {
|
|
|
5777
6100
|
}
|
|
5778
6101
|
|
|
5779
6102
|
// src/services/Services.ts
|
|
5780
|
-
function
|
|
6103
|
+
function from10(config) {
|
|
5781
6104
|
const {
|
|
5782
6105
|
chain,
|
|
5783
6106
|
rpcUrl,
|
|
@@ -5905,7 +6228,7 @@ var RouterCmd = class _RouterCmd extends Command {
|
|
|
5905
6228
|
"RPC URL is required, please set the ROUTER_RPC_URL environment variable or use --rpc-url <url> option."
|
|
5906
6229
|
);
|
|
5907
6230
|
}
|
|
5908
|
-
const routerServices =
|
|
6231
|
+
const routerServices = from10({
|
|
5909
6232
|
chain,
|
|
5910
6233
|
rpcUrl,
|
|
5911
6234
|
dbConfig: {
|
|
@@ -5919,6 +6242,7 @@ var RouterCmd = class _RouterCmd extends Command {
|
|
|
5919
6242
|
command.setOptionValue("rpcUrl", rpcUrl);
|
|
5920
6243
|
command.setOptionValue("routerServices", routerServices);
|
|
5921
6244
|
command.setOptionValue("logger", logger);
|
|
6245
|
+
command.setOptionValue("chain", chain);
|
|
5922
6246
|
return { routerServices };
|
|
5923
6247
|
}
|
|
5924
6248
|
// allows to use the logger defined in the setup function for the action of the command
|
|
@@ -5933,12 +6257,116 @@ var RouterCmd = class _RouterCmd extends Command {
|
|
|
5933
6257
|
}
|
|
5934
6258
|
};
|
|
5935
6259
|
|
|
5936
|
-
// src/cli/commands/
|
|
6260
|
+
// src/cli/commands/mock.ts
|
|
6261
|
+
var mockCmd = new RouterCmd("mock");
|
|
6262
|
+
mockCmd.description("Start Router mock.").addOption(
|
|
6263
|
+
new Option("--seed <n>", "Seed random offers to router").argParser((v) => Number(v)).default(0)
|
|
6264
|
+
).addOption(new Option("--file <path>", "Seed offers from a JSON file")).action(
|
|
6265
|
+
async (opts) => {
|
|
6266
|
+
const {
|
|
6267
|
+
port,
|
|
6268
|
+
routerServices: { liquidityStore, offerStore, routerApi },
|
|
6269
|
+
logger,
|
|
6270
|
+
chain
|
|
6271
|
+
} = opts;
|
|
6272
|
+
const stops = [];
|
|
6273
|
+
if (opts.seed > 0) {
|
|
6274
|
+
const offers2 = Array.from(Array(opts.seed ?? 0).keys()).map((_i) => ({
|
|
6275
|
+
offer: Offer_exports.random({
|
|
6276
|
+
chains: [chain],
|
|
6277
|
+
loanTokens: Array.from(chain.whitelistedAssets.values()),
|
|
6278
|
+
collateralTokens: Array.from(chain.whitelistedAssets.values()),
|
|
6279
|
+
assetsDecimals: {
|
|
6280
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": 6,
|
|
6281
|
+
// USDC
|
|
6282
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F": 18,
|
|
6283
|
+
// DAI
|
|
6284
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": 18,
|
|
6285
|
+
// WETH
|
|
6286
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599": 8,
|
|
6287
|
+
// WBTC
|
|
6288
|
+
"0x4200000000000000000000000000000000000006": 18,
|
|
6289
|
+
// WETH
|
|
6290
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": 6,
|
|
6291
|
+
// USDC
|
|
6292
|
+
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb": 18
|
|
6293
|
+
// DAI
|
|
6294
|
+
}
|
|
6295
|
+
}),
|
|
6296
|
+
status: "valid"
|
|
6297
|
+
}));
|
|
6298
|
+
await offerStore.createMany(offers2);
|
|
6299
|
+
for (const offer of offers2) {
|
|
6300
|
+
await liquidityStore.save({
|
|
6301
|
+
pools: [
|
|
6302
|
+
{
|
|
6303
|
+
id: offer.offer.hash,
|
|
6304
|
+
amount: offer.offer.assets
|
|
6305
|
+
}
|
|
6306
|
+
],
|
|
6307
|
+
offerPools: [
|
|
6308
|
+
{
|
|
6309
|
+
offerHash: offer.offer.hash,
|
|
6310
|
+
poolId: offer.offer.hash,
|
|
6311
|
+
amount: offer.offer.assets
|
|
6312
|
+
}
|
|
6313
|
+
],
|
|
6314
|
+
links: []
|
|
6315
|
+
});
|
|
6316
|
+
}
|
|
6317
|
+
logger.info({ msg: `Offers seeded`, count: opts.seed });
|
|
6318
|
+
}
|
|
6319
|
+
if (opts.file) {
|
|
6320
|
+
const offers2 = await getOffersFromFile(opts.file);
|
|
6321
|
+
await offerStore.createMany(offers2.map((ro) => ({ offer: ro })));
|
|
6322
|
+
for (const offer of offers2) {
|
|
6323
|
+
await liquidityStore.save({
|
|
6324
|
+
pools: [
|
|
6325
|
+
{
|
|
6326
|
+
id: offer.hash,
|
|
6327
|
+
amount: offer.assets
|
|
6328
|
+
}
|
|
6329
|
+
],
|
|
6330
|
+
offerPools: [
|
|
6331
|
+
{
|
|
6332
|
+
offerHash: offer.hash,
|
|
6333
|
+
poolId: offer.hash,
|
|
6334
|
+
amount: offer.assets
|
|
6335
|
+
}
|
|
6336
|
+
],
|
|
6337
|
+
links: []
|
|
6338
|
+
});
|
|
6339
|
+
}
|
|
6340
|
+
logger.info({ msg: `Offers seeded from file`, count: offers2.length, file: opts.file });
|
|
6341
|
+
}
|
|
6342
|
+
await routerApi.serve({ port });
|
|
6343
|
+
logger.info({ msg: `API started`, url: `http://localhost:${port}` });
|
|
6344
|
+
const shutdown = async (signal) => {
|
|
6345
|
+
logger.warn({ msg: `Shutdown signal`, signal });
|
|
6346
|
+
try {
|
|
6347
|
+
stops.forEach((stop) => stop());
|
|
6348
|
+
} catch {
|
|
6349
|
+
}
|
|
6350
|
+
process.exit(0);
|
|
6351
|
+
};
|
|
6352
|
+
process.once("SIGINT", () => void shutdown("SIGINT"));
|
|
6353
|
+
process.once("SIGTERM", () => void shutdown("SIGTERM"));
|
|
6354
|
+
process.stdin.resume();
|
|
6355
|
+
}
|
|
6356
|
+
);
|
|
6357
|
+
async function getOffersFromFile(file) {
|
|
6358
|
+
const raw = await readFile(file, "utf8");
|
|
6359
|
+
const json = JSON.parse(raw);
|
|
6360
|
+
const items = Array.isArray(json) ? json : json.offers ?? [];
|
|
6361
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
6362
|
+
console.log("No offers found in file");
|
|
6363
|
+
return [];
|
|
6364
|
+
}
|
|
6365
|
+
return items.map((o) => Offer_exports.from(o));
|
|
6366
|
+
}
|
|
5937
6367
|
var startCmd = new RouterCmd("start");
|
|
5938
6368
|
startCmd.description("Start Router services.").addOption(
|
|
5939
6369
|
new Option("--block-window <n>", "Block window to get logs from").argParser((v) => Number(v)).default(100)
|
|
5940
|
-
).addOption(
|
|
5941
|
-
new Option("--seed <n>", "Seed random offers to router").argParser((v) => Number(v)).default(0)
|
|
5942
6370
|
).addOption(new Option("--file <path>", "Seed offers from a JSON file")).addOption(
|
|
5943
6371
|
new Option("--max-block-number <n>", "Block number at which to stop indexing").argParser(
|
|
5944
6372
|
(v) => Number(v)
|
|
@@ -5951,24 +6379,10 @@ startCmd.description("Start Router services.").addOption(
|
|
|
5951
6379
|
async (opts) => {
|
|
5952
6380
|
const {
|
|
5953
6381
|
port,
|
|
5954
|
-
routerServices: {
|
|
6382
|
+
routerServices: { indexer, routerApi },
|
|
5955
6383
|
logger
|
|
5956
6384
|
} = opts;
|
|
5957
6385
|
const stops = [];
|
|
5958
|
-
if (opts.seed > 0) {
|
|
5959
|
-
await offerStore.createMany(
|
|
5960
|
-
Array.from(Array(opts.seed ?? 0).keys()).map((_i) => ({
|
|
5961
|
-
offer: Offer_exports.random(),
|
|
5962
|
-
status: "valid"
|
|
5963
|
-
}))
|
|
5964
|
-
);
|
|
5965
|
-
logger.info({ msg: `Offers seeded`, count: opts.seed });
|
|
5966
|
-
}
|
|
5967
|
-
if (opts.file) {
|
|
5968
|
-
const offers2 = await getOffersFromFile(opts.file);
|
|
5969
|
-
await offerStore.createMany(offers2.map((ro) => ({ offer: ro })));
|
|
5970
|
-
logger.info({ msg: `Offers seeded from file`, count: offers2.length, file: opts.file });
|
|
5971
|
-
}
|
|
5972
6386
|
const selectedServices = new Set(opts.services);
|
|
5973
6387
|
if (selectedServices.has("indexer")) {
|
|
5974
6388
|
stops.push(await indexer.start());
|
|
@@ -5991,16 +6405,6 @@ startCmd.description("Start Router services.").addOption(
|
|
|
5991
6405
|
process.stdin.resume();
|
|
5992
6406
|
}
|
|
5993
6407
|
);
|
|
5994
|
-
async function getOffersFromFile(file) {
|
|
5995
|
-
const raw = await readFile(file, "utf8");
|
|
5996
|
-
const json = JSON.parse(raw);
|
|
5997
|
-
const items = Array.isArray(json) ? json : json.offers ?? [];
|
|
5998
|
-
if (!Array.isArray(items) || items.length === 0) {
|
|
5999
|
-
console.log("No offers found in file");
|
|
6000
|
-
return [];
|
|
6001
|
-
}
|
|
6002
|
-
return items.map((o) => Offer_exports.from(o));
|
|
6003
|
-
}
|
|
6004
6408
|
var tunnelCmd = new Command("tunnel");
|
|
6005
6409
|
tunnelCmd.description("Expose the local Router via ngrok").addOption(
|
|
6006
6410
|
new Option("--port <n>").env("ROUTER_PORT").default(7891).argParser((v) => Number(v))
|
|
@@ -6032,6 +6436,7 @@ cli.name(package_default.name.split("/")[1] || "cli").description(package_defaul
|
|
|
6032
6436
|
cli.addCommand(startCmd);
|
|
6033
6437
|
cli.addCommand(tunnelCmd);
|
|
6034
6438
|
cli.addCommand(mempoolCmd);
|
|
6439
|
+
cli.addCommand(mockCmd);
|
|
6035
6440
|
cli.command("__commands", { hidden: true }).description("internal: list commands").action(() => {
|
|
6036
6441
|
const names2 = cli.commands.map((c) => c.name());
|
|
6037
6442
|
console.log(JSON.stringify(names2));
|