@morpho-dev/router 0.6.0 → 0.7.1
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/dist/cli.js +2040 -2156
- package/dist/index.browser.d.mts +154 -290
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +154 -290
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +223 -464
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +223 -464
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +155 -301
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +154 -300
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +1966 -2190
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +1970 -2188
- package/dist/index.node.mjs.map +1 -1
- package/docs/integrator.md +5 -6
- package/package.json +1 -1
package/dist/index.node.js
CHANGED
|
@@ -51,11 +51,13 @@ require("@opentelemetry/propagator-aws-xray");
|
|
|
51
51
|
require("@opentelemetry/resources");
|
|
52
52
|
require("@opentelemetry/sdk-trace-node");
|
|
53
53
|
require("@opentelemetry/semantic-conventions");
|
|
54
|
+
let drizzle_orm = require("drizzle-orm");
|
|
54
55
|
let viem_chains = require("viem/chains");
|
|
55
56
|
let zod = require("zod");
|
|
56
57
|
zod = __toESM(zod);
|
|
57
58
|
let _openzeppelin_merkle_tree = require("@openzeppelin/merkle-tree");
|
|
58
59
|
let pako = require("pako");
|
|
60
|
+
let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
|
|
59
61
|
let _hono_node_server = require("@hono/node-server");
|
|
60
62
|
let hono = require("hono");
|
|
61
63
|
let hono_cors = require("hono/cors");
|
|
@@ -70,8 +72,6 @@ let node_fs_promises = require("node:fs/promises");
|
|
|
70
72
|
let node_path = require("node:path");
|
|
71
73
|
let node_url = require("node:url");
|
|
72
74
|
let marked = require("marked");
|
|
73
|
-
let drizzle_orm = require("drizzle-orm");
|
|
74
|
-
let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
|
|
75
75
|
let openapi_fetch = require("openapi-fetch");
|
|
76
76
|
openapi_fetch = __toESM(openapi_fetch);
|
|
77
77
|
let _electric_sql_pglite = require("@electric-sql/pglite");
|
|
@@ -1146,107 +1146,14 @@ const Morpho = [
|
|
|
1146
1146
|
//#region src/core/Callback.ts
|
|
1147
1147
|
var Callback_exports = /* @__PURE__ */ __exportAll({
|
|
1148
1148
|
Type: () => Type$1,
|
|
1149
|
-
decode: () => decode$2,
|
|
1150
|
-
decodeBuyERC20: () => decodeBuyERC20,
|
|
1151
|
-
decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
|
|
1152
|
-
decodeSellERC20Callback: () => decodeSellERC20Callback,
|
|
1153
|
-
encode: () => encode$2,
|
|
1154
|
-
encodeBuyERC20: () => encodeBuyERC20,
|
|
1155
|
-
encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
|
|
1156
|
-
encodeSellERC20Callback: () => encodeSellERC20Callback,
|
|
1157
1149
|
isEmptyCallback: () => isEmptyCallback
|
|
1158
1150
|
});
|
|
1159
1151
|
let Type$1 = /* @__PURE__ */ function(Type) {
|
|
1160
1152
|
Type["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
1161
|
-
Type["
|
|
1162
|
-
Type["BuyVaultV1Callback"] = "buy_vault_v1_callback";
|
|
1163
|
-
Type["SellERC20Callback"] = "sell_erc20_callback";
|
|
1153
|
+
Type["SellWithEmptyCallback"] = "sell_with_empty_callback";
|
|
1164
1154
|
return Type;
|
|
1165
1155
|
}({});
|
|
1166
1156
|
const isEmptyCallback = (offer) => offer.callback.data === "0x";
|
|
1167
|
-
function decode$2(type, data) {
|
|
1168
|
-
switch (type) {
|
|
1169
|
-
case Type$1.BuyERC20: return decodeBuyERC20(data);
|
|
1170
|
-
case Type$1.BuyVaultV1Callback: return decodeBuyVaultV1Callback(data);
|
|
1171
|
-
case Type$1.SellERC20Callback: return decodeSellERC20Callback(data);
|
|
1172
|
-
default: throw new Error("Invalid callback type");
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
function encode$2(type, data) {
|
|
1176
|
-
switch (type) {
|
|
1177
|
-
case Type$1.BuyERC20:
|
|
1178
|
-
if (!("tokens" in data)) throw new Error("Invalid callback data");
|
|
1179
|
-
return encodeBuyERC20(data);
|
|
1180
|
-
case Type$1.BuyVaultV1Callback:
|
|
1181
|
-
if (!("vaults" in data)) throw new Error("Invalid callback data");
|
|
1182
|
-
return encodeBuyVaultV1Callback(data);
|
|
1183
|
-
case Type$1.SellERC20Callback:
|
|
1184
|
-
if (!("collaterals" in data)) throw new Error("Invalid callback data");
|
|
1185
|
-
return encodeSellERC20Callback(data);
|
|
1186
|
-
default: throw new Error("Invalid callback type");
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
/**
|
|
1190
|
-
* Decodes BuyERC20 callback data into positions.
|
|
1191
|
-
* @param data - The ABI-encoded callback data containing token addresses and amounts.
|
|
1192
|
-
* @returns Array of positions with contract address and amount.
|
|
1193
|
-
* @throws If data is empty, malformed, or arrays have mismatched lengths.
|
|
1194
|
-
*/
|
|
1195
|
-
function decodeBuyERC20(data) {
|
|
1196
|
-
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
1197
|
-
let tokens;
|
|
1198
|
-
let amounts;
|
|
1199
|
-
try {
|
|
1200
|
-
[tokens, amounts] = (0, viem.decodeAbiParameters)([{ type: "address[]" }, { type: "uint256[]" }], data);
|
|
1201
|
-
} catch (_) {
|
|
1202
|
-
throw new Error("Invalid BuyERC20 callback data");
|
|
1203
|
-
}
|
|
1204
|
-
if (tokens.length !== amounts.length) throw new Error("Mismatched array lengths");
|
|
1205
|
-
return tokens.map((token, index) => ({
|
|
1206
|
-
contract: token,
|
|
1207
|
-
amount: amounts[index]
|
|
1208
|
-
}));
|
|
1209
|
-
}
|
|
1210
|
-
/**
|
|
1211
|
-
* Encodes BuyERC20 callback parameters into ABI-encoded data.
|
|
1212
|
-
* @param parameters - The tokens and amounts to encode.
|
|
1213
|
-
* @returns ABI-encoded hex string.
|
|
1214
|
-
*/
|
|
1215
|
-
function encodeBuyERC20(parameters) {
|
|
1216
|
-
return (0, viem.encodeAbiParameters)([{ type: "address[]" }, { type: "uint256[]" }], [parameters.tokens, parameters.amounts]);
|
|
1217
|
-
}
|
|
1218
|
-
function decodeBuyVaultV1Callback(data) {
|
|
1219
|
-
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
1220
|
-
try {
|
|
1221
|
-
const [vaults, amounts] = (0, viem.decodeAbiParameters)([{ type: "address[]" }, { type: "uint256[]" }], data);
|
|
1222
|
-
if (vaults.length !== amounts.length) throw new Error("Mismatched array lengths");
|
|
1223
|
-
return vaults.map((v, i) => ({
|
|
1224
|
-
contract: v,
|
|
1225
|
-
amount: amounts[i]
|
|
1226
|
-
}));
|
|
1227
|
-
} catch (_) {
|
|
1228
|
-
throw new Error("Invalid BuyVaultV1Callback callback data");
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
function decodeSellERC20Callback(data) {
|
|
1232
|
-
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
1233
|
-
try {
|
|
1234
|
-
const [collaterals, amounts] = (0, viem.decodeAbiParameters)([{ type: "address[]" }, { type: "uint256[]" }], data);
|
|
1235
|
-
if (collaterals.length !== amounts.length) throw new Error("Mismatched array lengths");
|
|
1236
|
-
return collaterals.map((c, i) => ({
|
|
1237
|
-
contract: c,
|
|
1238
|
-
amount: amounts[i]
|
|
1239
|
-
}));
|
|
1240
|
-
} catch (_) {
|
|
1241
|
-
throw new Error("Invalid SellERC20Callback callback data");
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
function encodeBuyVaultV1Callback(parameters) {
|
|
1245
|
-
return (0, viem.encodeAbiParameters)([{ type: "address[]" }, { type: "uint256[]" }], [parameters.vaults, parameters.amounts]);
|
|
1246
|
-
}
|
|
1247
|
-
function encodeSellERC20Callback(parameters) {
|
|
1248
|
-
return (0, viem.encodeAbiParameters)([{ type: "address[]" }, { type: "uint256[]" }], [parameters.collaterals, parameters.amounts]);
|
|
1249
|
-
}
|
|
1250
1157
|
|
|
1251
1158
|
//#endregion
|
|
1252
1159
|
//#region src/core/Chain.ts
|
|
@@ -1768,11 +1675,9 @@ var Liquidity_exports = /* @__PURE__ */ __exportAll({
|
|
|
1768
1675
|
calculateMaxDebt: () => calculateMaxDebt,
|
|
1769
1676
|
generateAllowancePoolId: () => generateAllowancePoolId,
|
|
1770
1677
|
generateBalancePoolId: () => generateBalancePoolId,
|
|
1771
|
-
generateBuyVaultCallbackPoolId: () => generateBuyVaultCallbackPoolId,
|
|
1772
1678
|
generateDebtPoolId: () => generateDebtPoolId,
|
|
1773
1679
|
generateMarketLiquidityPoolId: () => generateMarketLiquidityPoolId,
|
|
1774
1680
|
generateObligationCollateralPoolId: () => generateObligationCollateralPoolId,
|
|
1775
|
-
generateSellERC20CallbackPoolId: () => generateSellERC20CallbackPoolId,
|
|
1776
1681
|
generateUserVaultPositionPoolId: () => generateUserVaultPositionPoolId,
|
|
1777
1682
|
generateVaultPositionPoolId: () => generateVaultPositionPoolId
|
|
1778
1683
|
});
|
|
@@ -1801,14 +1706,6 @@ function generateAllowancePoolId(parameters) {
|
|
|
1801
1706
|
return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
|
|
1802
1707
|
}
|
|
1803
1708
|
/**
|
|
1804
|
-
* Generate pool ID for sell ERC20 callback pools.
|
|
1805
|
-
* Each offer has its own callback pool to prevent liquidity conflicts.
|
|
1806
|
-
*/
|
|
1807
|
-
function generateSellERC20CallbackPoolId(parameters) {
|
|
1808
|
-
const { user, chainId, obligationId, token, offerHash } = parameters;
|
|
1809
|
-
return `${user}-${chainId.toString()}-${obligationId}-${token}-${offerHash}-sell_erc20_callback`.toLowerCase();
|
|
1810
|
-
}
|
|
1811
|
-
/**
|
|
1812
1709
|
* Generate pool ID for obligation collateral pools.
|
|
1813
1710
|
* Obligation collateral pools represent collateral already deposited in the obligation.
|
|
1814
1711
|
* These pools are shared across all offers with the same obligation.
|
|
@@ -1818,13 +1715,6 @@ function generateObligationCollateralPoolId(parameters) {
|
|
|
1818
1715
|
return `${user}-${chainId.toString()}-${obligationId}-${token}-obligation-collateral`.toLowerCase();
|
|
1819
1716
|
}
|
|
1820
1717
|
/**
|
|
1821
|
-
* Generate pool ID for buy vault callback pools.
|
|
1822
|
-
*/
|
|
1823
|
-
function generateBuyVaultCallbackPoolId(parameters) {
|
|
1824
|
-
const { user, chainId, vault, offerHash } = parameters;
|
|
1825
|
-
return `${user}-${chainId.toString()}-${vault}-${offerHash}-${Type$1.BuyVaultV1Callback}`.toLowerCase();
|
|
1826
|
-
}
|
|
1827
|
-
/**
|
|
1828
1718
|
* Generate pool ID for debt pools.
|
|
1829
1719
|
*/
|
|
1830
1720
|
function generateDebtPoolId(parameters) {
|
|
@@ -2159,6 +2049,7 @@ var Offer_exports = /* @__PURE__ */ __exportAll({
|
|
|
2159
2049
|
obligationId: () => obligationId,
|
|
2160
2050
|
random: () => random$1,
|
|
2161
2051
|
serialize: () => serialize,
|
|
2052
|
+
takeEvent: () => takeEvent,
|
|
2162
2053
|
toSnakeCase: () => toSnakeCase,
|
|
2163
2054
|
types: () => types
|
|
2164
2055
|
});
|
|
@@ -2277,7 +2168,7 @@ function random$1(config) {
|
|
|
2277
2168
|
const chain = config?.chains ? config.chains[int(config.chains.length)] : chains$2.ethereum;
|
|
2278
2169
|
const loanToken = config?.loanTokens ? config.loanTokens[int(config.loanTokens.length)] : address();
|
|
2279
2170
|
const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [address()];
|
|
2280
|
-
|
|
2171
|
+
collateralCandidates[int(collateralCandidates.length)];
|
|
2281
2172
|
const maturityOption = weightedChoice([["end_of_month", 1], ["end_of_next_month", 1]]);
|
|
2282
2173
|
const maturity = config?.maturity ?? from$16(maturityOption);
|
|
2283
2174
|
const lltv = from$18(weightedChoice([
|
|
@@ -2304,21 +2195,10 @@ function random$1(config) {
|
|
|
2304
2195
|
const unit = BigInt(10) ** BigInt(loanTokenDecimals);
|
|
2305
2196
|
const amountBase = BigInt(100 + int(999901));
|
|
2306
2197
|
const assetsScaled = config?.assets ?? amountBase * unit;
|
|
2307
|
-
const
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
};
|
|
2312
|
-
const sellCallbackAddress = "0x3333333333333333333333333333333333333333";
|
|
2313
|
-
const amount = assetsScaled * 1000000000000000000000n;
|
|
2314
|
-
return {
|
|
2315
|
-
address: sellCallbackAddress,
|
|
2316
|
-
data: encodeSellERC20Callback({
|
|
2317
|
-
collaterals: [collateralAsset],
|
|
2318
|
-
amounts: [amount]
|
|
2319
|
-
})
|
|
2320
|
-
};
|
|
2321
|
-
})();
|
|
2198
|
+
const emptyCallback = {
|
|
2199
|
+
address: viem.zeroAddress,
|
|
2200
|
+
data: "0x"
|
|
2201
|
+
};
|
|
2322
2202
|
return from$14({
|
|
2323
2203
|
maker: config?.maker ?? address(),
|
|
2324
2204
|
assets: assetsScaled,
|
|
@@ -2337,7 +2217,7 @@ function random$1(config) {
|
|
|
2337
2217
|
...random$3(),
|
|
2338
2218
|
lltv
|
|
2339
2219
|
})).sort((a, b) => a.asset.localeCompare(b.asset)),
|
|
2340
|
-
callback: config?.callback ??
|
|
2220
|
+
callback: config?.callback ?? emptyCallback
|
|
2341
2221
|
});
|
|
2342
2222
|
}
|
|
2343
2223
|
const weightedChoice = (pairs) => {
|
|
@@ -2627,6 +2507,94 @@ function decode$1(data) {
|
|
|
2627
2507
|
});
|
|
2628
2508
|
}
|
|
2629
2509
|
/**
|
|
2510
|
+
* ABI for the Take event emitted by the Morpho V2 contract.
|
|
2511
|
+
*/
|
|
2512
|
+
const takeEvent = {
|
|
2513
|
+
type: "event",
|
|
2514
|
+
name: "Take",
|
|
2515
|
+
inputs: [
|
|
2516
|
+
{
|
|
2517
|
+
name: "caller",
|
|
2518
|
+
type: "address",
|
|
2519
|
+
indexed: false,
|
|
2520
|
+
internalType: "address"
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
name: "id",
|
|
2524
|
+
type: "bytes32",
|
|
2525
|
+
indexed: true,
|
|
2526
|
+
internalType: "bytes32"
|
|
2527
|
+
},
|
|
2528
|
+
{
|
|
2529
|
+
name: "maker",
|
|
2530
|
+
type: "address",
|
|
2531
|
+
indexed: true,
|
|
2532
|
+
internalType: "address"
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
name: "taker",
|
|
2536
|
+
type: "address",
|
|
2537
|
+
indexed: true,
|
|
2538
|
+
internalType: "address"
|
|
2539
|
+
},
|
|
2540
|
+
{
|
|
2541
|
+
name: "offerIsBuy",
|
|
2542
|
+
type: "bool",
|
|
2543
|
+
indexed: false,
|
|
2544
|
+
internalType: "bool"
|
|
2545
|
+
},
|
|
2546
|
+
{
|
|
2547
|
+
name: "buyerAssets",
|
|
2548
|
+
type: "uint256",
|
|
2549
|
+
indexed: false,
|
|
2550
|
+
internalType: "uint256"
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
name: "sellerAssets",
|
|
2554
|
+
type: "uint256",
|
|
2555
|
+
indexed: false,
|
|
2556
|
+
internalType: "uint256"
|
|
2557
|
+
},
|
|
2558
|
+
{
|
|
2559
|
+
name: "obligationUnits",
|
|
2560
|
+
type: "uint256",
|
|
2561
|
+
indexed: false,
|
|
2562
|
+
internalType: "uint256"
|
|
2563
|
+
},
|
|
2564
|
+
{
|
|
2565
|
+
name: "obligationShares",
|
|
2566
|
+
type: "uint256",
|
|
2567
|
+
indexed: false,
|
|
2568
|
+
internalType: "uint256"
|
|
2569
|
+
},
|
|
2570
|
+
{
|
|
2571
|
+
name: "buyerIsLender",
|
|
2572
|
+
type: "bool",
|
|
2573
|
+
indexed: false,
|
|
2574
|
+
internalType: "bool"
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
name: "sellerIsBorrower",
|
|
2578
|
+
type: "bool",
|
|
2579
|
+
indexed: false,
|
|
2580
|
+
internalType: "bool"
|
|
2581
|
+
},
|
|
2582
|
+
{
|
|
2583
|
+
name: "group",
|
|
2584
|
+
type: "bytes32",
|
|
2585
|
+
indexed: false,
|
|
2586
|
+
internalType: "bytes32"
|
|
2587
|
+
},
|
|
2588
|
+
{
|
|
2589
|
+
name: "consumed",
|
|
2590
|
+
type: "uint256",
|
|
2591
|
+
indexed: false,
|
|
2592
|
+
internalType: "uint256"
|
|
2593
|
+
}
|
|
2594
|
+
],
|
|
2595
|
+
anonymous: false
|
|
2596
|
+
};
|
|
2597
|
+
/**
|
|
2630
2598
|
* ABI for the Consume event emitted by the Obligation contract.
|
|
2631
2599
|
*/
|
|
2632
2600
|
const consumedEvent = {
|
|
@@ -3378,160 +3346,701 @@ var SignatureDomainError = class extends BaseError {
|
|
|
3378
3346
|
const BrandTypeId = Symbol.for("mempool/Brand");
|
|
3379
3347
|
|
|
3380
3348
|
//#endregion
|
|
3381
|
-
//#region src/
|
|
3382
|
-
|
|
3383
|
-
let { db, collector, client, lastBlockNumber: blockNumber, epoch, options: { maxBatchSize = 1e3, blockWindow } = {} } = parameters;
|
|
3384
|
-
const logger = getLogger();
|
|
3385
|
-
let startBlock = blockNumber;
|
|
3386
|
-
let reorgDetected = false;
|
|
3387
|
-
const { blockNumber: latestBlockNumberChain } = await db.blocks.getChain(client.chain.id);
|
|
3388
|
-
const stream = streamLogs({
|
|
3389
|
-
client,
|
|
3390
|
-
contractAddress: client.chain.custom.morpho.address,
|
|
3391
|
-
event: consumedEvent,
|
|
3392
|
-
blockNumberGte: blockNumber,
|
|
3393
|
-
blockNumberLte: latestBlockNumberChain,
|
|
3394
|
-
order: "asc",
|
|
3395
|
-
options: {
|
|
3396
|
-
maxBatchSize,
|
|
3397
|
-
blockWindow
|
|
3398
|
-
}
|
|
3399
|
-
});
|
|
3400
|
-
for await (const { logs, blockNumber: lastStreamBlockNumber } of stream) {
|
|
3401
|
-
const parsedLogs = (0, viem.parseEventLogs)({
|
|
3402
|
-
abi: [consumedEvent],
|
|
3403
|
-
logs
|
|
3404
|
-
});
|
|
3405
|
-
const events = [];
|
|
3406
|
-
for (const log of parsedLogs) {
|
|
3407
|
-
if (log.blockNumber === null || log.logIndex === null || log.transactionHash === null) {
|
|
3408
|
-
logger.debug({
|
|
3409
|
-
collector,
|
|
3410
|
-
chainId: client.chain.id,
|
|
3411
|
-
msg: "Skipping log because it is missing required fields"
|
|
3412
|
-
});
|
|
3413
|
-
continue;
|
|
3414
|
-
}
|
|
3415
|
-
events.push({
|
|
3416
|
-
id: `${log.blockNumber.toString()}-${log.logIndex.toString()}-${client.chain.id}-${log.transactionHash}`,
|
|
3417
|
-
chainId: client.chain.id,
|
|
3418
|
-
maker: log.args.user,
|
|
3419
|
-
group: log.args.group,
|
|
3420
|
-
amount: log.args.amount,
|
|
3421
|
-
blockNumber: Number(log.blockNumber)
|
|
3422
|
-
});
|
|
3423
|
-
}
|
|
3424
|
-
await db.transaction(async (dbTx) => {
|
|
3425
|
-
try {
|
|
3426
|
-
await dbTx.consumed.create(events);
|
|
3427
|
-
if (events.length > 0) logger.info({
|
|
3428
|
-
msg: `Events indexed`,
|
|
3429
|
-
collector,
|
|
3430
|
-
count: events.length,
|
|
3431
|
-
chain_id: client.chain.id,
|
|
3432
|
-
block_range: [startBlock, lastStreamBlockNumber]
|
|
3433
|
-
});
|
|
3434
|
-
} catch (err) {
|
|
3435
|
-
logger.error({
|
|
3436
|
-
err,
|
|
3437
|
-
msg: "Failed to process offer_consumed events"
|
|
3438
|
-
});
|
|
3439
|
-
}
|
|
3440
|
-
blockNumber = lastStreamBlockNumber;
|
|
3441
|
-
try {
|
|
3442
|
-
await dbTx.blocks.advanceCollector({
|
|
3443
|
-
collectorName: collector,
|
|
3444
|
-
chainId: client.chain.id,
|
|
3445
|
-
blockNumber,
|
|
3446
|
-
epoch
|
|
3447
|
-
});
|
|
3448
|
-
} catch (_) {
|
|
3449
|
-
try {
|
|
3450
|
-
const ancestor = await dbTx.blocks.getCollector({
|
|
3451
|
-
collectorName: collector,
|
|
3452
|
-
chainId: client.chain.id
|
|
3453
|
-
});
|
|
3454
|
-
blockNumber = ancestor.blockNumber;
|
|
3455
|
-
const deleted = await dbTx.consumed.delete({
|
|
3456
|
-
chainId: client.chain.id,
|
|
3457
|
-
blockNumberGte: blockNumber + 1
|
|
3458
|
-
});
|
|
3459
|
-
logger.info({
|
|
3460
|
-
collector,
|
|
3461
|
-
chain_id: client.chain.id,
|
|
3462
|
-
msg: `Reorg detected, events deleted`,
|
|
3463
|
-
count: deleted,
|
|
3464
|
-
block_number: blockNumber
|
|
3465
|
-
});
|
|
3466
|
-
await dbTx.blocks.advanceCollector({
|
|
3467
|
-
collectorName: collector,
|
|
3468
|
-
chainId: client.chain.id,
|
|
3469
|
-
blockNumber,
|
|
3470
|
-
epoch: ancestor.epoch
|
|
3471
|
-
});
|
|
3472
|
-
reorgDetected = true;
|
|
3473
|
-
} catch (err) {
|
|
3474
|
-
const msg = "Failed to delete consumed events when handling reorg.";
|
|
3475
|
-
logger.error({
|
|
3476
|
-
collector,
|
|
3477
|
-
chainId: client.chain.id,
|
|
3478
|
-
msg,
|
|
3479
|
-
err
|
|
3480
|
-
});
|
|
3481
|
-
throw new Error(msg);
|
|
3482
|
-
}
|
|
3483
|
-
}
|
|
3484
|
-
});
|
|
3485
|
-
if (reorgDetected) return;
|
|
3486
|
-
yield blockNumber;
|
|
3487
|
-
startBlock = blockNumber;
|
|
3488
|
-
}
|
|
3489
|
-
}
|
|
3349
|
+
//#region src/database/drizzle/VERSION.ts
|
|
3350
|
+
const VERSION = "router_v1.6";
|
|
3490
3351
|
|
|
3491
3352
|
//#endregion
|
|
3492
|
-
//#region src/
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3353
|
+
//#region src/database/drizzle/schema.ts
|
|
3354
|
+
var schema_exports = /* @__PURE__ */ __exportAll({
|
|
3355
|
+
PositionTypes: () => PositionTypes,
|
|
3356
|
+
StatusCode: () => StatusCode,
|
|
3357
|
+
TABLE_NAMES: () => TABLE_NAMES,
|
|
3358
|
+
VERSIONED_TABLE_NAMES: () => VERSIONED_TABLE_NAMES,
|
|
3359
|
+
callbacks: () => callbacks,
|
|
3360
|
+
chains: () => chains$1,
|
|
3361
|
+
collectors: () => collectors,
|
|
3362
|
+
consumedEvents: () => consumedEvents,
|
|
3363
|
+
groups: () => groups,
|
|
3364
|
+
lots: () => lots,
|
|
3365
|
+
merklePaths: () => merklePaths,
|
|
3366
|
+
obligationCollateralsV2: () => obligationCollateralsV2,
|
|
3367
|
+
obligations: () => obligations,
|
|
3368
|
+
offers: () => offers,
|
|
3369
|
+
offersCallbacks: () => offersCallbacks,
|
|
3370
|
+
offsets: () => offsets,
|
|
3371
|
+
oracles: () => oracles$1,
|
|
3372
|
+
positionTypes: () => positionTypes,
|
|
3373
|
+
positions: () => positions,
|
|
3374
|
+
status: () => status,
|
|
3375
|
+
transfers: () => transfers,
|
|
3376
|
+
trees: () => trees,
|
|
3377
|
+
validations: () => validations
|
|
3378
|
+
});
|
|
3379
|
+
const s = (0, drizzle_orm_pg_core.pgSchema)(VERSION);
|
|
3380
|
+
var EnumTableName = /* @__PURE__ */ function(EnumTableName) {
|
|
3381
|
+
EnumTableName["OBLIGATIONS"] = "obligations";
|
|
3382
|
+
EnumTableName["GROUPS"] = "groups";
|
|
3383
|
+
EnumTableName["CONSUMED_EVENTS"] = "consumed_events";
|
|
3384
|
+
EnumTableName["OBLIGATION_COLLATERALS_V2"] = "obligation_collaterals_v2";
|
|
3385
|
+
EnumTableName["ORACLES"] = "oracles";
|
|
3386
|
+
EnumTableName["OFFERS"] = "offers";
|
|
3387
|
+
EnumTableName["OFFERS_CALLBACKS"] = "offers_callbacks";
|
|
3388
|
+
EnumTableName["CALLBACKS"] = "callbacks";
|
|
3389
|
+
EnumTableName["POSITIONS"] = "positions";
|
|
3390
|
+
EnumTableName["TRANSFERS"] = "transfers";
|
|
3391
|
+
EnumTableName["VALIDATIONS"] = "validations";
|
|
3392
|
+
EnumTableName["COLLECTORS"] = "collectors";
|
|
3393
|
+
EnumTableName["CHAINS"] = "chains";
|
|
3394
|
+
EnumTableName["LOTS"] = "lots";
|
|
3395
|
+
EnumTableName["OFFSETS"] = "offsets";
|
|
3396
|
+
EnumTableName["TREES"] = "trees";
|
|
3397
|
+
EnumTableName["MERKLE_PATHS"] = "merkle_paths";
|
|
3398
|
+
return EnumTableName;
|
|
3399
|
+
}(EnumTableName || {});
|
|
3400
|
+
const TABLE_NAMES = Object.values(EnumTableName);
|
|
3401
|
+
const VERSIONED_TABLE_NAMES = TABLE_NAMES.map((table) => `"${VERSION}"."${table}"`);
|
|
3402
|
+
const obligations = s.table(EnumTableName.OBLIGATIONS, {
|
|
3403
|
+
obligationId: (0, drizzle_orm_pg_core.varchar)("obligation_id", { length: 66 }).primaryKey(),
|
|
3404
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3405
|
+
loanToken: (0, drizzle_orm_pg_core.varchar)("loan_token", { length: 42 }).notNull(),
|
|
3406
|
+
maturity: (0, drizzle_orm_pg_core.integer)("maturity").notNull()
|
|
3407
|
+
});
|
|
3408
|
+
const groups = s.table(EnumTableName.GROUPS, {
|
|
3409
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3410
|
+
maker: (0, drizzle_orm_pg_core.varchar)("maker", { length: 42 }).notNull(),
|
|
3411
|
+
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
3412
|
+
consumed: (0, drizzle_orm_pg_core.numeric)("consumed", {
|
|
3413
|
+
precision: 78,
|
|
3414
|
+
scale: 0
|
|
3415
|
+
}).notNull(),
|
|
3416
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3417
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3418
|
+
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
3419
|
+
columns: [
|
|
3420
|
+
table.chainId,
|
|
3421
|
+
table.maker,
|
|
3422
|
+
table.group
|
|
3423
|
+
],
|
|
3424
|
+
name: "groups_pk"
|
|
3425
|
+
}), (0, drizzle_orm_pg_core.index)("groups_chain_id_maker_group_consumed_idx").on(table.chainId, table.maker, table.group, table.consumed)]);
|
|
3426
|
+
const consumedEvents = s.table(EnumTableName.CONSUMED_EVENTS, {
|
|
3427
|
+
eventId: (0, drizzle_orm_pg_core.varchar)("event_id", { length: 128 }).primaryKey(),
|
|
3428
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3429
|
+
maker: (0, drizzle_orm_pg_core.varchar)("maker", { length: 42 }).notNull(),
|
|
3430
|
+
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
3431
|
+
amount: (0, drizzle_orm_pg_core.numeric)("amount", {
|
|
3432
|
+
precision: 78,
|
|
3433
|
+
scale: 0
|
|
3434
|
+
}).notNull(),
|
|
3435
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3436
|
+
createdAt: (0, drizzle_orm_pg_core.timestamp)("created_at").defaultNow().notNull()
|
|
3437
|
+
}, (t) => [
|
|
3438
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3439
|
+
columns: [
|
|
3440
|
+
t.chainId,
|
|
3441
|
+
t.maker,
|
|
3442
|
+
t.group
|
|
3443
|
+
],
|
|
3444
|
+
foreignColumns: [
|
|
3445
|
+
groups.chainId,
|
|
3446
|
+
groups.maker,
|
|
3447
|
+
groups.group
|
|
3448
|
+
],
|
|
3449
|
+
name: "consumed_events_groups_fk"
|
|
3450
|
+
}).onDelete("cascade"),
|
|
3451
|
+
(0, drizzle_orm_pg_core.index)("consumed_events_group_idx").on(t.chainId, t.maker, t.group),
|
|
3452
|
+
(0, drizzle_orm_pg_core.index)("consumed_events_block_number_idx").on(t.blockNumber)
|
|
3453
|
+
]);
|
|
3454
|
+
const obligationCollateralsV2 = s.table(EnumTableName.OBLIGATION_COLLATERALS_V2, {
|
|
3455
|
+
obligationId: (0, drizzle_orm_pg_core.varchar)("obligation_id", { length: 66 }).notNull().references(() => obligations.obligationId, { onDelete: "cascade" }),
|
|
3456
|
+
asset: (0, drizzle_orm_pg_core.varchar)("asset", { length: 42 }).notNull(),
|
|
3457
|
+
oracleChainId: (0, drizzle_orm_pg_core.bigint)("oracle_chain_id", { mode: "number" }).$type().notNull(),
|
|
3458
|
+
oracleAddress: (0, drizzle_orm_pg_core.varchar)("oracle_address", { length: 42 }).notNull(),
|
|
3459
|
+
lltv: (0, drizzle_orm_pg_core.bigint)("lltv", { mode: "bigint" }).notNull(),
|
|
3460
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3461
|
+
}, (table) => [
|
|
3462
|
+
(0, drizzle_orm_pg_core.primaryKey)({
|
|
3463
|
+
columns: [table.obligationId, table.asset],
|
|
3464
|
+
name: "obligation_collaterals_v2_pk"
|
|
3465
|
+
}),
|
|
3466
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3467
|
+
columns: [table.oracleChainId, table.oracleAddress],
|
|
3468
|
+
foreignColumns: [oracles$1.chainId, oracles$1.address],
|
|
3469
|
+
name: "obligation_collaterals_v2_oracles_fk"
|
|
3470
|
+
}),
|
|
3471
|
+
(0, drizzle_orm_pg_core.index)("obligation_collaterals_v2_obligation_id_idx").on(table.obligationId),
|
|
3472
|
+
(0, drizzle_orm_pg_core.index)("obligation_collaterals_v2_oracle_fk_idx").on(table.oracleChainId, table.oracleAddress)
|
|
3473
|
+
]);
|
|
3474
|
+
const oracles$1 = s.table(EnumTableName.ORACLES, {
|
|
3475
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3476
|
+
address: (0, drizzle_orm_pg_core.varchar)("address", { length: 42 }).notNull(),
|
|
3477
|
+
price: (0, drizzle_orm_pg_core.numeric)("price", {
|
|
3478
|
+
precision: 78,
|
|
3479
|
+
scale: 0
|
|
3480
|
+
}),
|
|
3481
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3482
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3483
|
+
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
3484
|
+
columns: [table.chainId, table.address],
|
|
3485
|
+
name: "oracles_pk"
|
|
3486
|
+
})]);
|
|
3487
|
+
const offers = s.table(EnumTableName.OFFERS, {
|
|
3488
|
+
hash: (0, drizzle_orm_pg_core.varchar)("hash", { length: 66 }).primaryKey(),
|
|
3489
|
+
obligationId: (0, drizzle_orm_pg_core.varchar)("obligation_id", { length: 66 }).notNull().references(() => obligations.obligationId, { onDelete: "cascade" }),
|
|
3490
|
+
assets: (0, drizzle_orm_pg_core.numeric)("assets", {
|
|
3491
|
+
precision: 78,
|
|
3492
|
+
scale: 0
|
|
3493
|
+
}).notNull(),
|
|
3494
|
+
obligationUnits: (0, drizzle_orm_pg_core.numeric)("obligation_units", {
|
|
3495
|
+
precision: 78,
|
|
3496
|
+
scale: 0
|
|
3497
|
+
}).notNull().default("0"),
|
|
3498
|
+
obligationShares: (0, drizzle_orm_pg_core.numeric)("obligation_shares", {
|
|
3499
|
+
precision: 78,
|
|
3500
|
+
scale: 0
|
|
3501
|
+
}).notNull().default("0"),
|
|
3502
|
+
price: (0, drizzle_orm_pg_core.numeric)("price", {
|
|
3503
|
+
precision: 78,
|
|
3504
|
+
scale: 0
|
|
3505
|
+
}).notNull(),
|
|
3506
|
+
maturity: (0, drizzle_orm_pg_core.integer)("maturity").notNull(),
|
|
3507
|
+
expiry: (0, drizzle_orm_pg_core.integer)("expiry").notNull(),
|
|
3508
|
+
start: (0, drizzle_orm_pg_core.integer)("start").notNull(),
|
|
3509
|
+
groupChainId: (0, drizzle_orm_pg_core.bigint)("group_chain_id", { mode: "number" }).$type().notNull(),
|
|
3510
|
+
groupMaker: (0, drizzle_orm_pg_core.varchar)("group_maker", { length: 42 }).notNull(),
|
|
3511
|
+
group: (0, drizzle_orm_pg_core.varchar)("group_group", { length: 66 }).notNull(),
|
|
3512
|
+
session: (0, drizzle_orm_pg_core.varchar)("session", { length: 66 }).notNull(),
|
|
3513
|
+
buy: (0, drizzle_orm_pg_core.boolean)("buy").notNull(),
|
|
3514
|
+
callbackAddress: (0, drizzle_orm_pg_core.varchar)("callback_address", { length: 42 }).notNull(),
|
|
3515
|
+
callbackData: (0, drizzle_orm_pg_core.text)("callback_data").notNull(),
|
|
3516
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3517
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3518
|
+
}, (table) => [
|
|
3519
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3520
|
+
columns: [
|
|
3521
|
+
table.groupChainId,
|
|
3522
|
+
table.groupMaker,
|
|
3523
|
+
table.group
|
|
3524
|
+
],
|
|
3525
|
+
foreignColumns: [
|
|
3526
|
+
groups.chainId,
|
|
3527
|
+
groups.maker,
|
|
3528
|
+
groups.group
|
|
3529
|
+
],
|
|
3530
|
+
name: "offers_groups_fk"
|
|
3531
|
+
}).onDelete("cascade"),
|
|
3532
|
+
(0, drizzle_orm_pg_core.index)("offers_group_fk_idx").on(table.groupChainId, table.groupMaker, table.group),
|
|
3533
|
+
(0, drizzle_orm_pg_core.index)("offers_group_and_hash_idx").on(table.groupChainId, table.groupMaker, table.group, table.hash),
|
|
3534
|
+
(0, drizzle_orm_pg_core.index)("offers_obligation_id_side_idx").on(table.obligationId, table.buy)
|
|
3535
|
+
]);
|
|
3536
|
+
const offersCallbacks = s.table(EnumTableName.OFFERS_CALLBACKS, {
|
|
3537
|
+
offerHash: (0, drizzle_orm_pg_core.varchar)("offer_hash", { length: 66 }).notNull().references(() => offers.hash, { onDelete: "cascade" }),
|
|
3538
|
+
callbackId: (0, drizzle_orm_pg_core.varchar)("callback_id", { length: 66 })
|
|
3539
|
+
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
3540
|
+
columns: [table.offerHash, table.callbackId],
|
|
3541
|
+
name: "offers_callbacks_pk"
|
|
3542
|
+
})]);
|
|
3543
|
+
const callbacks = s.table(EnumTableName.CALLBACKS, {
|
|
3544
|
+
id: (0, drizzle_orm_pg_core.varchar)("id", { length: 66 }).primaryKey(),
|
|
3545
|
+
positionChainId: (0, drizzle_orm_pg_core.bigint)("position_chain_id", { mode: "number" }).$type().notNull(),
|
|
3546
|
+
positionContract: (0, drizzle_orm_pg_core.varchar)("position_contract", { length: 42 }).notNull(),
|
|
3547
|
+
positionUser: (0, drizzle_orm_pg_core.varchar)("position_user", { length: 42 }).notNull(),
|
|
3548
|
+
amount: (0, drizzle_orm_pg_core.numeric)("amount", {
|
|
3549
|
+
precision: 78,
|
|
3550
|
+
scale: 0
|
|
3551
|
+
})
|
|
3552
|
+
}, (table) => [(0, drizzle_orm_pg_core.foreignKey)({
|
|
3553
|
+
columns: [
|
|
3554
|
+
table.positionChainId,
|
|
3555
|
+
table.positionContract,
|
|
3556
|
+
table.positionUser
|
|
3557
|
+
],
|
|
3558
|
+
foreignColumns: [
|
|
3559
|
+
positions.chainId,
|
|
3560
|
+
positions.contract,
|
|
3561
|
+
positions.user
|
|
3562
|
+
],
|
|
3563
|
+
name: "callbacks_positions_fk"
|
|
3564
|
+
}).onDelete("cascade")]);
|
|
3565
|
+
const lots = s.table(EnumTableName.LOTS, {
|
|
3566
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3567
|
+
user: (0, drizzle_orm_pg_core.varchar)("user", { length: 42 }).notNull(),
|
|
3568
|
+
contract: (0, drizzle_orm_pg_core.varchar)("contract", { length: 42 }).notNull(),
|
|
3569
|
+
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
3570
|
+
lower: (0, drizzle_orm_pg_core.numeric)("lower", {
|
|
3571
|
+
precision: 78,
|
|
3572
|
+
scale: 0
|
|
3573
|
+
}).notNull(),
|
|
3574
|
+
upper: (0, drizzle_orm_pg_core.numeric)("upper", {
|
|
3575
|
+
precision: 78,
|
|
3576
|
+
scale: 0
|
|
3577
|
+
}).notNull()
|
|
3578
|
+
}, (table) => [
|
|
3579
|
+
(0, drizzle_orm_pg_core.primaryKey)({
|
|
3580
|
+
columns: [
|
|
3581
|
+
table.chainId,
|
|
3582
|
+
table.user,
|
|
3583
|
+
table.contract,
|
|
3584
|
+
table.group
|
|
3585
|
+
],
|
|
3586
|
+
name: "lots_pk"
|
|
3587
|
+
}),
|
|
3588
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3589
|
+
columns: [
|
|
3590
|
+
table.chainId,
|
|
3591
|
+
table.contract,
|
|
3592
|
+
table.user
|
|
3593
|
+
],
|
|
3594
|
+
foreignColumns: [
|
|
3595
|
+
positions.chainId,
|
|
3596
|
+
positions.contract,
|
|
3597
|
+
positions.user
|
|
3598
|
+
],
|
|
3599
|
+
name: "lots_positions_fk"
|
|
3600
|
+
}).onDelete("cascade"),
|
|
3601
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3602
|
+
columns: [
|
|
3603
|
+
table.chainId,
|
|
3604
|
+
table.user,
|
|
3605
|
+
table.group
|
|
3606
|
+
],
|
|
3607
|
+
foreignColumns: [
|
|
3608
|
+
groups.chainId,
|
|
3609
|
+
groups.maker,
|
|
3610
|
+
groups.group
|
|
3611
|
+
],
|
|
3612
|
+
name: "lots_groups_fk"
|
|
3613
|
+
}).onDelete("cascade")
|
|
3614
|
+
]);
|
|
3615
|
+
const offsets = s.table(EnumTableName.OFFSETS, {
|
|
3616
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3617
|
+
user: (0, drizzle_orm_pg_core.varchar)("user", { length: 42 }).notNull(),
|
|
3618
|
+
contract: (0, drizzle_orm_pg_core.varchar)("contract", { length: 42 }).notNull(),
|
|
3619
|
+
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
3620
|
+
value: (0, drizzle_orm_pg_core.numeric)("value", {
|
|
3621
|
+
precision: 78,
|
|
3622
|
+
scale: 0
|
|
3623
|
+
}).notNull()
|
|
3624
|
+
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
3625
|
+
columns: [
|
|
3626
|
+
table.chainId,
|
|
3627
|
+
table.user,
|
|
3628
|
+
table.contract,
|
|
3629
|
+
table.group
|
|
3630
|
+
],
|
|
3631
|
+
name: "offsets_pk"
|
|
3632
|
+
}), (0, drizzle_orm_pg_core.foreignKey)({
|
|
3633
|
+
columns: [
|
|
3634
|
+
table.chainId,
|
|
3635
|
+
table.contract,
|
|
3636
|
+
table.user
|
|
3637
|
+
],
|
|
3638
|
+
foreignColumns: [
|
|
3639
|
+
positions.chainId,
|
|
3640
|
+
positions.contract,
|
|
3641
|
+
positions.user
|
|
3642
|
+
],
|
|
3643
|
+
name: "offsets_positions_fk"
|
|
3644
|
+
}).onDelete("cascade")]);
|
|
3645
|
+
const PositionTypes = s.enum("position_type", Object.values(Type));
|
|
3646
|
+
const positionTypes = s.table("position_types", {
|
|
3647
|
+
id: (0, drizzle_orm_pg_core.serial)("id").primaryKey(),
|
|
3648
|
+
type: PositionTypes("type").notNull()
|
|
3649
|
+
});
|
|
3650
|
+
const positions = s.table(EnumTableName.POSITIONS, {
|
|
3651
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3652
|
+
contract: (0, drizzle_orm_pg_core.varchar)("contract", { length: 42 }).notNull(),
|
|
3653
|
+
user: (0, drizzle_orm_pg_core.varchar)("user", { length: 42 }).notNull(),
|
|
3654
|
+
positionTypeId: (0, drizzle_orm_pg_core.integer)("position_type_id").notNull().references(() => positionTypes.id, { onDelete: "no action" }),
|
|
3655
|
+
balance: (0, drizzle_orm_pg_core.numeric)("balance", {
|
|
3656
|
+
precision: 78,
|
|
3657
|
+
scale: 0
|
|
3658
|
+
}),
|
|
3659
|
+
asset: (0, drizzle_orm_pg_core.varchar)("asset", { length: 42 }),
|
|
3660
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3661
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3662
|
+
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
3663
|
+
columns: [
|
|
3664
|
+
table.chainId,
|
|
3665
|
+
table.contract,
|
|
3666
|
+
table.user
|
|
3667
|
+
],
|
|
3668
|
+
name: "positions_pk"
|
|
3669
|
+
})]);
|
|
3670
|
+
const transfers = s.table(EnumTableName.TRANSFERS, {
|
|
3671
|
+
eventId: (0, drizzle_orm_pg_core.varchar)("event_id", { length: 128 }).primaryKey(),
|
|
3672
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3673
|
+
contract: (0, drizzle_orm_pg_core.varchar)("contract", { length: 42 }).notNull(),
|
|
3674
|
+
from: (0, drizzle_orm_pg_core.varchar)("from", { length: 42 }).notNull(),
|
|
3675
|
+
to: (0, drizzle_orm_pg_core.varchar)("to", { length: 42 }).notNull(),
|
|
3676
|
+
value: (0, drizzle_orm_pg_core.numeric)("value", {
|
|
3677
|
+
precision: 78,
|
|
3678
|
+
scale: 0
|
|
3679
|
+
}).notNull(),
|
|
3680
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3681
|
+
createdAt: (0, drizzle_orm_pg_core.timestamp)("created_at").defaultNow().notNull()
|
|
3682
|
+
}, (table) => [
|
|
3683
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3684
|
+
columns: [
|
|
3685
|
+
table.chainId,
|
|
3686
|
+
table.contract,
|
|
3687
|
+
table.from
|
|
3688
|
+
],
|
|
3689
|
+
foreignColumns: [
|
|
3690
|
+
positions.chainId,
|
|
3691
|
+
positions.contract,
|
|
3692
|
+
positions.user
|
|
3693
|
+
],
|
|
3694
|
+
name: "transfers_positions_from_fk"
|
|
3695
|
+
}).onDelete("cascade"),
|
|
3696
|
+
(0, drizzle_orm_pg_core.foreignKey)({
|
|
3697
|
+
columns: [
|
|
3698
|
+
table.chainId,
|
|
3699
|
+
table.contract,
|
|
3700
|
+
table.to
|
|
3701
|
+
],
|
|
3702
|
+
foreignColumns: [
|
|
3703
|
+
positions.chainId,
|
|
3704
|
+
positions.contract,
|
|
3705
|
+
positions.user
|
|
3706
|
+
],
|
|
3707
|
+
name: "transfers_positions_to_fk"
|
|
3708
|
+
}).onDelete("cascade"),
|
|
3709
|
+
(0, drizzle_orm_pg_core.index)("transfers_chain_contract_user_idx").on(table.chainId, table.contract, table.from, table.to, table.blockNumber)
|
|
3710
|
+
]);
|
|
3711
|
+
const StatusCode = s.enum("status_code", Object.values(Status));
|
|
3712
|
+
const status = s.table("status", {
|
|
3713
|
+
id: (0, drizzle_orm_pg_core.serial)("id").primaryKey(),
|
|
3714
|
+
code: StatusCode("code").unique()
|
|
3715
|
+
});
|
|
3716
|
+
const validations = s.table("validations", {
|
|
3717
|
+
offerHash: (0, drizzle_orm_pg_core.varchar)("offer_hash", { length: 66 }).primaryKey().references(() => offers.hash, { onDelete: "cascade" }),
|
|
3718
|
+
statusId: (0, drizzle_orm_pg_core.integer)("status_id").notNull().references(() => status.id, { onDelete: "no action" }),
|
|
3719
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3720
|
+
});
|
|
3721
|
+
const collectors = s.table(EnumTableName.COLLECTORS, {
|
|
3722
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull().references(() => chains$1.chainId, { onDelete: "no action" }),
|
|
3723
|
+
name: (0, drizzle_orm_pg_core.text)("name").$type().notNull(),
|
|
3724
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3725
|
+
epoch: (0, drizzle_orm_pg_core.numeric)("epoch", {
|
|
3726
|
+
precision: 78,
|
|
3727
|
+
scale: 0
|
|
3728
|
+
}).default("0").notNull(),
|
|
3729
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3730
|
+
}, (table) => [(0, drizzle_orm_pg_core.uniqueIndex)("collectors_chain_name_idx").on(table.chainId, table.name)]);
|
|
3731
|
+
const chains$1 = s.table(EnumTableName.CHAINS, {
|
|
3732
|
+
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
3733
|
+
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
3734
|
+
epoch: (0, drizzle_orm_pg_core.numeric)("epoch", {
|
|
3735
|
+
precision: 78,
|
|
3736
|
+
scale: 0
|
|
3737
|
+
}).default("0").notNull(),
|
|
3738
|
+
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
3739
|
+
}, (table) => [(0, drizzle_orm_pg_core.uniqueIndex)("chains_chain_id_idx").on(table.chainId)]);
|
|
3740
|
+
const trees = s.table(EnumTableName.TREES, {
|
|
3741
|
+
root: (0, drizzle_orm_pg_core.varchar)("root", { length: 66 }).primaryKey(),
|
|
3742
|
+
rootSignature: (0, drizzle_orm_pg_core.varchar)("root_signature", { length: 132 }).notNull(),
|
|
3743
|
+
createdAt: (0, drizzle_orm_pg_core.timestamp)("created_at").defaultNow().notNull()
|
|
3744
|
+
});
|
|
3745
|
+
const merklePaths = s.table(EnumTableName.MERKLE_PATHS, {
|
|
3746
|
+
offerHash: (0, drizzle_orm_pg_core.varchar)("offer_hash", { length: 66 }).primaryKey().references(() => offers.hash, { onDelete: "cascade" }),
|
|
3747
|
+
treeRoot: (0, drizzle_orm_pg_core.varchar)("tree_root", { length: 66 }).notNull().references(() => trees.root, { onDelete: "cascade" }),
|
|
3748
|
+
proofNodes: (0, drizzle_orm_pg_core.text)("proof_nodes").notNull(),
|
|
3749
|
+
createdAt: (0, drizzle_orm_pg_core.timestamp)("created_at").defaultNow().notNull()
|
|
3750
|
+
}, (table) => [(0, drizzle_orm_pg_core.index)("merkle_paths_tree_root_idx").on(table.treeRoot)]);
|
|
3751
|
+
|
|
3752
|
+
//#endregion
|
|
3753
|
+
//#region src/indexer/collectors/CollectFunctions/collectConsumedEvents.ts
|
|
3754
|
+
const buildGroupKey = (parameters) => {
|
|
3755
|
+
return `${parameters.chainId}-${parameters.maker.toLowerCase()}-${parameters.group.toLowerCase()}`;
|
|
3756
|
+
};
|
|
3757
|
+
async function* collectConsumedEvents(parameters) {
|
|
3758
|
+
let { db, collector, client, lastBlockNumber: blockNumber, epoch, options: { maxBatchSize = 1e3, blockWindow } = {} } = parameters;
|
|
3759
|
+
const logger = getLogger();
|
|
3760
|
+
let startBlock = blockNumber;
|
|
3761
|
+
let reorgDetected = false;
|
|
3762
|
+
const { blockNumber: latestBlockNumberChain } = await db.blocks.getChain(client.chain.id);
|
|
3763
|
+
const stream = streamLogs({
|
|
3764
|
+
client,
|
|
3765
|
+
contractAddress: client.chain.custom.morpho.address,
|
|
3766
|
+
blockNumberGte: blockNumber,
|
|
3767
|
+
blockNumberLte: latestBlockNumberChain,
|
|
3768
|
+
order: "asc",
|
|
3769
|
+
options: {
|
|
3770
|
+
maxBatchSize,
|
|
3771
|
+
blockWindow
|
|
3772
|
+
}
|
|
3773
|
+
});
|
|
3774
|
+
for await (const { logs, blockNumber: lastStreamBlockNumber } of stream) {
|
|
3775
|
+
const parsedLogs = (0, viem.parseEventLogs)({
|
|
3776
|
+
abi: [consumedEvent, takeEvent],
|
|
3777
|
+
logs,
|
|
3778
|
+
strict: false
|
|
3779
|
+
});
|
|
3780
|
+
const normalizedLogs = [];
|
|
3781
|
+
const groups$3 = /* @__PURE__ */ new Map();
|
|
3782
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
3783
|
+
const recordLog = (log) => {
|
|
3784
|
+
normalizedLogs.push(log);
|
|
3785
|
+
eventIds.add(log.id);
|
|
3786
|
+
const groupKey = buildGroupKey({
|
|
3787
|
+
chainId: log.chainId,
|
|
3788
|
+
maker: log.maker,
|
|
3789
|
+
group: log.group
|
|
3790
|
+
});
|
|
3791
|
+
if (!groups$3.has(groupKey)) groups$3.set(groupKey, {
|
|
3792
|
+
chainId: log.chainId,
|
|
3793
|
+
maker: log.maker.toLowerCase(),
|
|
3794
|
+
group: log.group.toLowerCase()
|
|
3795
|
+
});
|
|
3796
|
+
};
|
|
3797
|
+
for (const rawLog of parsedLogs) {
|
|
3798
|
+
if (rawLog.blockNumber === null || rawLog.logIndex === null || rawLog.transactionHash === null) {
|
|
3799
|
+
logger.debug({
|
|
3800
|
+
collector,
|
|
3801
|
+
chainId: client.chain.id,
|
|
3802
|
+
msg: "Skipping log because it is missing required fields"
|
|
3803
|
+
});
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
if (rawLog.eventName === consumedEvent.name) {
|
|
3807
|
+
const consumeArgs = rawLog.args;
|
|
3808
|
+
if (consumeArgs.user === void 0 || consumeArgs.group === void 0 || consumeArgs.amount === void 0) {
|
|
3809
|
+
logger.debug({
|
|
3810
|
+
collector,
|
|
3811
|
+
chainId: client.chain.id,
|
|
3812
|
+
msg: "Skipping Consume log because it is missing required args"
|
|
3813
|
+
});
|
|
3814
|
+
continue;
|
|
3815
|
+
}
|
|
3816
|
+
recordLog({
|
|
3817
|
+
kind: "consume",
|
|
3818
|
+
id: `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${client.chain.id}-${rawLog.transactionHash}`,
|
|
3819
|
+
chainId: client.chain.id,
|
|
3820
|
+
maker: consumeArgs.user,
|
|
3821
|
+
group: consumeArgs.group,
|
|
3822
|
+
amount: consumeArgs.amount,
|
|
3823
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
3824
|
+
});
|
|
3825
|
+
continue;
|
|
3826
|
+
}
|
|
3827
|
+
if (rawLog.eventName === takeEvent.name) {
|
|
3828
|
+
const takeArgs = rawLog.args;
|
|
3829
|
+
if (takeArgs.maker === void 0 || takeArgs.group === void 0 || takeArgs.consumed === void 0) {
|
|
3830
|
+
logger.debug({
|
|
3831
|
+
collector,
|
|
3832
|
+
chainId: client.chain.id,
|
|
3833
|
+
msg: "Skipping Take log because it is missing required args"
|
|
3834
|
+
});
|
|
3835
|
+
continue;
|
|
3836
|
+
}
|
|
3837
|
+
recordLog({
|
|
3838
|
+
kind: "take",
|
|
3839
|
+
id: `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${client.chain.id}-${rawLog.transactionHash}`,
|
|
3840
|
+
chainId: client.chain.id,
|
|
3841
|
+
maker: takeArgs.maker,
|
|
3842
|
+
group: takeArgs.group,
|
|
3843
|
+
consumed: takeArgs.consumed,
|
|
3844
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
3845
|
+
});
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
await db.transaction(async (dbTx) => {
|
|
3849
|
+
const existingEventIds = /* @__PURE__ */ new Set();
|
|
3850
|
+
if (eventIds.size > 0) {
|
|
3851
|
+
const ids = Array.from(eventIds);
|
|
3852
|
+
for (let index = 0; index < ids.length; index += 500) {
|
|
3853
|
+
const slice = ids.slice(index, index + 500);
|
|
3854
|
+
const { rows } = await dbTx.execute(drizzle_orm.sql`
|
|
3855
|
+
SELECT event_id
|
|
3856
|
+
FROM ${consumedEvents}
|
|
3857
|
+
WHERE event_id IN (${drizzle_orm.sql.join(slice.map((id) => drizzle_orm.sql`${id}`), drizzle_orm.sql`,`)});
|
|
3858
|
+
`);
|
|
3859
|
+
for (const row of rows) existingEventIds.add(row.event_id);
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
const consumedByGroup = /* @__PURE__ */ new Map();
|
|
3863
|
+
if (groups$3.size > 0) {
|
|
3864
|
+
const groupList = Array.from(groups$3.values());
|
|
3865
|
+
for (let index = 0; index < groupList.length; index += 500) {
|
|
3866
|
+
const slice = groupList.slice(index, index + 500);
|
|
3867
|
+
const { rows } = await dbTx.execute(drizzle_orm.sql`
|
|
3868
|
+
WITH targets(chain_id, maker, "group") AS (
|
|
3869
|
+
VALUES ${drizzle_orm.sql.join(slice.map((group) => drizzle_orm.sql`(${group.chainId}::bigint, ${group.maker.toLowerCase()}::varchar(42), ${group.group.toLowerCase()}::varchar(66))`), drizzle_orm.sql`,`)}
|
|
3870
|
+
)
|
|
3871
|
+
SELECT
|
|
3872
|
+
targets.chain_id,
|
|
3873
|
+
targets.maker,
|
|
3874
|
+
targets."group",
|
|
3875
|
+
COALESCE(g.consumed, 0)::numeric AS consumed
|
|
3876
|
+
FROM targets
|
|
3877
|
+
LEFT JOIN ${groups} g
|
|
3878
|
+
ON g.chain_id = targets.chain_id
|
|
3879
|
+
AND g.maker = targets.maker
|
|
3880
|
+
AND g."group" = targets."group";
|
|
3881
|
+
`);
|
|
3882
|
+
for (const row of rows) {
|
|
3883
|
+
const groupKey = buildGroupKey({
|
|
3884
|
+
chainId: Number(row.chain_id),
|
|
3885
|
+
maker: row.maker,
|
|
3886
|
+
group: row.group
|
|
3887
|
+
});
|
|
3888
|
+
consumedByGroup.set(groupKey, BigInt(row.consumed ?? "0"));
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
const events = [];
|
|
3893
|
+
for (const log of normalizedLogs) {
|
|
3894
|
+
if (existingEventIds.has(log.id)) continue;
|
|
3895
|
+
const groupKey = buildGroupKey({
|
|
3896
|
+
chainId: log.chainId,
|
|
3897
|
+
maker: log.maker,
|
|
3898
|
+
group: log.group
|
|
3899
|
+
});
|
|
3900
|
+
const previousConsumed = consumedByGroup.get(groupKey) ?? 0n;
|
|
3901
|
+
if (log.kind === "consume") {
|
|
3902
|
+
events.push({
|
|
3903
|
+
id: log.id,
|
|
3904
|
+
chainId: log.chainId,
|
|
3905
|
+
maker: log.maker,
|
|
3906
|
+
group: log.group,
|
|
3907
|
+
amount: log.amount,
|
|
3908
|
+
blockNumber: log.blockNumber
|
|
3909
|
+
});
|
|
3910
|
+
consumedByGroup.set(groupKey, previousConsumed + log.amount);
|
|
3911
|
+
continue;
|
|
3912
|
+
}
|
|
3913
|
+
const delta = log.consumed - previousConsumed;
|
|
3914
|
+
if (delta <= 0n) {
|
|
3915
|
+
logger.debug({
|
|
3916
|
+
collector,
|
|
3917
|
+
chainId: client.chain.id,
|
|
3918
|
+
msg: "Skipping Take log because consumed did not increase",
|
|
3919
|
+
previous_consumed: previousConsumed.toString(),
|
|
3920
|
+
consumed: log.consumed.toString()
|
|
3921
|
+
});
|
|
3922
|
+
continue;
|
|
3923
|
+
}
|
|
3924
|
+
events.push({
|
|
3925
|
+
id: log.id,
|
|
3926
|
+
chainId: log.chainId,
|
|
3927
|
+
maker: log.maker,
|
|
3928
|
+
group: log.group,
|
|
3929
|
+
amount: delta,
|
|
3930
|
+
blockNumber: log.blockNumber
|
|
3931
|
+
});
|
|
3932
|
+
consumedByGroup.set(groupKey, log.consumed);
|
|
3933
|
+
}
|
|
3934
|
+
try {
|
|
3935
|
+
await dbTx.consumed.create(events);
|
|
3936
|
+
if (events.length > 0) logger.info({
|
|
3937
|
+
msg: `Events indexed`,
|
|
3938
|
+
collector,
|
|
3939
|
+
count: events.length,
|
|
3940
|
+
chain_id: client.chain.id,
|
|
3941
|
+
block_range: [startBlock, lastStreamBlockNumber]
|
|
3942
|
+
});
|
|
3943
|
+
} catch (err) {
|
|
3944
|
+
logger.error({
|
|
3945
|
+
err,
|
|
3946
|
+
msg: "Failed to process consumed events"
|
|
3947
|
+
});
|
|
3948
|
+
}
|
|
3949
|
+
blockNumber = lastStreamBlockNumber;
|
|
3950
|
+
try {
|
|
3951
|
+
await dbTx.blocks.advanceCollector({
|
|
3952
|
+
collectorName: collector,
|
|
3953
|
+
chainId: client.chain.id,
|
|
3954
|
+
blockNumber,
|
|
3955
|
+
epoch
|
|
3956
|
+
});
|
|
3957
|
+
} catch (_) {
|
|
3958
|
+
try {
|
|
3959
|
+
const ancestor = await dbTx.blocks.getCollector({
|
|
3960
|
+
collectorName: collector,
|
|
3961
|
+
chainId: client.chain.id
|
|
3962
|
+
});
|
|
3963
|
+
blockNumber = ancestor.blockNumber;
|
|
3964
|
+
const deleted = await dbTx.consumed.delete({
|
|
3965
|
+
chainId: client.chain.id,
|
|
3966
|
+
blockNumberGte: blockNumber + 1
|
|
3967
|
+
});
|
|
3968
|
+
logger.info({
|
|
3969
|
+
collector,
|
|
3970
|
+
chain_id: client.chain.id,
|
|
3971
|
+
msg: `Reorg detected, events deleted`,
|
|
3972
|
+
count: deleted,
|
|
3973
|
+
block_number: blockNumber
|
|
3974
|
+
});
|
|
3975
|
+
await dbTx.blocks.advanceCollector({
|
|
3976
|
+
collectorName: collector,
|
|
3977
|
+
chainId: client.chain.id,
|
|
3978
|
+
blockNumber,
|
|
3979
|
+
epoch: ancestor.epoch
|
|
3980
|
+
});
|
|
3981
|
+
reorgDetected = true;
|
|
3982
|
+
} catch (err) {
|
|
3983
|
+
const msg = "Failed to delete consumed events when handling reorg.";
|
|
3984
|
+
logger.error({
|
|
3985
|
+
collector,
|
|
3986
|
+
chainId: client.chain.id,
|
|
3987
|
+
msg,
|
|
3988
|
+
err
|
|
3989
|
+
});
|
|
3990
|
+
throw new Error(msg, { cause: err });
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
});
|
|
3994
|
+
if (reorgDetected) return;
|
|
3995
|
+
yield blockNumber;
|
|
3996
|
+
startBlock = blockNumber;
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
//#endregion
|
|
4001
|
+
//#region src/indexer/collectors/CollectFunctions/collectOffers.ts
|
|
4002
|
+
async function* collectOffersV2(parameters) {
|
|
4003
|
+
let { db, collector, client, lastBlockNumber: blockNumber, gatekeeper, options: { maxBatchSize = 1e3, blockWindow } = {} } = parameters;
|
|
4004
|
+
const logger = getLogger();
|
|
4005
|
+
let startBlock = blockNumber;
|
|
4006
|
+
let reorgDetected = false;
|
|
4007
|
+
if (client.chain.custom.morpho.address.toLowerCase() === viem.zeroAddress) {
|
|
4008
|
+
const msg = "Morpho V2 address is zero, signature verification will fail. Please set the Morpho V2 address in the chain configuration.";
|
|
4009
|
+
logger.error({
|
|
4010
|
+
msg,
|
|
4011
|
+
chain_id: client.chain.id
|
|
4012
|
+
});
|
|
4013
|
+
throw new Error(msg);
|
|
4014
|
+
}
|
|
4015
|
+
const signatureDomain = {
|
|
4016
|
+
chainId: client.chain.id,
|
|
4017
|
+
verifyingContract: client.chain.custom.morpho.address
|
|
4018
|
+
};
|
|
4019
|
+
const { blockNumber: latestBlockNumberChain } = await db.blocks.getChain(client.chain.id);
|
|
4020
|
+
const stream = streamLogs({
|
|
4021
|
+
client,
|
|
4022
|
+
contractAddress: client.chain.custom.mempool.address,
|
|
4023
|
+
event: {
|
|
4024
|
+
type: "event",
|
|
4025
|
+
name: "Event",
|
|
4026
|
+
inputs: [{
|
|
4027
|
+
name: "data",
|
|
4028
|
+
type: "bytes",
|
|
4029
|
+
indexed: false,
|
|
4030
|
+
internalType: "bytes"
|
|
4031
|
+
}],
|
|
4032
|
+
anonymous: false
|
|
4033
|
+
},
|
|
4034
|
+
blockNumberGte: blockNumber,
|
|
4035
|
+
blockNumberLte: latestBlockNumberChain,
|
|
4036
|
+
order: "asc",
|
|
4037
|
+
options: {
|
|
4038
|
+
maxBatchSize,
|
|
4039
|
+
blockWindow
|
|
4040
|
+
}
|
|
4041
|
+
});
|
|
4042
|
+
for await (const { logs, blockNumber: lastStreamBlockNumber } of stream) {
|
|
4043
|
+
blockNumber = lastStreamBlockNumber;
|
|
3535
4044
|
const decodedTrees = [];
|
|
3536
4045
|
for (const log of logs) {
|
|
3537
4046
|
if (!log) continue;
|
|
@@ -3617,9 +4126,8 @@ async function* collectOffersV2(parameters) {
|
|
|
3617
4126
|
offers: offersWithBlock,
|
|
3618
4127
|
hashes: insertedHashes
|
|
3619
4128
|
});
|
|
3620
|
-
const { callbacks, positions, lots } =
|
|
4129
|
+
const { callbacks, positions, lots } = decodeCallbacks({
|
|
3621
4130
|
chainId: client.chain.id,
|
|
3622
|
-
gatekeeper,
|
|
3623
4131
|
offers: insertedOffers
|
|
3624
4132
|
});
|
|
3625
4133
|
if (positions.length > 0) await dbTx.positions.upsert(positions);
|
|
@@ -3682,83 +4190,44 @@ async function* collectOffersV2(parameters) {
|
|
|
3682
4190
|
startBlock = blockNumber;
|
|
3683
4191
|
}
|
|
3684
4192
|
}
|
|
3685
|
-
|
|
3686
|
-
const {
|
|
4193
|
+
function decodeCallbacks(parameters) {
|
|
4194
|
+
const { offers } = parameters;
|
|
3687
4195
|
if (offers.length === 0) return {
|
|
3688
4196
|
callbacks: [],
|
|
3689
4197
|
positions: [],
|
|
3690
4198
|
lots: []
|
|
3691
4199
|
};
|
|
3692
|
-
const addresses = offers.filter((entry) => entry.offer.callback.data !== "0x").map((entry) => entry.offer.callback.address);
|
|
3693
|
-
if (addresses.length === 0) return {
|
|
3694
|
-
callbacks: [],
|
|
3695
|
-
positions: [],
|
|
3696
|
-
lots: []
|
|
3697
|
-
};
|
|
3698
|
-
let response;
|
|
3699
|
-
try {
|
|
3700
|
-
response = await gatekeeper.getCallbackTypes({ callbacks: [{
|
|
3701
|
-
chain_id: chainId,
|
|
3702
|
-
addresses
|
|
3703
|
-
}] });
|
|
3704
|
-
} catch (err) {
|
|
3705
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
3706
|
-
throw new Error("Failed to resolve callback types", { cause: error });
|
|
3707
|
-
}
|
|
3708
|
-
const entry = response.find((item) => item.chain_id === chainId);
|
|
3709
|
-
const typeByAddress = /* @__PURE__ */ new Map();
|
|
3710
|
-
if (entry) for (const [key, list] of Object.entries(entry)) {
|
|
3711
|
-
if (key === "chain_id" || key === "not_supported") continue;
|
|
3712
|
-
if (!Array.isArray(list)) continue;
|
|
3713
|
-
for (const address of list) typeByAddress.set(address.toLowerCase(), key);
|
|
3714
|
-
}
|
|
3715
4200
|
const callbacks = [];
|
|
3716
4201
|
const positions = [];
|
|
3717
4202
|
const lots = [];
|
|
3718
4203
|
for (const { offer, blockNumber: offerBlockNumber } of offers) {
|
|
3719
|
-
if (offer.
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
try {
|
|
3724
|
-
decoded = decode$2(callbackType, offer.callback.data);
|
|
3725
|
-
} catch (err) {
|
|
3726
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
3727
|
-
throw new Error("Failed to decode callback data", { cause: error });
|
|
3728
|
-
}
|
|
3729
|
-
if (decoded.length === 0) continue;
|
|
3730
|
-
const offerHash = hash(offer);
|
|
3731
|
-
const callbackInputs = decoded.map((callback) => ({
|
|
4204
|
+
if (!offer.buy) continue;
|
|
4205
|
+
if (!isEmptyCallback(offer)) continue;
|
|
4206
|
+
const loanToken = offer.loanToken.toLowerCase();
|
|
4207
|
+
positions.push(from$12({
|
|
3732
4208
|
chainId: offer.chainId,
|
|
3733
|
-
contract:
|
|
4209
|
+
contract: loanToken,
|
|
3734
4210
|
user: offer.maker,
|
|
3735
|
-
|
|
4211
|
+
type: Type.ERC20,
|
|
4212
|
+
asset: loanToken,
|
|
4213
|
+
blockNumber: offerBlockNumber
|
|
3736
4214
|
}));
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4215
|
+
lots.push({
|
|
4216
|
+
positionChainId: offer.chainId,
|
|
4217
|
+
positionContract: loanToken,
|
|
4218
|
+
positionUser: offer.maker,
|
|
4219
|
+
group: offer.group,
|
|
4220
|
+
size: offer.assets
|
|
3740
4221
|
});
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
const asset = callbackType === Type$1.BuyVaultV1Callback ? void 0 : contract;
|
|
3745
|
-
positions.push(from$12({
|
|
4222
|
+
callbacks.push({
|
|
4223
|
+
offerHash: hash(offer),
|
|
4224
|
+
callbacks: [{
|
|
3746
4225
|
chainId: offer.chainId,
|
|
3747
|
-
contract,
|
|
4226
|
+
contract: loanToken,
|
|
3748
4227
|
user: offer.maker,
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
}));
|
|
3753
|
-
const isLoanPosition = offer.loanToken.toLowerCase() === asset?.toLowerCase();
|
|
3754
|
-
lots.push({
|
|
3755
|
-
positionChainId: offer.chainId,
|
|
3756
|
-
positionContract: contract,
|
|
3757
|
-
positionUser: offer.maker,
|
|
3758
|
-
group: offer.group,
|
|
3759
|
-
size: isLoanPosition ? offer.assets : callback.amount
|
|
3760
|
-
});
|
|
3761
|
-
}
|
|
4228
|
+
amount: offer.assets
|
|
4229
|
+
}]
|
|
4230
|
+
});
|
|
3762
4231
|
}
|
|
3763
4232
|
return {
|
|
3764
4233
|
callbacks,
|
|
@@ -4945,8 +5414,8 @@ const offerExample = {
|
|
|
4945
5414
|
price: "2750000000000000000",
|
|
4946
5415
|
group: "0x000000000000000000000000000000000000000000000000000000000008b8f4",
|
|
4947
5416
|
session: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
4948
|
-
callback: "
|
|
4949
|
-
callback_data: "
|
|
5417
|
+
callback: "0x0000000000000000000000000000000000000000",
|
|
5418
|
+
callback_data: "0x"
|
|
4950
5419
|
},
|
|
4951
5420
|
offer_hash: "0xac4bd8318ec914f89f8af913f162230575b0ac0696a19256bc12138c5cfe1427",
|
|
4952
5421
|
obligation_id: "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc",
|
|
@@ -4998,25 +5467,10 @@ const validateOfferExample = {
|
|
|
4998
5467
|
lltv: "860000000000000000"
|
|
4999
5468
|
}],
|
|
5000
5469
|
callback: {
|
|
5001
|
-
address: "
|
|
5002
|
-
data: "
|
|
5470
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
5471
|
+
data: "0x"
|
|
5003
5472
|
}
|
|
5004
5473
|
};
|
|
5005
|
-
const callbackTypesRequestExample = { callbacks: [{
|
|
5006
|
-
chain_id: 1,
|
|
5007
|
-
addresses: [
|
|
5008
|
-
"0x1111111111111111111111111111111111111111",
|
|
5009
|
-
"0x3333333333333333333333333333333333333333",
|
|
5010
|
-
"0x9999999999999999999999999999999999999999"
|
|
5011
|
-
]
|
|
5012
|
-
}] };
|
|
5013
|
-
const callbackTypesResponseExample = [{
|
|
5014
|
-
chain_id: 1,
|
|
5015
|
-
sell_erc20_callback: ["0x1111111111111111111111111111111111111111"],
|
|
5016
|
-
buy_erc20: ["0x5555555555555555555555555555555555555555"],
|
|
5017
|
-
buy_vault_v1_callback: ["0x3333333333333333333333333333333333333333"],
|
|
5018
|
-
not_supported: ["0x9999999999999999999999999999999999999999"]
|
|
5019
|
-
}];
|
|
5020
5474
|
const routerStatusExample = {
|
|
5021
5475
|
status: "live",
|
|
5022
5476
|
initialized: true,
|
|
@@ -5085,55 +5539,6 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
|
5085
5539
|
type: "string",
|
|
5086
5540
|
example: validateOfferExample.callback.data
|
|
5087
5541
|
})], ValidateCallbackRequest.prototype, "data", void 0);
|
|
5088
|
-
var CallbackTypesChainRequest = class {};
|
|
5089
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5090
|
-
type: "number",
|
|
5091
|
-
example: callbackTypesRequestExample.callbacks[0].chain_id
|
|
5092
|
-
})], CallbackTypesChainRequest.prototype, "chain_id", void 0);
|
|
5093
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5094
|
-
type: () => [String],
|
|
5095
|
-
example: callbackTypesRequestExample.callbacks[0].addresses
|
|
5096
|
-
})], CallbackTypesChainRequest.prototype, "addresses", void 0);
|
|
5097
|
-
var CallbackTypesRequest = class {};
|
|
5098
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5099
|
-
type: () => [CallbackTypesChainRequest],
|
|
5100
|
-
example: callbackTypesRequestExample.callbacks
|
|
5101
|
-
})], CallbackTypesRequest.prototype, "callbacks", void 0);
|
|
5102
|
-
var CallbackTypesChainResponse = class {};
|
|
5103
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5104
|
-
type: "number",
|
|
5105
|
-
example: callbackTypesResponseExample[0].chain_id
|
|
5106
|
-
})], CallbackTypesChainResponse.prototype, "chain_id", void 0);
|
|
5107
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5108
|
-
type: () => [String],
|
|
5109
|
-
required: false,
|
|
5110
|
-
example: callbackTypesResponseExample[0].buy_vault_v1_callback
|
|
5111
|
-
})], CallbackTypesChainResponse.prototype, "buy_vault_v1_callback", void 0);
|
|
5112
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5113
|
-
type: () => [String],
|
|
5114
|
-
required: false,
|
|
5115
|
-
example: callbackTypesResponseExample[0].sell_erc20_callback
|
|
5116
|
-
})], CallbackTypesChainResponse.prototype, "sell_erc20_callback", void 0);
|
|
5117
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5118
|
-
type: () => [String],
|
|
5119
|
-
required: false,
|
|
5120
|
-
example: callbackTypesResponseExample[0].buy_erc20
|
|
5121
|
-
})], CallbackTypesChainResponse.prototype, "buy_erc20", void 0);
|
|
5122
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5123
|
-
type: () => [String],
|
|
5124
|
-
example: callbackTypesResponseExample[0].not_supported
|
|
5125
|
-
})], CallbackTypesChainResponse.prototype, "not_supported", void 0);
|
|
5126
|
-
var CallbackTypesSuccessResponse = class extends SuccessResponse {};
|
|
5127
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5128
|
-
type: "string",
|
|
5129
|
-
nullable: true,
|
|
5130
|
-
example: "maturity:1:1730415600:end_of_next_month"
|
|
5131
|
-
})], CallbackTypesSuccessResponse.prototype, "cursor", void 0);
|
|
5132
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5133
|
-
type: () => [CallbackTypesChainResponse],
|
|
5134
|
-
description: "Callback types grouped by chain.",
|
|
5135
|
-
example: callbackTypesResponseExample
|
|
5136
|
-
})], CallbackTypesSuccessResponse.prototype, "data", void 0);
|
|
5137
5542
|
var AskResponse = class {};
|
|
5138
5543
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5139
5544
|
type: "string",
|
|
@@ -5298,7 +5703,8 @@ var OfferListResponse = class extends SuccessResponse {};
|
|
|
5298
5703
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5299
5704
|
type: "string",
|
|
5300
5705
|
nullable: true,
|
|
5301
|
-
example: offerCursorExample
|
|
5706
|
+
example: offerCursorExample,
|
|
5707
|
+
description: "Pagination cursor. Offer hash (0x...) for maker queries; base64url-encoded cursor for obligation queries."
|
|
5302
5708
|
})], OfferListResponse.prototype, "cursor", void 0);
|
|
5303
5709
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5304
5710
|
type: () => [OfferListItemResponse],
|
|
@@ -5650,7 +6056,7 @@ __decorate([
|
|
|
5650
6056
|
methods: ["post"],
|
|
5651
6057
|
path: "/v1/validate",
|
|
5652
6058
|
summary: "Validate offers",
|
|
5653
|
-
description: "Validates offers against router validation rules. Returns unsigned payload + root on success, or issues only on validation failure."
|
|
6059
|
+
description: "Validates offers against router validation rules. Only empty callbacks (zero address, 0x data) are accepted. Returns unsigned payload + root on success, or issues only on validation failure."
|
|
5654
6060
|
}),
|
|
5655
6061
|
(0, openapi_metadata_decorators.ApiBody)({ type: ValidateOffersRequest }),
|
|
5656
6062
|
(0, openapi_metadata_decorators.ApiResponse)({
|
|
@@ -5669,28 +6075,6 @@ ValidateController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"
|
|
|
5669
6075
|
description: "Bad Request",
|
|
5670
6076
|
type: BadRequestResponse
|
|
5671
6077
|
})], ValidateController);
|
|
5672
|
-
let CallbacksController = class CallbacksController {
|
|
5673
|
-
async resolveCallbackTypes() {}
|
|
5674
|
-
};
|
|
5675
|
-
__decorate([
|
|
5676
|
-
(0, openapi_metadata_decorators.ApiOperation)({
|
|
5677
|
-
methods: ["post"],
|
|
5678
|
-
path: "/v1/callbacks",
|
|
5679
|
-
summary: "Resolve callback types",
|
|
5680
|
-
description: "Returns callback types for callback addresses grouped by chain."
|
|
5681
|
-
}),
|
|
5682
|
-
(0, openapi_metadata_decorators.ApiBody)({ type: CallbackTypesRequest }),
|
|
5683
|
-
(0, openapi_metadata_decorators.ApiResponse)({
|
|
5684
|
-
status: 200,
|
|
5685
|
-
description: "Success",
|
|
5686
|
-
type: CallbackTypesSuccessResponse
|
|
5687
|
-
})
|
|
5688
|
-
], CallbacksController.prototype, "resolveCallbackTypes", null);
|
|
5689
|
-
CallbacksController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"), (0, openapi_metadata_decorators.ApiResponse)({
|
|
5690
|
-
status: 400,
|
|
5691
|
-
description: "Bad Request",
|
|
5692
|
-
type: BadRequestResponse
|
|
5693
|
-
})], CallbacksController);
|
|
5694
6078
|
let OffersController = class OffersController {
|
|
5695
6079
|
async getOffers() {}
|
|
5696
6080
|
};
|
|
@@ -5840,22 +6224,21 @@ const configRulesMaturityExample = {
|
|
|
5840
6224
|
name: "end_of_next_month",
|
|
5841
6225
|
timestamp: 1730415600
|
|
5842
6226
|
};
|
|
5843
|
-
const configRulesCallbackExample = {
|
|
5844
|
-
type: "callback",
|
|
5845
|
-
chain_id: 1,
|
|
5846
|
-
address: "0x1111111111111111111111111111111111111111",
|
|
5847
|
-
callback_type: "sell_erc20_callback"
|
|
5848
|
-
};
|
|
5849
6227
|
const configRulesLoanTokenExample = {
|
|
5850
6228
|
type: "loan_token",
|
|
5851
6229
|
chain_id: 1,
|
|
5852
6230
|
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
|
5853
6231
|
};
|
|
6232
|
+
const configRulesOracleExample = {
|
|
6233
|
+
type: "oracle",
|
|
6234
|
+
chain_id: 1,
|
|
6235
|
+
address: "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83"
|
|
6236
|
+
};
|
|
5854
6237
|
const configRulesChecksumExample = "f1d2d2f924e986ac86fdf7b36c94bcdf";
|
|
5855
6238
|
const configRulesPayloadExample = [
|
|
5856
6239
|
configRulesMaturityExample,
|
|
5857
|
-
|
|
5858
|
-
|
|
6240
|
+
configRulesLoanTokenExample,
|
|
6241
|
+
configRulesOracleExample
|
|
5859
6242
|
];
|
|
5860
6243
|
const configContractNames = [
|
|
5861
6244
|
"mempool",
|
|
@@ -5918,14 +6301,9 @@ __decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
|
5918
6301
|
})], ConfigRulesRuleResponse.prototype, "timestamp", void 0);
|
|
5919
6302
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5920
6303
|
type: "string",
|
|
5921
|
-
example:
|
|
6304
|
+
example: configRulesLoanTokenExample.address,
|
|
5922
6305
|
required: false
|
|
5923
6306
|
})], ConfigRulesRuleResponse.prototype, "address", void 0);
|
|
5924
|
-
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
5925
|
-
type: "string",
|
|
5926
|
-
example: configRulesCallbackExample.callback_type,
|
|
5927
|
-
required: false
|
|
5928
|
-
})], ConfigRulesRuleResponse.prototype, "callback_type", void 0);
|
|
5929
6307
|
var ConfigRulesSuccessResponse = class {};
|
|
5930
6308
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({ type: () => ConfigRulesMeta })], ConfigRulesSuccessResponse.prototype, "meta", void 0);
|
|
5931
6309
|
__decorate([(0, openapi_metadata_decorators.ApiProperty)({
|
|
@@ -5986,7 +6364,7 @@ __decorate([
|
|
|
5986
6364
|
methods: ["get"],
|
|
5987
6365
|
path: "/v1/config/rules",
|
|
5988
6366
|
summary: "Get config rules",
|
|
5989
|
-
description: "Returns configured rules for supported chains."
|
|
6367
|
+
description: "Returns configured rules (maturities, loan tokens, oracles) for supported chains."
|
|
5990
6368
|
}),
|
|
5991
6369
|
(0, openapi_metadata_decorators.ApiQuery)({
|
|
5992
6370
|
name: "cursor",
|
|
@@ -6006,7 +6384,7 @@ __decorate([
|
|
|
6006
6384
|
name: "types",
|
|
6007
6385
|
type: ["string"],
|
|
6008
6386
|
required: false,
|
|
6009
|
-
example: "maturity,loan_token",
|
|
6387
|
+
example: "maturity,loan_token,oracle",
|
|
6010
6388
|
description: "Filter by rule types (comma-separated).",
|
|
6011
6389
|
style: "form",
|
|
6012
6390
|
explode: false
|
|
@@ -6023,1060 +6401,454 @@ __decorate([
|
|
|
6023
6401
|
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6024
6402
|
status: 200,
|
|
6025
6403
|
description: "Success",
|
|
6026
|
-
type: ConfigRulesSuccessResponse
|
|
6027
|
-
})
|
|
6028
|
-
], ConfigRulesController.prototype, "getConfigRules", null);
|
|
6029
|
-
ConfigRulesController = __decorate([(0, openapi_metadata_decorators.ApiTags)("System")], ConfigRulesController);
|
|
6030
|
-
let ObligationsController = class ObligationsController {
|
|
6031
|
-
async getObligations() {}
|
|
6032
|
-
async getObligation() {}
|
|
6033
|
-
};
|
|
6034
|
-
__decorate([
|
|
6035
|
-
(0, openapi_metadata_decorators.ApiOperation)({
|
|
6036
|
-
methods: ["get"],
|
|
6037
|
-
path: "/v1/obligations",
|
|
6038
|
-
summary: "List all obligations",
|
|
6039
|
-
description: "Returns a list of obligations with their current best ask and bid. Obligations are sorted by their id in ascending order by default."
|
|
6040
|
-
}),
|
|
6041
|
-
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6042
|
-
name: "cursor",
|
|
6043
|
-
type: "string",
|
|
6044
|
-
example: obligationCursorExample,
|
|
6045
|
-
description: "Obligation id cursor for pagination."
|
|
6046
|
-
}),
|
|
6047
|
-
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6048
|
-
name: "limit",
|
|
6049
|
-
type: "number",
|
|
6050
|
-
example: 10,
|
|
6051
|
-
description: "Maximum number of obligations to return."
|
|
6052
|
-
}),
|
|
6053
|
-
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6054
|
-
name: "chains",
|
|
6055
|
-
type: ["number"],
|
|
6056
|
-
required: false,
|
|
6057
|
-
example: "1,8453",
|
|
6058
|
-
description: "Filter by chain IDs (comma-separated).",
|
|
6059
|
-
style: "form",
|
|
6060
|
-
explode: false
|
|
6061
|
-
}),
|
|
6062
|
-
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6063
|
-
name: "loan_tokens",
|
|
6064
|
-
type: ["string"],
|
|
6065
|
-
required: false,
|
|
6066
|
-
example: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078,0x34Cf890dB685FC536E05652FB41f02090c3fb751",
|
|
6067
|
-
description: "Filter by loan token addresses (comma-separated).",
|
|
6068
|
-
style: "form",
|
|
6069
|
-
explode: false
|
|
6070
|
-
}),
|
|
6071
|
-
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6072
|
-
name: "collateral_tokens",
|
|
6073
|
-
type: ["string"],
|
|
6074
|
-
required: false,
|
|
6075
|
-
example: "0x34Cf890dB685FC536E05652FB41f02090c3fb751,0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078",
|
|
6076
|
-
description: "Filter by collateral tokens (comma-separated, matches any collateral).",
|
|
6077
|
-
style: "form",
|
|
6078
|
-
explode: false
|
|
6079
|
-
}),
|
|
6080
|
-
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6081
|
-
name: "maturities",
|
|
6082
|
-
type: ["number"],
|
|
6083
|
-
required: false,
|
|
6084
|
-
example: "1761922800,1764524800",
|
|
6085
|
-
description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
|
|
6086
|
-
style: "form",
|
|
6087
|
-
explode: false
|
|
6088
|
-
}),
|
|
6089
|
-
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6090
|
-
status: 200,
|
|
6091
|
-
description: "Success",
|
|
6092
|
-
type: ObligationListResponse
|
|
6093
|
-
})
|
|
6094
|
-
], ObligationsController.prototype, "getObligations", null);
|
|
6095
|
-
__decorate([
|
|
6096
|
-
(0, openapi_metadata_decorators.ApiOperation)({
|
|
6097
|
-
methods: ["get"],
|
|
6098
|
-
path: "/v1/obligations/{obligationId}",
|
|
6099
|
-
summary: "Get an obligation",
|
|
6100
|
-
description: "Returns an obligation by its id."
|
|
6101
|
-
}),
|
|
6102
|
-
(0, openapi_metadata_decorators.ApiParam)({
|
|
6103
|
-
name: "obligationId",
|
|
6104
|
-
type: "string",
|
|
6105
|
-
example: "0x12590ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9f67",
|
|
6106
|
-
description: "Obligation id."
|
|
6107
|
-
}),
|
|
6108
|
-
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6109
|
-
status: 200,
|
|
6110
|
-
description: "Success",
|
|
6111
|
-
type: ObligationSingleSuccessResponse
|
|
6112
|
-
})
|
|
6113
|
-
], ObligationsController.prototype, "getObligation", null);
|
|
6114
|
-
ObligationsController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Markets"), (0, openapi_metadata_decorators.ApiResponse)({
|
|
6115
|
-
status: 400,
|
|
6116
|
-
description: "Bad Request",
|
|
6117
|
-
type: BadRequestResponse
|
|
6118
|
-
})], ObligationsController);
|
|
6119
|
-
let UsersController = class UsersController {
|
|
6120
|
-
async getUserPositions() {}
|
|
6404
|
+
type: ConfigRulesSuccessResponse
|
|
6405
|
+
})
|
|
6406
|
+
], ConfigRulesController.prototype, "getConfigRules", null);
|
|
6407
|
+
ConfigRulesController = __decorate([(0, openapi_metadata_decorators.ApiTags)("System")], ConfigRulesController);
|
|
6408
|
+
let ObligationsController = class ObligationsController {
|
|
6409
|
+
async getObligations() {}
|
|
6410
|
+
async getObligation() {}
|
|
6121
6411
|
};
|
|
6122
6412
|
__decorate([
|
|
6123
6413
|
(0, openapi_metadata_decorators.ApiOperation)({
|
|
6124
6414
|
methods: ["get"],
|
|
6125
|
-
path: "/v1/
|
|
6126
|
-
summary: "
|
|
6127
|
-
description: "Returns
|
|
6128
|
-
}),
|
|
6129
|
-
(0, openapi_metadata_decorators.ApiParam)({
|
|
6130
|
-
name: "userAddress",
|
|
6131
|
-
type: "string",
|
|
6132
|
-
example: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
|
|
6133
|
-
description: "User address to get positions for."
|
|
6415
|
+
path: "/v1/obligations",
|
|
6416
|
+
summary: "List all obligations",
|
|
6417
|
+
description: "Returns a list of obligations with their current best ask and bid. Obligations are sorted by their id in ascending order by default."
|
|
6134
6418
|
}),
|
|
6135
6419
|
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6136
6420
|
name: "cursor",
|
|
6137
6421
|
type: "string",
|
|
6138
|
-
example:
|
|
6139
|
-
description: "
|
|
6422
|
+
example: obligationCursorExample,
|
|
6423
|
+
description: "Obligation id cursor for pagination."
|
|
6140
6424
|
}),
|
|
6141
6425
|
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6142
6426
|
name: "limit",
|
|
6143
6427
|
type: "number",
|
|
6144
6428
|
example: 10,
|
|
6145
|
-
description: "Maximum number of
|
|
6146
|
-
}),
|
|
6147
|
-
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6148
|
-
status: 200,
|
|
6149
|
-
description: "Success",
|
|
6150
|
-
type: PositionListResponse
|
|
6151
|
-
})
|
|
6152
|
-
], UsersController.prototype, "getUserPositions", null);
|
|
6153
|
-
UsersController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"), (0, openapi_metadata_decorators.ApiResponse)({
|
|
6154
|
-
status: 400,
|
|
6155
|
-
description: "Bad Request",
|
|
6156
|
-
type: BadRequestResponse
|
|
6157
|
-
})], UsersController);
|
|
6158
|
-
const OpenApi = async () => {
|
|
6159
|
-
return await (0, openapi_metadata.generateDocument)({
|
|
6160
|
-
controllers: [
|
|
6161
|
-
BooksController,
|
|
6162
|
-
ConfigContractsController,
|
|
6163
|
-
ConfigRulesController,
|
|
6164
|
-
OffersController,
|
|
6165
|
-
ObligationsController,
|
|
6166
|
-
HealthController,
|
|
6167
|
-
UsersController,
|
|
6168
|
-
ValidateController,
|
|
6169
|
-
CallbacksController
|
|
6170
|
-
],
|
|
6171
|
-
document: {
|
|
6172
|
-
openapi: "3.1.0",
|
|
6173
|
-
info: {
|
|
6174
|
-
title: "Router API",
|
|
6175
|
-
version: "1.0.0",
|
|
6176
|
-
description: "API for the Morpho Router"
|
|
6177
|
-
},
|
|
6178
|
-
servers: [{
|
|
6179
|
-
url: "https://router.morpho.dev",
|
|
6180
|
-
description: "Production server"
|
|
6181
|
-
}, {
|
|
6182
|
-
url: "http://localhost:7891",
|
|
6183
|
-
description: "Local development server"
|
|
6184
|
-
}],
|
|
6185
|
-
tags: [
|
|
6186
|
-
{
|
|
6187
|
-
name: "Markets",
|
|
6188
|
-
description: "Read-only endpoints to discover markets, order books and fetch current offers."
|
|
6189
|
-
},
|
|
6190
|
-
{
|
|
6191
|
-
name: "Make",
|
|
6192
|
-
description: "Utilities to ease making offers."
|
|
6193
|
-
},
|
|
6194
|
-
{
|
|
6195
|
-
name: "System",
|
|
6196
|
-
description: "Router configuration and health monitoring."
|
|
6197
|
-
}
|
|
6198
|
-
]
|
|
6199
|
-
}
|
|
6200
|
-
});
|
|
6201
|
-
};
|
|
6202
|
-
|
|
6203
|
-
//#endregion
|
|
6204
|
-
//#region src/api/Schema/PositionResponse.ts
|
|
6205
|
-
var PositionResponse_exports = /* @__PURE__ */ __exportAll({ from: () => from$2 });
|
|
6206
|
-
/**
|
|
6207
|
-
* Creates a `PositionResponse` from a `PositionWithReserved`.
|
|
6208
|
-
* @param position - {@link PositionWithReserved}
|
|
6209
|
-
* @returns The created `PositionResponse`. {@link PositionResponse}
|
|
6210
|
-
*/
|
|
6211
|
-
function from$2(position) {
|
|
6212
|
-
return {
|
|
6213
|
-
chain_id: position.chainId,
|
|
6214
|
-
contract: position.contract,
|
|
6215
|
-
user: position.user,
|
|
6216
|
-
reserved: position.reserved.toString(),
|
|
6217
|
-
block_number: position.blockNumber
|
|
6218
|
-
};
|
|
6219
|
-
}
|
|
6220
|
-
|
|
6221
|
-
//#endregion
|
|
6222
|
-
//#region src/api/Schema/requests.ts
|
|
6223
|
-
const MAX_LIMIT = 100;
|
|
6224
|
-
const DEFAULT_LIMIT$4 = 20;
|
|
6225
|
-
const CONFIG_RULES_MAX_LIMIT = 1e3;
|
|
6226
|
-
const CONFIG_RULES_DEFAULT_LIMIT = 100;
|
|
6227
|
-
const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
|
|
6228
|
-
const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
|
|
6229
|
-
/** Validate cursor is a valid base64url-encoded JSON object.
|
|
6230
|
-
* Domain layer handles semantic validation of cursor fields. */
|
|
6231
|
-
function isValidBase64urlJson(val) {
|
|
6232
|
-
try {
|
|
6233
|
-
const decoded = Buffer.from(val, "base64url").toString("utf8");
|
|
6234
|
-
JSON.parse(decoded);
|
|
6235
|
-
return true;
|
|
6236
|
-
} catch {
|
|
6237
|
-
return false;
|
|
6238
|
-
}
|
|
6239
|
-
}
|
|
6240
|
-
const csvArray = (schema) => zod.preprocess((value) => {
|
|
6241
|
-
if (value === void 0) return void 0;
|
|
6242
|
-
if (Array.isArray(value)) {
|
|
6243
|
-
if (value.some((item) => typeof item !== "string")) return value;
|
|
6244
|
-
return value.flatMap((item) => item.split(",")).map((item) => item.trim()).filter((item) => item.length > 0);
|
|
6245
|
-
}
|
|
6246
|
-
if (typeof value === "string") return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
6247
|
-
return value;
|
|
6248
|
-
}, zod.array(schema)).optional();
|
|
6249
|
-
const PaginationQueryParams = zod.object({
|
|
6250
|
-
cursor: zod.string().optional().refine((val) => {
|
|
6251
|
-
if (!val) return true;
|
|
6252
|
-
return isValidBase64urlJson(val);
|
|
6253
|
-
}, { message: "Invalid cursor format. Must be a valid base64url-encoded cursor object" }).meta({
|
|
6254
|
-
description: "Pagination cursor in base64url-encoded format",
|
|
6255
|
-
example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
|
|
6256
|
-
}),
|
|
6257
|
-
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
|
|
6258
|
-
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
|
|
6259
|
-
example: 10
|
|
6260
|
-
})
|
|
6261
|
-
});
|
|
6262
|
-
const ConfigRuleTypes = zod.enum([
|
|
6263
|
-
"maturity",
|
|
6264
|
-
"callback",
|
|
6265
|
-
"loan_token"
|
|
6266
|
-
]);
|
|
6267
|
-
const GetConfigRulesQueryParams = zod.object({
|
|
6268
|
-
cursor: zod.string().regex(/^(maturity|callback|loan_token):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
|
|
6269
|
-
description: "Pagination cursor in type:chain_id:<value> format",
|
|
6270
|
-
example: "maturity:1:1730415600:end_of_next_month"
|
|
6271
|
-
}),
|
|
6272
|
-
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(CONFIG_RULES_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_RULES_MAX_LIMIT}` })).optional().default(CONFIG_RULES_DEFAULT_LIMIT).meta({
|
|
6273
|
-
description: `Limit maximum: ${CONFIG_RULES_MAX_LIMIT}. Default: ${CONFIG_RULES_DEFAULT_LIMIT}`,
|
|
6274
|
-
example: 100
|
|
6275
|
-
}),
|
|
6276
|
-
types: csvArray(ConfigRuleTypes).meta({
|
|
6277
|
-
description: "Filter by rule types (comma-separated).",
|
|
6278
|
-
example: "maturity,loan_token"
|
|
6279
|
-
}),
|
|
6280
|
-
chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6281
|
-
description: "Filter by chain IDs (comma-separated).",
|
|
6282
|
-
example: "1,8453"
|
|
6283
|
-
})
|
|
6284
|
-
});
|
|
6285
|
-
const GetConfigContractsQueryParams = zod.object({
|
|
6286
|
-
cursor: zod.string().regex(/^[1-9]\d*:0x[a-fA-F0-9]{40}$/, { message: "Cursor must be in the format chain_id:0x..." }).optional().meta({
|
|
6287
|
-
description: "Pagination cursor in chain_id:address format (lowercase address).",
|
|
6288
|
-
example: "1:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
6289
|
-
}),
|
|
6290
|
-
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(CONFIG_CONTRACTS_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_CONTRACTS_MAX_LIMIT}` })).optional().default(CONFIG_CONTRACTS_DEFAULT_LIMIT).meta({
|
|
6291
|
-
description: `Limit maximum: ${CONFIG_CONTRACTS_MAX_LIMIT}. Default: ${CONFIG_CONTRACTS_DEFAULT_LIMIT}`,
|
|
6292
|
-
example: 1e3
|
|
6293
|
-
}),
|
|
6294
|
-
chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6295
|
-
description: "Filter by chain IDs (comma-separated).",
|
|
6296
|
-
example: "1,8453"
|
|
6297
|
-
})
|
|
6298
|
-
});
|
|
6299
|
-
const GetOffersQueryParams = zod.object({
|
|
6300
|
-
...PaginationQueryParams.shape,
|
|
6301
|
-
side: zod.enum(["buy", "sell"]).optional().meta({
|
|
6302
|
-
description: "Side of the offer. Required when using obligation_id.",
|
|
6303
|
-
example: "buy"
|
|
6304
|
-
}),
|
|
6305
|
-
obligation_id: zod.string().regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).optional().meta({
|
|
6306
|
-
description: "Offers obligation id. Required when not using maker.",
|
|
6307
|
-
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6308
|
-
}),
|
|
6309
|
-
maker: zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Maker must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).optional().meta({
|
|
6310
|
-
description: "Maker address to filter offers by. Alternative to obligation_id + side.",
|
|
6311
|
-
example: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
|
|
6312
|
-
})
|
|
6313
|
-
}).superRefine((val, ctx) => {
|
|
6314
|
-
const hasObligation = val.obligation_id !== void 0;
|
|
6315
|
-
const hasSide = val.side !== void 0;
|
|
6316
|
-
const hasMaker = val.maker !== void 0;
|
|
6317
|
-
if (hasMaker && (hasObligation || hasSide)) {
|
|
6318
|
-
ctx.addIssue({
|
|
6319
|
-
code: "custom",
|
|
6320
|
-
message: "Cannot use both maker and obligation_id/side parameters"
|
|
6321
|
-
});
|
|
6322
|
-
return;
|
|
6323
|
-
}
|
|
6324
|
-
if (hasMaker) return;
|
|
6325
|
-
if (!hasObligation || !hasSide) ctx.addIssue({
|
|
6326
|
-
code: "custom",
|
|
6327
|
-
message: "Must provide either maker or both obligation_id and side"
|
|
6328
|
-
});
|
|
6329
|
-
});
|
|
6330
|
-
const GetObligationsQueryParams = zod.object({
|
|
6331
|
-
...PaginationQueryParams.shape,
|
|
6332
|
-
cursor: zod.string().optional().meta({
|
|
6333
|
-
description: "Obligation id cursor",
|
|
6334
|
-
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6429
|
+
description: "Maximum number of obligations to return."
|
|
6335
6430
|
}),
|
|
6336
|
-
|
|
6431
|
+
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6432
|
+
name: "chains",
|
|
6433
|
+
type: ["number"],
|
|
6434
|
+
required: false,
|
|
6435
|
+
example: "1,8453",
|
|
6337
6436
|
description: "Filter by chain IDs (comma-separated).",
|
|
6338
|
-
|
|
6437
|
+
style: "form",
|
|
6438
|
+
explode: false
|
|
6339
6439
|
}),
|
|
6340
|
-
|
|
6440
|
+
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6441
|
+
name: "loan_tokens",
|
|
6442
|
+
type: ["string"],
|
|
6443
|
+
required: false,
|
|
6444
|
+
example: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078,0x34Cf890dB685FC536E05652FB41f02090c3fb751",
|
|
6341
6445
|
description: "Filter by loan token addresses (comma-separated).",
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
collateral_tokens: csvArray(zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Collateral token must be a valid 20-byte address" }).transform((val) => val.toLowerCase())).meta({
|
|
6345
|
-
description: "Filter by collateral tokens (comma-separated, matches any collateral).",
|
|
6346
|
-
example: "0x34Cf890dB685FC536E05652FB41f02090c3fb751,0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078"
|
|
6347
|
-
}),
|
|
6348
|
-
maturities: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Maturity must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6349
|
-
description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
|
|
6350
|
-
example: "1761922800,1764524800"
|
|
6351
|
-
})
|
|
6352
|
-
});
|
|
6353
|
-
const GetObligationParams = zod.object({ obligation_id: zod.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({
|
|
6354
|
-
description: "Obligation id",
|
|
6355
|
-
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6356
|
-
}) });
|
|
6357
|
-
/** Validate a book cursor format: {side, lastPrice, offersCursor} */
|
|
6358
|
-
function isValidBookCursor(cursorString) {
|
|
6359
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
6360
|
-
try {
|
|
6361
|
-
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
6362
|
-
return (v?.side === "buy" || v?.side === "sell") && isNumericString(v?.lastPrice) && (v?.offersCursor === null || typeof v?.offersCursor === "string");
|
|
6363
|
-
} catch {
|
|
6364
|
-
return false;
|
|
6365
|
-
}
|
|
6366
|
-
}
|
|
6367
|
-
const BookPaginationQueryParams = zod.object({
|
|
6368
|
-
cursor: zod.string().optional().refine((value) => {
|
|
6369
|
-
if (!value) return true;
|
|
6370
|
-
return isValidBookCursor(value);
|
|
6371
|
-
}, { message: "Invalid cursor format. Must be a valid base64url-encoded book cursor object" }).meta({
|
|
6372
|
-
description: "Pagination cursor in base64url-encoded format for book levels",
|
|
6373
|
-
example: "eyJzaWRlIjoiYnV5IiwibGFzdFJhdGUiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwib2ZmZXJzQ3Vyc29yIjpudWxsfQ"
|
|
6446
|
+
style: "form",
|
|
6447
|
+
explode: false
|
|
6374
6448
|
}),
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
"1",
|
|
6384
|
-
"0"
|
|
6385
|
-
]).transform((value) => value === "true" || value === "1").optional().meta({
|
|
6386
|
-
description: "Enable strict mode to fail health checks when initialization is incomplete.",
|
|
6387
|
-
example: "true"
|
|
6388
|
-
}) });
|
|
6389
|
-
const GetBookParams = zod.object({
|
|
6390
|
-
...BookPaginationQueryParams.shape,
|
|
6391
|
-
obligation_id: zod.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({
|
|
6392
|
-
description: "Obligation id",
|
|
6393
|
-
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6449
|
+
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6450
|
+
name: "collateral_tokens",
|
|
6451
|
+
type: ["string"],
|
|
6452
|
+
required: false,
|
|
6453
|
+
example: "0x34Cf890dB685FC536E05652FB41f02090c3fb751,0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078",
|
|
6454
|
+
description: "Filter by collateral tokens (comma-separated, matches any collateral).",
|
|
6455
|
+
style: "form",
|
|
6456
|
+
explode: false
|
|
6394
6457
|
}),
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
description: "Chain id.",
|
|
6404
|
-
example: 1
|
|
6458
|
+
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6459
|
+
name: "maturities",
|
|
6460
|
+
type: ["number"],
|
|
6461
|
+
required: false,
|
|
6462
|
+
example: "1761922800,1764524800",
|
|
6463
|
+
description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
|
|
6464
|
+
style: "form",
|
|
6465
|
+
explode: false
|
|
6405
6466
|
}),
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
}).strict()) }).strict();
|
|
6411
|
-
const GetUserPositionsParams = zod.object({
|
|
6412
|
-
...PaginationQueryParams.shape,
|
|
6413
|
-
user_address: zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "User address must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).meta({
|
|
6414
|
-
description: "User address to get positions for",
|
|
6415
|
-
example: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
|
|
6467
|
+
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6468
|
+
status: 200,
|
|
6469
|
+
description: "Success",
|
|
6470
|
+
type: ObligationListResponse
|
|
6416
6471
|
})
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
if (!result.success) return failure(result.error);
|
|
6445
|
-
const query = result.data;
|
|
6446
|
-
try {
|
|
6447
|
-
const { levels, nextCursor } = await db.book.get({
|
|
6448
|
-
side: query.side,
|
|
6449
|
-
obligationId: query.obligation_id,
|
|
6450
|
-
cursor: query.cursor,
|
|
6451
|
-
limit: query.limit
|
|
6452
|
-
});
|
|
6453
|
-
return success({
|
|
6454
|
-
data: levels.map(from$5),
|
|
6455
|
-
cursor: nextCursor
|
|
6456
|
-
});
|
|
6457
|
-
} catch (err) {
|
|
6458
|
-
logger.error({
|
|
6459
|
-
err,
|
|
6460
|
-
msg: "Error get book",
|
|
6461
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
6462
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
6463
|
-
});
|
|
6464
|
-
return failure(err);
|
|
6465
|
-
}
|
|
6466
|
-
}
|
|
6467
|
-
|
|
6468
|
-
//#endregion
|
|
6469
|
-
//#region src/api/Controllers/getConfigContracts.ts
|
|
6470
|
-
const CONFIG_CONTRACT_NAMES = [
|
|
6471
|
-
"mempool",
|
|
6472
|
-
"multicall",
|
|
6473
|
-
"v2"
|
|
6474
|
-
];
|
|
6475
|
-
/**
|
|
6476
|
-
* Returns contract addresses used by indexers (mempool, v2) plus multicall per chain.
|
|
6477
|
-
* @param query - Raw query parameters containing optional chain filters.
|
|
6478
|
-
* @param chainRegistry - The chain registry instance. {@link ChainRegistry.ChainRegistry}
|
|
6479
|
-
* @returns The indexer contract configuration. {@link ApiPayload.Payload<ConfigContract[]>}
|
|
6480
|
-
*/
|
|
6481
|
-
async function getConfigContracts(query, chainRegistry) {
|
|
6482
|
-
const parsed = safeParse("get_config_contracts", query ?? {});
|
|
6483
|
-
if (!parsed.success) return failure(parsed.error);
|
|
6484
|
-
const { chains: chainsFilter, cursor, limit } = parsed.data;
|
|
6485
|
-
const chainFilter = chainsFilter?.length ? new Set(chainsFilter) : null;
|
|
6486
|
-
const contracts = [];
|
|
6487
|
-
const seenAddresses = /* @__PURE__ */ new Set();
|
|
6488
|
-
for (const chain of chainRegistry.list()) {
|
|
6489
|
-
if (chainFilter && !chainFilter.has(chain.id)) continue;
|
|
6490
|
-
const mempool = chain.custom?.mempool?.address;
|
|
6491
|
-
if (!mempool) return failure(new InternalServerError(`Missing mempool address for chain ${chain.id}.`));
|
|
6492
|
-
const multicall = chain.contracts?.multicall3?.address;
|
|
6493
|
-
if (!multicall) return failure(new InternalServerError(`Missing multicall3 address for chain ${chain.id}.`));
|
|
6494
|
-
const v2 = chain.custom?.morpho?.address;
|
|
6495
|
-
if (!v2) return failure(new InternalServerError(`Missing morpho address for chain ${chain.id}.`));
|
|
6496
|
-
const chainContracts = [
|
|
6497
|
-
{
|
|
6498
|
-
chain_id: chain.id,
|
|
6499
|
-
name: "mempool",
|
|
6500
|
-
address: mempool
|
|
6501
|
-
},
|
|
6502
|
-
{
|
|
6503
|
-
chain_id: chain.id,
|
|
6504
|
-
name: "multicall",
|
|
6505
|
-
address: multicall
|
|
6506
|
-
},
|
|
6507
|
-
{
|
|
6508
|
-
chain_id: chain.id,
|
|
6509
|
-
name: "v2",
|
|
6510
|
-
address: v2
|
|
6511
|
-
}
|
|
6512
|
-
];
|
|
6513
|
-
for (const contract of chainContracts) {
|
|
6514
|
-
const cursorKey = `${contract.chain_id}:${contract.address.toLowerCase()}`;
|
|
6515
|
-
if (seenAddresses.has(cursorKey)) return failure(new InternalServerError(`Duplicate contract address ${contract.address} for chain ${chain.id}.`));
|
|
6516
|
-
seenAddresses.add(cursorKey);
|
|
6517
|
-
contracts.push(contract);
|
|
6518
|
-
}
|
|
6519
|
-
}
|
|
6520
|
-
contracts.sort((a, b) => {
|
|
6521
|
-
if (a.chain_id !== b.chain_id) return a.chain_id - b.chain_id;
|
|
6522
|
-
const addressCompare = a.address.toLowerCase().localeCompare(b.address.toLowerCase());
|
|
6523
|
-
if (addressCompare !== 0) return addressCompare;
|
|
6524
|
-
return a.name.localeCompare(b.name);
|
|
6525
|
-
});
|
|
6526
|
-
let cursorContract = null;
|
|
6527
|
-
if (cursor) try {
|
|
6528
|
-
cursorContract = parseCursor$1(cursor);
|
|
6529
|
-
} catch (err) {
|
|
6530
|
-
return failure(err);
|
|
6531
|
-
}
|
|
6532
|
-
const startIndex = cursorContract ? findStartIndex$1(contracts, cursorContract) : 0;
|
|
6533
|
-
const page = contracts.slice(startIndex, startIndex + limit);
|
|
6534
|
-
const nextCursor = startIndex + limit < contracts.length && page.length > 0 ? formatCursor$1(page.at(-1)) : null;
|
|
6535
|
-
return success({
|
|
6536
|
-
data: page,
|
|
6537
|
-
cursor: nextCursor
|
|
6538
|
-
});
|
|
6539
|
-
}
|
|
6540
|
-
function parseCursor$1(cursor) {
|
|
6541
|
-
const [chain, address] = cursor.split(":", 2);
|
|
6542
|
-
if (!chain || !address) throw new BadRequestError("Cursor must be in the format chain_id:0x...");
|
|
6543
|
-
return {
|
|
6544
|
-
chain_id: Number.parseInt(chain, 10),
|
|
6545
|
-
address: address.toLowerCase()
|
|
6546
|
-
};
|
|
6547
|
-
}
|
|
6548
|
-
function formatCursor$1(contract) {
|
|
6549
|
-
return `${contract.chain_id}:${contract.address.toLowerCase()}`;
|
|
6550
|
-
}
|
|
6551
|
-
function findStartIndex$1(contracts, cursor) {
|
|
6552
|
-
let low = 0;
|
|
6553
|
-
let high = contracts.length;
|
|
6554
|
-
while (low < high) {
|
|
6555
|
-
const mid = Math.floor((low + high) / 2);
|
|
6556
|
-
const current = contracts[mid];
|
|
6557
|
-
if (compareContract(current, cursor) <= 0) low = mid + 1;
|
|
6558
|
-
else high = mid;
|
|
6559
|
-
}
|
|
6560
|
-
return low;
|
|
6561
|
-
}
|
|
6562
|
-
function compareContract(contract, cursor) {
|
|
6563
|
-
if (contract.chain_id !== cursor.chain_id) return contract.chain_id - cursor.chain_id;
|
|
6564
|
-
return contract.address.toLowerCase().localeCompare(cursor.address.toLowerCase());
|
|
6565
|
-
}
|
|
6566
|
-
|
|
6567
|
-
//#endregion
|
|
6568
|
-
//#region src/gatekeeper/GateConfig.ts
|
|
6569
|
-
/**
|
|
6570
|
-
* Returns the callback configuration for a given chain and callback type, if it exists.
|
|
6571
|
-
*
|
|
6572
|
-
* @param chain - Chain name for which to read the validation configuration
|
|
6573
|
-
* @param type - Callback type to retrieve
|
|
6574
|
-
* @returns The matching callback configuration or undefined if not configured
|
|
6575
|
-
*/
|
|
6576
|
-
function getCallback(chain, type) {
|
|
6577
|
-
return configs[chain].callbacks?.find((c) => c.type === type);
|
|
6578
|
-
}
|
|
6579
|
-
/**
|
|
6580
|
-
* Attempts to infer the configured callback type from a callback address on a chain.
|
|
6581
|
-
* Skips the empty callback type as it does not carry addresses.
|
|
6582
|
-
*
|
|
6583
|
-
* @param chain - Chain name for which to infer the callback type
|
|
6584
|
-
* @param address - Callback contract address
|
|
6585
|
-
* @returns The callback type when found, otherwise undefined
|
|
6586
|
-
*/
|
|
6587
|
-
function getCallbackType(chain, address) {
|
|
6588
|
-
return configs[chain].callbacks?.find((c) => c.type !== Type$1.BuyWithEmptyCallback && c.addresses.includes(address?.toLowerCase()))?.type;
|
|
6589
|
-
}
|
|
6590
|
-
/**
|
|
6591
|
-
* Returns the list of allowed non-empty callback addresses for a chain.
|
|
6592
|
-
*
|
|
6593
|
-
* @param chain - Chain name
|
|
6594
|
-
* @returns Array of allowed callback addresses (lowercased). Empty when none configured
|
|
6595
|
-
*/
|
|
6596
|
-
const getCallbackAddresses = (chain) => {
|
|
6597
|
-
return configs[chain].callbacks?.filter((c) => c.type !== Type$1.BuyWithEmptyCallback).flatMap((c) => c.addresses) ?? [];
|
|
6598
|
-
};
|
|
6599
|
-
const assets = {
|
|
6600
|
-
[ChainId.ETHEREUM.toString()]: [
|
|
6601
|
-
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
6602
|
-
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
6603
|
-
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
6604
|
-
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
6605
|
-
],
|
|
6606
|
-
[ChainId.BASE.toString()]: [
|
|
6607
|
-
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
6608
|
-
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
|
6609
|
-
"0x4200000000000000000000000000000000000006",
|
|
6610
|
-
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
6611
|
-
],
|
|
6612
|
-
[ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
|
|
6613
|
-
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
6614
|
-
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
6615
|
-
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
6616
|
-
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
6617
|
-
"0xce79ddb3152d52ff8fe65a4c7e058b035fcb560a"
|
|
6618
|
-
],
|
|
6619
|
-
[ChainId.ANVIL.toString()]: [
|
|
6620
|
-
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
6621
|
-
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
6622
|
-
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
6623
|
-
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
6624
|
-
]
|
|
6472
|
+
], ObligationsController.prototype, "getObligations", null);
|
|
6473
|
+
__decorate([
|
|
6474
|
+
(0, openapi_metadata_decorators.ApiOperation)({
|
|
6475
|
+
methods: ["get"],
|
|
6476
|
+
path: "/v1/obligations/{obligationId}",
|
|
6477
|
+
summary: "Get an obligation",
|
|
6478
|
+
description: "Returns an obligation by its id."
|
|
6479
|
+
}),
|
|
6480
|
+
(0, openapi_metadata_decorators.ApiParam)({
|
|
6481
|
+
name: "obligationId",
|
|
6482
|
+
type: "string",
|
|
6483
|
+
example: "0x12590ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9f67",
|
|
6484
|
+
description: "Obligation id."
|
|
6485
|
+
}),
|
|
6486
|
+
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6487
|
+
status: 200,
|
|
6488
|
+
description: "Success",
|
|
6489
|
+
type: ObligationSingleSuccessResponse
|
|
6490
|
+
})
|
|
6491
|
+
], ObligationsController.prototype, "getObligation", null);
|
|
6492
|
+
ObligationsController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Markets"), (0, openapi_metadata_decorators.ApiResponse)({
|
|
6493
|
+
status: 400,
|
|
6494
|
+
description: "Bad Request",
|
|
6495
|
+
type: BadRequestResponse
|
|
6496
|
+
})], ObligationsController);
|
|
6497
|
+
let UsersController = class UsersController {
|
|
6498
|
+
async getUserPositions() {}
|
|
6625
6499
|
};
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6500
|
+
__decorate([
|
|
6501
|
+
(0, openapi_metadata_decorators.ApiOperation)({
|
|
6502
|
+
methods: ["get"],
|
|
6503
|
+
path: "/v1/users/{userAddress}/positions",
|
|
6504
|
+
summary: "Get user positions",
|
|
6505
|
+
description: "Returns positions for a user with reserved balance. The reserved balance is the amount locked by active offers (max lot upper - offset - consumed)."
|
|
6506
|
+
}),
|
|
6507
|
+
(0, openapi_metadata_decorators.ApiParam)({
|
|
6508
|
+
name: "userAddress",
|
|
6509
|
+
type: "string",
|
|
6510
|
+
example: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
|
|
6511
|
+
description: "User address to get positions for."
|
|
6512
|
+
}),
|
|
6513
|
+
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6514
|
+
name: "cursor",
|
|
6515
|
+
type: "string",
|
|
6516
|
+
example: offerCursorExample,
|
|
6517
|
+
description: "Pagination cursor in base64url-encoded format."
|
|
6518
|
+
}),
|
|
6519
|
+
(0, openapi_metadata_decorators.ApiQuery)({
|
|
6520
|
+
name: "limit",
|
|
6521
|
+
type: "number",
|
|
6522
|
+
example: 10,
|
|
6523
|
+
description: "Maximum number of positions to return."
|
|
6524
|
+
}),
|
|
6525
|
+
(0, openapi_metadata_decorators.ApiResponse)({
|
|
6526
|
+
status: 200,
|
|
6527
|
+
description: "Success",
|
|
6528
|
+
type: PositionListResponse
|
|
6529
|
+
})
|
|
6530
|
+
], UsersController.prototype, "getUserPositions", null);
|
|
6531
|
+
UsersController = __decorate([(0, openapi_metadata_decorators.ApiTags)("Make"), (0, openapi_metadata_decorators.ApiResponse)({
|
|
6532
|
+
status: 400,
|
|
6533
|
+
description: "Bad Request",
|
|
6534
|
+
type: BadRequestResponse
|
|
6535
|
+
})], UsersController);
|
|
6536
|
+
const OpenApi = async () => {
|
|
6537
|
+
return await (0, openapi_metadata.generateDocument)({
|
|
6538
|
+
controllers: [
|
|
6539
|
+
BooksController,
|
|
6540
|
+
ConfigContractsController,
|
|
6541
|
+
ConfigRulesController,
|
|
6542
|
+
OffersController,
|
|
6543
|
+
ObligationsController,
|
|
6544
|
+
HealthController,
|
|
6545
|
+
UsersController,
|
|
6546
|
+
ValidateController
|
|
6669
6547
|
],
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
addresses: ["0x3333333333333333333333333333333333333333", "0x4444444444444444444444444444444444444444"],
|
|
6677
|
-
vaultFactories: ["0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101", "0x1897A8997241C1cD4bD0698647e4EB7213535c24"]
|
|
6678
|
-
},
|
|
6679
|
-
{
|
|
6680
|
-
type: Type$1.SellERC20Callback,
|
|
6681
|
-
addresses: ["0x1111111111111111111111111111111111111111", "0x2222222222222222222222222222222222222222"]
|
|
6548
|
+
document: {
|
|
6549
|
+
openapi: "3.1.0",
|
|
6550
|
+
info: {
|
|
6551
|
+
title: "Router API",
|
|
6552
|
+
version: "1.0.0",
|
|
6553
|
+
description: "API for the Morpho Router"
|
|
6682
6554
|
},
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
name: maturityName,
|
|
6705
|
-
timestamp: from$16(maturityName)
|
|
6706
|
-
});
|
|
6707
|
-
const callbacks = config.callbacks ?? [];
|
|
6708
|
-
for (const callback of callbacks) {
|
|
6709
|
-
if (callback.type === Type$1.BuyWithEmptyCallback) continue;
|
|
6710
|
-
if (!("addresses" in callback)) continue;
|
|
6711
|
-
for (const address of callback.addresses) rules.push({
|
|
6712
|
-
type: "callback",
|
|
6713
|
-
chain_id: chain.id,
|
|
6714
|
-
address: normalizeAddress(address),
|
|
6715
|
-
callback_type: callback.type
|
|
6716
|
-
});
|
|
6717
|
-
}
|
|
6718
|
-
const loanTokens = assets[chain.id.toString()] ?? [];
|
|
6719
|
-
for (const address of loanTokens) rules.push({
|
|
6720
|
-
type: "loan_token",
|
|
6721
|
-
chain_id: chain.id,
|
|
6722
|
-
address: normalizeAddress(address)
|
|
6723
|
-
});
|
|
6724
|
-
}
|
|
6725
|
-
rules.sort(compareConfigRules);
|
|
6726
|
-
return rules;
|
|
6727
|
-
}
|
|
6728
|
-
/**
|
|
6729
|
-
* Compute a stable checksum for the provided configured rules.
|
|
6730
|
-
* @param rules - Configured rules to checksum.
|
|
6731
|
-
* @returns MD5 checksum.
|
|
6732
|
-
*/
|
|
6733
|
-
function buildConfigRulesChecksum(rules) {
|
|
6734
|
-
const hash = (0, node_crypto.createHash)("md5");
|
|
6735
|
-
const orderedRules = [...rules].sort(compareConfigRules);
|
|
6736
|
-
for (const rule of orderedRules) {
|
|
6737
|
-
if (rule.type === "maturity") {
|
|
6738
|
-
hash.update(`maturity:${rule.chain_id}:${rule.name}:${rule.timestamp}\n`);
|
|
6739
|
-
continue;
|
|
6740
|
-
}
|
|
6741
|
-
if (rule.type === "callback") {
|
|
6742
|
-
hash.update(`callback:${rule.chain_id}:${rule.callback_type}:${rule.address}\n`);
|
|
6743
|
-
continue;
|
|
6555
|
+
servers: [{
|
|
6556
|
+
url: "https://router.morpho.dev",
|
|
6557
|
+
description: "Production server"
|
|
6558
|
+
}, {
|
|
6559
|
+
url: "http://localhost:7891",
|
|
6560
|
+
description: "Local development server"
|
|
6561
|
+
}],
|
|
6562
|
+
tags: [
|
|
6563
|
+
{
|
|
6564
|
+
name: "Markets",
|
|
6565
|
+
description: "Read-only endpoints to discover markets, order books and fetch current offers."
|
|
6566
|
+
},
|
|
6567
|
+
{
|
|
6568
|
+
name: "Make",
|
|
6569
|
+
description: "Utilities to ease making offers."
|
|
6570
|
+
},
|
|
6571
|
+
{
|
|
6572
|
+
name: "System",
|
|
6573
|
+
description: "Router configuration and health monitoring."
|
|
6574
|
+
}
|
|
6575
|
+
]
|
|
6744
6576
|
}
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
return hash.digest("hex");
|
|
6748
|
-
}
|
|
6749
|
-
function normalizeAddress(address) {
|
|
6750
|
-
return address.toLowerCase();
|
|
6751
|
-
}
|
|
6752
|
-
function compareConfigRules(left, right) {
|
|
6753
|
-
if (left.chain_id !== right.chain_id) return left.chain_id - right.chain_id;
|
|
6754
|
-
if (left.type !== right.type) return left.type.localeCompare(right.type);
|
|
6755
|
-
if (left.type === "maturity" && right.type === "maturity") return left.timestamp - right.timestamp;
|
|
6756
|
-
if (left.type === "callback" && right.type === "callback") {
|
|
6757
|
-
if (left.callback_type !== right.callback_type) return left.callback_type.localeCompare(right.callback_type);
|
|
6758
|
-
return left.address.localeCompare(right.address);
|
|
6759
|
-
}
|
|
6760
|
-
if (left.type === "loan_token" && right.type === "loan_token") return left.address.localeCompare(right.address);
|
|
6761
|
-
return 0;
|
|
6762
|
-
}
|
|
6577
|
+
});
|
|
6578
|
+
};
|
|
6763
6579
|
|
|
6764
6580
|
//#endregion
|
|
6765
|
-
//#region src/api/
|
|
6581
|
+
//#region src/api/Schema/PositionResponse.ts
|
|
6582
|
+
var PositionResponse_exports = /* @__PURE__ */ __exportAll({ from: () => from$2 });
|
|
6766
6583
|
/**
|
|
6767
|
-
*
|
|
6768
|
-
* @param
|
|
6769
|
-
* @
|
|
6770
|
-
* @returns Config rules response payload. {@link ApiPayload.Payload}
|
|
6584
|
+
* Creates a `PositionResponse` from a `PositionWithReserved`.
|
|
6585
|
+
* @param position - {@link PositionWithReserved}
|
|
6586
|
+
* @returns The created `PositionResponse`. {@link PositionResponse}
|
|
6771
6587
|
*/
|
|
6772
|
-
|
|
6773
|
-
const parsed = safeParse("get_config_rules", query ?? {});
|
|
6774
|
-
if (!parsed.success) return failure(parsed.error);
|
|
6775
|
-
const { cursor, limit, types, chains: chainIds } = parsed.data;
|
|
6776
|
-
const typeFilter = types?.length ? new Set(types) : null;
|
|
6777
|
-
const chainFilter = chainIds?.length ? new Set(chainIds) : null;
|
|
6778
|
-
const filteredRules = buildConfigRules(chains).filter((rule) => {
|
|
6779
|
-
if (chainFilter && !chainFilter.has(rule.chain_id)) return false;
|
|
6780
|
-
if (typeFilter && !typeFilter.has(rule.type)) return false;
|
|
6781
|
-
return true;
|
|
6782
|
-
});
|
|
6783
|
-
const checksum = buildConfigRulesChecksum(filteredRules);
|
|
6784
|
-
let cursorRule = null;
|
|
6785
|
-
if (cursor) try {
|
|
6786
|
-
cursorRule = parseCursor(cursor);
|
|
6787
|
-
} catch (err) {
|
|
6788
|
-
return failure(err);
|
|
6789
|
-
}
|
|
6790
|
-
if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError("Cursor type must match requested rule types"));
|
|
6791
|
-
if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError("Cursor chain_id must match requested chains"));
|
|
6792
|
-
const startIndex = cursorRule ? findStartIndex(filteredRules, cursorRule) : 0;
|
|
6793
|
-
const page = filteredRules.slice(startIndex, startIndex + limit);
|
|
6794
|
-
const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor(page.at(-1)) : null;
|
|
6795
|
-
const response = success({
|
|
6796
|
-
data: page,
|
|
6797
|
-
cursor: nextCursor
|
|
6798
|
-
});
|
|
6799
|
-
response.body.meta.checksum = checksum;
|
|
6800
|
-
return response;
|
|
6801
|
-
}
|
|
6802
|
-
function formatCursor(rule) {
|
|
6803
|
-
if (rule.type === "maturity") return `maturity:${rule.chain_id}:${rule.timestamp}:${rule.name}`;
|
|
6804
|
-
if (rule.type === "callback") return `callback:${rule.chain_id}:${rule.callback_type}:${rule.address.toLowerCase()}`;
|
|
6805
|
-
return `loan_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
6806
|
-
}
|
|
6807
|
-
function parseCursor(cursor) {
|
|
6808
|
-
const [type, chain, ...rest] = cursor.split(":");
|
|
6809
|
-
if (!type || !chain || rest.length === 0) throw new BadRequestError("Cursor must be in the format type:chain_id:<value>");
|
|
6810
|
-
if (!isConfigRuleType(type)) throw new BadRequestError("Cursor has an invalid rule type");
|
|
6811
|
-
const chain_id = Number.parseInt(chain, 10);
|
|
6812
|
-
if (!Number.isFinite(chain_id)) throw new BadRequestError("Cursor has an invalid chain_id");
|
|
6813
|
-
if (type === "maturity") {
|
|
6814
|
-
const timestampValue = Number.parseInt(rest[0] ?? "", 10);
|
|
6815
|
-
const nameValue = rest.slice(1).join(":");
|
|
6816
|
-
if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError("Cursor must be in the format maturity:chain_id:timestamp:name");
|
|
6817
|
-
if (!isMaturityType(nameValue)) throw new BadRequestError("Cursor has an invalid maturity name");
|
|
6818
|
-
return {
|
|
6819
|
-
type,
|
|
6820
|
-
chain_id,
|
|
6821
|
-
timestamp: parseMaturity(timestampValue),
|
|
6822
|
-
name: nameValue
|
|
6823
|
-
};
|
|
6824
|
-
}
|
|
6825
|
-
if (type === "callback") {
|
|
6826
|
-
const callbackTypeValue = rest[0] ?? "";
|
|
6827
|
-
const addressValue = rest.slice(1).join(":");
|
|
6828
|
-
if (!callbackTypeValue || !addressValue) throw new BadRequestError("Cursor must be in the format callback:chain_id:callback_type:address");
|
|
6829
|
-
if (!isCallbackType(callbackTypeValue)) throw new BadRequestError("Cursor has an invalid callback type");
|
|
6830
|
-
return {
|
|
6831
|
-
type,
|
|
6832
|
-
chain_id,
|
|
6833
|
-
callback_type: callbackTypeValue,
|
|
6834
|
-
address: parseAddress(addressValue, "Cursor address")
|
|
6835
|
-
};
|
|
6836
|
-
}
|
|
6837
|
-
const addressValue = rest.join(":");
|
|
6838
|
-
if (!addressValue) throw new BadRequestError("Cursor must be in the format loan_token:chain_id:address");
|
|
6588
|
+
function from$2(position) {
|
|
6839
6589
|
return {
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6590
|
+
chain_id: position.chainId,
|
|
6591
|
+
contract: position.contract,
|
|
6592
|
+
user: position.user,
|
|
6593
|
+
reserved: position.reserved.toString(),
|
|
6594
|
+
block_number: position.blockNumber
|
|
6843
6595
|
};
|
|
6844
6596
|
}
|
|
6845
|
-
function findStartIndex(rules, cursor) {
|
|
6846
|
-
let low = 0;
|
|
6847
|
-
let high = rules.length;
|
|
6848
|
-
while (low < high) {
|
|
6849
|
-
const mid = Math.floor((low + high) / 2);
|
|
6850
|
-
const current = rules[mid];
|
|
6851
|
-
if (compareConfigRules(current, cursor) <= 0) low = mid + 1;
|
|
6852
|
-
else high = mid;
|
|
6853
|
-
}
|
|
6854
|
-
return low;
|
|
6855
|
-
}
|
|
6856
|
-
function parseAddress(address, label) {
|
|
6857
|
-
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError(`${label} must be a valid 20-byte address`);
|
|
6858
|
-
return address.toLowerCase();
|
|
6859
|
-
}
|
|
6860
|
-
function isConfigRuleType(value) {
|
|
6861
|
-
return value === "maturity" || value === "callback" || value === "loan_token";
|
|
6862
|
-
}
|
|
6863
|
-
function isMaturityType(value) {
|
|
6864
|
-
return Object.values(MaturityType).includes(value);
|
|
6865
|
-
}
|
|
6866
|
-
function parseMaturity(value) {
|
|
6867
|
-
try {
|
|
6868
|
-
return from$16(value);
|
|
6869
|
-
} catch (err) {
|
|
6870
|
-
throw new BadRequestError(err instanceof Error ? err.message : "Invalid maturity timestamp");
|
|
6871
|
-
}
|
|
6872
|
-
}
|
|
6873
|
-
function isCallbackType(value) {
|
|
6874
|
-
if (value === Type$1.BuyWithEmptyCallback) return false;
|
|
6875
|
-
return Object.values(Type$1).includes(value);
|
|
6876
|
-
}
|
|
6877
6597
|
|
|
6878
6598
|
//#endregion
|
|
6879
|
-
//#region src/api/
|
|
6880
|
-
const
|
|
6599
|
+
//#region src/api/Schema/requests.ts
|
|
6600
|
+
const MAX_LIMIT = 100;
|
|
6601
|
+
const DEFAULT_LIMIT$4 = 20;
|
|
6602
|
+
const CONFIG_RULES_MAX_LIMIT = 1e3;
|
|
6603
|
+
const CONFIG_RULES_DEFAULT_LIMIT = 100;
|
|
6604
|
+
const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
|
|
6605
|
+
const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
|
|
6606
|
+
/** Validate cursor is a valid base64url-encoded JSON object.
|
|
6607
|
+
* Domain layer handles semantic validation of cursor fields. */
|
|
6608
|
+
function isValidBase64urlJson(val) {
|
|
6881
6609
|
try {
|
|
6882
|
-
|
|
6610
|
+
const decoded = Buffer.from(val, "base64url").toString("utf8");
|
|
6611
|
+
JSON.parse(decoded);
|
|
6612
|
+
return true;
|
|
6883
6613
|
} catch {
|
|
6884
|
-
return
|
|
6614
|
+
return false;
|
|
6885
6615
|
}
|
|
6886
|
-
})();
|
|
6887
|
-
/**
|
|
6888
|
-
* Build the OpenAPI document for the router.
|
|
6889
|
-
* @returns OpenAPI document. {@link OpenAPIDocument}
|
|
6890
|
-
*/
|
|
6891
|
-
async function getSwaggerJson() {
|
|
6892
|
-
return OpenApi();
|
|
6893
|
-
}
|
|
6894
|
-
/**
|
|
6895
|
-
* Render the API documentation HTML page.
|
|
6896
|
-
* @returns HTML page as string.
|
|
6897
|
-
*/
|
|
6898
|
-
async function getDocsHtml() {
|
|
6899
|
-
const spec = await OpenApi();
|
|
6900
|
-
return `<!DOCTYPE html>
|
|
6901
|
-
<html>
|
|
6902
|
-
<head>
|
|
6903
|
-
<meta charset="UTF-8">
|
|
6904
|
-
<title>Router API Docs (Scalar)</title>
|
|
6905
|
-
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"><\/script>
|
|
6906
|
-
<style>
|
|
6907
|
-
html, body { margin: 0; height: 100%; }
|
|
6908
|
-
api-reference { height: 100%; width: 100%; }
|
|
6909
|
-
</style>
|
|
6910
|
-
</head>
|
|
6911
|
-
<body>
|
|
6912
|
-
<div id="api-container" style="height:100%;width:100%;"></div>
|
|
6913
|
-
<script>
|
|
6914
|
-
window.addEventListener('load', function () {
|
|
6915
|
-
const spec = ${JSON.stringify(spec)};
|
|
6916
|
-
Scalar.createApiReference('#api-container', { spec: { content: spec, hideModels: true } });
|
|
6917
|
-
});
|
|
6918
|
-
<\/script>
|
|
6919
|
-
</body>
|
|
6920
|
-
</html>`;
|
|
6921
|
-
}
|
|
6922
|
-
/**
|
|
6923
|
-
* Finds the integrator.md file.
|
|
6924
|
-
* Handles source, bundled CLI, and Lambda scenarios.
|
|
6925
|
-
*/
|
|
6926
|
-
function findIntegratorMd() {
|
|
6927
|
-
const candidates = [
|
|
6928
|
-
(0, node_path.resolve)(__dirname$1, "../../../docs/integrator.md"),
|
|
6929
|
-
(0, node_path.resolve)(__dirname$1, "../docs/integrator.md"),
|
|
6930
|
-
(0, node_path.resolve)(process.cwd(), "docs/integrator.md")
|
|
6931
|
-
];
|
|
6932
|
-
for (const candidate of candidates) if ((0, node_fs.existsSync)(candidate)) return candidate;
|
|
6933
|
-
throw new Error(`integrator.md not found. Tried: ${candidates.join(", ")}`);
|
|
6934
6616
|
}
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
* @returns HTML page with the rendered markdown documentation.
|
|
6938
|
-
*/
|
|
6939
|
-
async function getIntegratorDocsHtml() {
|
|
6940
|
-
return `<!DOCTYPE html>
|
|
6941
|
-
<html>
|
|
6942
|
-
<head>
|
|
6943
|
-
<meta charset="UTF-8">
|
|
6944
|
-
<title>Documentation</title>
|
|
6945
|
-
<style>
|
|
6946
|
-
body { font-family: system-ui, sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.6; }
|
|
6947
|
-
pre { background: #f4f4f4; padding: 1rem; overflow-x: auto; }
|
|
6948
|
-
code { background: #f4f4f4; padding: 0.2rem 0.4rem; }
|
|
6949
|
-
a { color: #0066cc; }
|
|
6950
|
-
</style>
|
|
6951
|
-
</head>
|
|
6952
|
-
<body>
|
|
6953
|
-
<nav><a href="/docs/api">API Reference →</a></nav>
|
|
6954
|
-
${await (0, marked.marked)(await (0, node_fs_promises.readFile)(findIntegratorMd(), "utf-8"))}
|
|
6955
|
-
</body>
|
|
6956
|
-
</html>`;
|
|
6617
|
+
function isValidOfferHashCursor(val) {
|
|
6618
|
+
return /^0x[a-f0-9]{64}$/i.test(val);
|
|
6957
6619
|
}
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
try {
|
|
6964
|
-
const parsed = safeParse("get_health", query);
|
|
6965
|
-
if (!parsed.success) return failure(parsed.error);
|
|
6966
|
-
const snapshot = await create$16({
|
|
6967
|
-
db,
|
|
6968
|
-
chainRegistry
|
|
6969
|
-
}).getSnapshot();
|
|
6970
|
-
if (parsed.data.strict && !snapshot.initialized) return failure(new APIError(STATUS_CODE.INTERNAL_SERVER_ERROR, "Indexer block state is not initialized", "INTERNAL_SERVER_ERROR", toSnakeCase$1({
|
|
6971
|
-
missingChains: snapshot.missingChains,
|
|
6972
|
-
missingCollectors: snapshot.missingCollectors
|
|
6973
|
-
})));
|
|
6974
|
-
return success({ data: toSnakeCase$1({
|
|
6975
|
-
status: snapshot.status,
|
|
6976
|
-
initialized: snapshot.initialized,
|
|
6977
|
-
missingChains: snapshot.missingChains,
|
|
6978
|
-
missingCollectors: snapshot.missingCollectors
|
|
6979
|
-
}) });
|
|
6980
|
-
} catch (err) {
|
|
6981
|
-
logger.error({
|
|
6982
|
-
err,
|
|
6983
|
-
msg: "Error getting health status",
|
|
6984
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
6985
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
6986
|
-
});
|
|
6987
|
-
return failure(err);
|
|
6620
|
+
const csvArray = (schema) => zod.preprocess((value) => {
|
|
6621
|
+
if (value === void 0) return void 0;
|
|
6622
|
+
if (Array.isArray(value)) {
|
|
6623
|
+
if (value.some((item) => typeof item !== "string")) return value;
|
|
6624
|
+
return value.flatMap((item) => item.split(",")).map((item) => item.trim()).filter((item) => item.length > 0);
|
|
6988
6625
|
}
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
if (!
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
6626
|
+
if (typeof value === "string") return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
6627
|
+
return value;
|
|
6628
|
+
}, zod.array(schema)).optional();
|
|
6629
|
+
const PaginationQueryParams = zod.object({
|
|
6630
|
+
cursor: zod.string().optional().refine((val) => {
|
|
6631
|
+
if (!val) return true;
|
|
6632
|
+
return isValidBase64urlJson(val);
|
|
6633
|
+
}, { message: "Invalid cursor format. Must be a valid base64url-encoded cursor object" }).meta({
|
|
6634
|
+
description: "Pagination cursor in base64url-encoded format",
|
|
6635
|
+
example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
|
|
6636
|
+
}),
|
|
6637
|
+
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
|
|
6638
|
+
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
|
|
6639
|
+
example: 10
|
|
6640
|
+
})
|
|
6641
|
+
});
|
|
6642
|
+
const ConfigRuleTypes = zod.enum([
|
|
6643
|
+
"maturity",
|
|
6644
|
+
"callback",
|
|
6645
|
+
"loan_token",
|
|
6646
|
+
"oracle"
|
|
6647
|
+
]);
|
|
6648
|
+
const GetConfigRulesQueryParams = zod.object({
|
|
6649
|
+
cursor: zod.string().regex(/^(maturity|callback|loan_token|oracle):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
|
|
6650
|
+
description: "Pagination cursor in type:chain_id:<value> format",
|
|
6651
|
+
example: "maturity:1:1730415600:end_of_next_month"
|
|
6652
|
+
}),
|
|
6653
|
+
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(CONFIG_RULES_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_RULES_MAX_LIMIT}` })).optional().default(CONFIG_RULES_DEFAULT_LIMIT).meta({
|
|
6654
|
+
description: `Limit maximum: ${CONFIG_RULES_MAX_LIMIT}. Default: ${CONFIG_RULES_DEFAULT_LIMIT}`,
|
|
6655
|
+
example: 100
|
|
6656
|
+
}),
|
|
6657
|
+
types: csvArray(ConfigRuleTypes).meta({
|
|
6658
|
+
description: "Filter by rule types (comma-separated).",
|
|
6659
|
+
example: "maturity,loan_token,oracle"
|
|
6660
|
+
}),
|
|
6661
|
+
chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6662
|
+
description: "Filter by chain IDs (comma-separated).",
|
|
6663
|
+
example: "1,8453"
|
|
6664
|
+
})
|
|
6665
|
+
});
|
|
6666
|
+
const GetConfigContractsQueryParams = zod.object({
|
|
6667
|
+
cursor: zod.string().regex(/^[1-9]\d*:0x[a-fA-F0-9]{40}$/, { message: "Cursor must be in the format chain_id:0x..." }).optional().meta({
|
|
6668
|
+
description: "Pagination cursor in chain_id:address format (lowercase address).",
|
|
6669
|
+
example: "1:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
6670
|
+
}),
|
|
6671
|
+
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(CONFIG_CONTRACTS_MAX_LIMIT, { message: `Limit cannot exceed ${CONFIG_CONTRACTS_MAX_LIMIT}` })).optional().default(CONFIG_CONTRACTS_DEFAULT_LIMIT).meta({
|
|
6672
|
+
description: `Limit maximum: ${CONFIG_CONTRACTS_MAX_LIMIT}. Default: ${CONFIG_CONTRACTS_DEFAULT_LIMIT}`,
|
|
6673
|
+
example: 1e3
|
|
6674
|
+
}),
|
|
6675
|
+
chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6676
|
+
description: "Filter by chain IDs (comma-separated).",
|
|
6677
|
+
example: "1,8453"
|
|
6678
|
+
})
|
|
6679
|
+
});
|
|
6680
|
+
const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend({
|
|
6681
|
+
cursor: zod.string().optional().meta({
|
|
6682
|
+
description: "Pagination cursor. Use offer hash (0x...) for maker queries, base64url for obligation queries.",
|
|
6683
|
+
example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
|
|
6684
|
+
}),
|
|
6685
|
+
side: zod.enum(["buy", "sell"]).optional().meta({
|
|
6686
|
+
description: "Side of the offer. Required when using obligation_id.",
|
|
6687
|
+
example: "buy"
|
|
6688
|
+
}),
|
|
6689
|
+
obligation_id: zod.string().regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).optional().meta({
|
|
6690
|
+
description: "Offers obligation id. Required when not using maker.",
|
|
6691
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6692
|
+
}),
|
|
6693
|
+
maker: zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Maker must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).optional().meta({
|
|
6694
|
+
description: "Maker address to filter offers by. Alternative to obligation_id + side.",
|
|
6695
|
+
example: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
|
|
6696
|
+
})
|
|
6697
|
+
}).superRefine((val, ctx) => {
|
|
6698
|
+
const hasObligation = val.obligation_id !== void 0;
|
|
6699
|
+
const hasSide = val.side !== void 0;
|
|
6700
|
+
const hasMaker = val.maker !== void 0;
|
|
6701
|
+
if (hasMaker && (hasObligation || hasSide)) {
|
|
6702
|
+
ctx.addIssue({
|
|
6703
|
+
code: "custom",
|
|
6704
|
+
message: "Cannot use both maker and obligation_id/side parameters"
|
|
7018
6705
|
});
|
|
7019
|
-
return
|
|
6706
|
+
return;
|
|
7020
6707
|
}
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
if (!parsed.success) return failure(parsed.error);
|
|
7027
|
-
const snapshot = await create$16({
|
|
7028
|
-
db,
|
|
7029
|
-
chainRegistry
|
|
7030
|
-
}).getSnapshot();
|
|
7031
|
-
if (parsed.data.strict && !snapshot.initialized) return failure(new APIError(STATUS_CODE.INTERNAL_SERVER_ERROR, "Indexer block state is not initialized", "INTERNAL_SERVER_ERROR", toSnakeCase$1({
|
|
7032
|
-
missingChains: snapshot.missingChains,
|
|
7033
|
-
missingCollectors: snapshot.missingCollectors
|
|
7034
|
-
})));
|
|
7035
|
-
const collectors = snapshot.collectors;
|
|
7036
|
-
return success({ data: collectors.map(({ name, chainId, blockNumber, updatedAt, lag, status, initialized }) => toSnakeCase$1({
|
|
7037
|
-
name,
|
|
7038
|
-
chainId,
|
|
7039
|
-
blockNumber,
|
|
7040
|
-
updatedAt,
|
|
7041
|
-
lag,
|
|
7042
|
-
status,
|
|
7043
|
-
initialized
|
|
7044
|
-
})) });
|
|
7045
|
-
} catch (err) {
|
|
7046
|
-
logger.error({
|
|
7047
|
-
err,
|
|
7048
|
-
msg: "Error getting health status for collectors",
|
|
7049
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7050
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
6708
|
+
if (hasMaker) {
|
|
6709
|
+
if (val.cursor !== void 0 && !isValidOfferHashCursor(val.cursor)) ctx.addIssue({
|
|
6710
|
+
code: "custom",
|
|
6711
|
+
path: ["cursor"],
|
|
6712
|
+
message: "Cursor must be a 32-byte hex offer hash when filtering by maker"
|
|
7051
6713
|
});
|
|
7052
|
-
return
|
|
6714
|
+
return;
|
|
6715
|
+
}
|
|
6716
|
+
if (!hasObligation || !hasSide) ctx.addIssue({
|
|
6717
|
+
code: "custom",
|
|
6718
|
+
message: "Must provide either maker or both obligation_id and side"
|
|
6719
|
+
});
|
|
6720
|
+
if (val.cursor !== void 0 && !isValidBase64urlJson(val.cursor)) ctx.addIssue({
|
|
6721
|
+
code: "custom",
|
|
6722
|
+
path: ["cursor"],
|
|
6723
|
+
message: "Invalid cursor format. Must be a valid base64url-encoded cursor object"
|
|
6724
|
+
});
|
|
6725
|
+
}).transform((val) => {
|
|
6726
|
+
if (val.maker && val.cursor) return {
|
|
6727
|
+
...val,
|
|
6728
|
+
cursor: val.cursor.toLowerCase()
|
|
6729
|
+
};
|
|
6730
|
+
return val;
|
|
6731
|
+
});
|
|
6732
|
+
const GetObligationsQueryParams = zod.object({
|
|
6733
|
+
...PaginationQueryParams.shape,
|
|
6734
|
+
cursor: zod.string().optional().meta({
|
|
6735
|
+
description: "Obligation id cursor",
|
|
6736
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6737
|
+
}),
|
|
6738
|
+
chains: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6739
|
+
description: "Filter by chain IDs (comma-separated).",
|
|
6740
|
+
example: "1,8453"
|
|
6741
|
+
}),
|
|
6742
|
+
loan_tokens: csvArray(zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Loan token must be a valid 20-byte address" }).transform((val) => val.toLowerCase())).meta({
|
|
6743
|
+
description: "Filter by loan token addresses (comma-separated).",
|
|
6744
|
+
example: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078,0x34Cf890dB685FC536E05652FB41f02090c3fb751"
|
|
6745
|
+
}),
|
|
6746
|
+
collateral_tokens: csvArray(zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "Collateral token must be a valid 20-byte address" }).transform((val) => val.toLowerCase())).meta({
|
|
6747
|
+
description: "Filter by collateral tokens (comma-separated, matches any collateral).",
|
|
6748
|
+
example: "0x34Cf890dB685FC536E05652FB41f02090c3fb751,0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078"
|
|
6749
|
+
}),
|
|
6750
|
+
maturities: csvArray(zod.string().regex(/^[1-9]\d*$/, { message: "Maturity must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
6751
|
+
description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
|
|
6752
|
+
example: "1761922800,1764524800"
|
|
6753
|
+
})
|
|
6754
|
+
});
|
|
6755
|
+
const GetObligationParams = zod.object({ obligation_id: zod.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({
|
|
6756
|
+
description: "Obligation id",
|
|
6757
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6758
|
+
}) });
|
|
6759
|
+
/** Validate a book cursor format: {side, lastPrice, offersCursor} */
|
|
6760
|
+
function isValidBookCursor(cursorString) {
|
|
6761
|
+
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
6762
|
+
try {
|
|
6763
|
+
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
6764
|
+
return (v?.side === "buy" || v?.side === "sell") && isNumericString(v?.lastPrice) && (v?.offersCursor === null || typeof v?.offersCursor === "string");
|
|
6765
|
+
} catch {
|
|
6766
|
+
return false;
|
|
7053
6767
|
}
|
|
7054
6768
|
}
|
|
6769
|
+
const BookPaginationQueryParams = zod.object({
|
|
6770
|
+
cursor: zod.string().optional().refine((value) => {
|
|
6771
|
+
if (!value) return true;
|
|
6772
|
+
return isValidBookCursor(value);
|
|
6773
|
+
}, { message: "Invalid cursor format. Must be a valid base64url-encoded book cursor object" }).meta({
|
|
6774
|
+
description: "Pagination cursor in base64url-encoded format for book levels",
|
|
6775
|
+
example: "eyJzaWRlIjoiYnV5IiwibGFzdFJhdGUiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwib2ZmZXJzQ3Vyc29yIjpudWxsfQ"
|
|
6776
|
+
}),
|
|
6777
|
+
limit: zod.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(zod.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$4).meta({
|
|
6778
|
+
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$4}`,
|
|
6779
|
+
example: 10
|
|
6780
|
+
})
|
|
6781
|
+
});
|
|
6782
|
+
const HealthQueryParams = zod.object({ strict: zod.enum([
|
|
6783
|
+
"true",
|
|
6784
|
+
"false",
|
|
6785
|
+
"1",
|
|
6786
|
+
"0"
|
|
6787
|
+
]).transform((value) => value === "true" || value === "1").optional().meta({
|
|
6788
|
+
description: "Enable strict mode to fail health checks when initialization is incomplete.",
|
|
6789
|
+
example: "true"
|
|
6790
|
+
}) });
|
|
6791
|
+
const GetBookParams = zod.object({
|
|
6792
|
+
...BookPaginationQueryParams.shape,
|
|
6793
|
+
obligation_id: zod.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({
|
|
6794
|
+
description: "Obligation id",
|
|
6795
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
6796
|
+
}),
|
|
6797
|
+
side: zod.enum(["buy", "sell"]).meta({
|
|
6798
|
+
description: "Side of the book (buy or sell).",
|
|
6799
|
+
example: "buy"
|
|
6800
|
+
})
|
|
6801
|
+
});
|
|
6802
|
+
const ValidateOffersBody = zod.object({ offers: zod.array(zod.unknown()).min(1, { message: "'offers' must contain at least 1 offer" }) }).strict();
|
|
6803
|
+
const GetUserPositionsParams = zod.object({
|
|
6804
|
+
...PaginationQueryParams.shape,
|
|
6805
|
+
user_address: zod.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "User address must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).meta({
|
|
6806
|
+
description: "User address to get positions for",
|
|
6807
|
+
example: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
|
|
6808
|
+
})
|
|
6809
|
+
});
|
|
6810
|
+
const schemas = {
|
|
6811
|
+
get_health: HealthQueryParams,
|
|
6812
|
+
get_health_collectors: HealthQueryParams,
|
|
6813
|
+
get_health_chains: HealthQueryParams,
|
|
6814
|
+
get_config_contracts: GetConfigContractsQueryParams,
|
|
6815
|
+
get_config_rules: GetConfigRulesQueryParams,
|
|
6816
|
+
get_offers: GetOffersQueryParams,
|
|
6817
|
+
get_obligations: GetObligationsQueryParams,
|
|
6818
|
+
get_obligation: GetObligationParams,
|
|
6819
|
+
get_book: GetBookParams,
|
|
6820
|
+
validate_offers: ValidateOffersBody,
|
|
6821
|
+
get_user_positions: GetUserPositionsParams
|
|
6822
|
+
};
|
|
6823
|
+
function parse(action, query) {
|
|
6824
|
+
return schemas[action].parse(query);
|
|
6825
|
+
}
|
|
6826
|
+
function safeParse(action, query, error) {
|
|
6827
|
+
return schemas[action].safeParse(query, { error });
|
|
6828
|
+
}
|
|
7055
6829
|
|
|
7056
6830
|
//#endregion
|
|
7057
|
-
//#region src/api/Controllers/
|
|
7058
|
-
async function
|
|
6831
|
+
//#region src/api/Controllers/getBook.ts
|
|
6832
|
+
async function getBook(params, db) {
|
|
7059
6833
|
const logger = getLogger();
|
|
7060
|
-
const result = safeParse("
|
|
6834
|
+
const result = safeParse("get_book", params, (issue) => issue.message);
|
|
7061
6835
|
if (!result.success) return failure(result.error);
|
|
7062
6836
|
const query = result.data;
|
|
7063
6837
|
try {
|
|
7064
|
-
const {
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
6838
|
+
const { levels, nextCursor } = await db.book.get({
|
|
6839
|
+
side: query.side,
|
|
6840
|
+
obligationId: query.obligation_id,
|
|
6841
|
+
cursor: query.cursor,
|
|
6842
|
+
limit: query.limit
|
|
6843
|
+
});
|
|
7068
6844
|
return success({
|
|
7069
|
-
data: from$
|
|
7070
|
-
|
|
7071
|
-
ask: { price: 0n },
|
|
7072
|
-
bid: { price: 0n }
|
|
7073
|
-
}),
|
|
7074
|
-
cursor: null
|
|
6845
|
+
data: levels.map(from$5),
|
|
6846
|
+
cursor: nextCursor
|
|
7075
6847
|
});
|
|
7076
6848
|
} catch (err) {
|
|
7077
6849
|
logger.error({
|
|
7078
6850
|
err,
|
|
7079
|
-
msg: "Error get
|
|
6851
|
+
msg: "Error get book",
|
|
7080
6852
|
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7081
6853
|
errorStack: err instanceof Error ? err.stack : void 0
|
|
7082
6854
|
});
|
|
@@ -7085,463 +6857,649 @@ async function getObligation(params, db) {
|
|
|
7085
6857
|
}
|
|
7086
6858
|
|
|
7087
6859
|
//#endregion
|
|
7088
|
-
//#region src/api/Controllers/
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
6860
|
+
//#region src/api/Controllers/getConfigContracts.ts
|
|
6861
|
+
const CONFIG_CONTRACT_NAMES = [
|
|
6862
|
+
"mempool",
|
|
6863
|
+
"multicall",
|
|
6864
|
+
"v2"
|
|
6865
|
+
];
|
|
6866
|
+
/**
|
|
6867
|
+
* Returns contract addresses used by indexers (mempool, v2) plus multicall per chain.
|
|
6868
|
+
* @param query - Raw query parameters containing optional chain filters.
|
|
6869
|
+
* @param chainRegistry - The chain registry instance. {@link ChainRegistry.ChainRegistry}
|
|
6870
|
+
* @returns The indexer contract configuration. {@link ApiPayload.Payload<ConfigContract[]>}
|
|
6871
|
+
*/
|
|
6872
|
+
async function getConfigContracts(query, chainRegistry) {
|
|
6873
|
+
const parsed = safeParse("get_config_contracts", query ?? {});
|
|
6874
|
+
if (!parsed.success) return failure(parsed.error);
|
|
6875
|
+
const { chains: chainsFilter, cursor, limit } = parsed.data;
|
|
6876
|
+
const chainFilter = chainsFilter?.length ? new Set(chainsFilter) : null;
|
|
6877
|
+
const contracts = [];
|
|
6878
|
+
const seenAddresses = /* @__PURE__ */ new Set();
|
|
6879
|
+
for (const chain of chainRegistry.list()) {
|
|
6880
|
+
if (chainFilter && !chainFilter.has(chain.id)) continue;
|
|
6881
|
+
const mempool = chain.custom?.mempool?.address;
|
|
6882
|
+
if (!mempool) return failure(new InternalServerError(`Missing mempool address for chain ${chain.id}.`));
|
|
6883
|
+
const multicall = chain.contracts?.multicall3?.address;
|
|
6884
|
+
if (!multicall) return failure(new InternalServerError(`Missing multicall3 address for chain ${chain.id}.`));
|
|
6885
|
+
const v2 = chain.custom?.morpho?.address;
|
|
6886
|
+
if (!v2) return failure(new InternalServerError(`Missing morpho address for chain ${chain.id}.`));
|
|
6887
|
+
const chainContracts = [
|
|
6888
|
+
{
|
|
6889
|
+
chain_id: chain.id,
|
|
6890
|
+
name: "mempool",
|
|
6891
|
+
address: mempool
|
|
6892
|
+
},
|
|
6893
|
+
{
|
|
6894
|
+
chain_id: chain.id,
|
|
6895
|
+
name: "multicall",
|
|
6896
|
+
address: multicall
|
|
6897
|
+
},
|
|
6898
|
+
{
|
|
6899
|
+
chain_id: chain.id,
|
|
6900
|
+
name: "v2",
|
|
6901
|
+
address: v2
|
|
6902
|
+
}
|
|
6903
|
+
];
|
|
6904
|
+
for (const contract of chainContracts) {
|
|
6905
|
+
const cursorKey = `${contract.chain_id}:${contract.address.toLowerCase()}`;
|
|
6906
|
+
if (seenAddresses.has(cursorKey)) return failure(new InternalServerError(`Duplicate contract address ${contract.address} for chain ${chain.id}.`));
|
|
6907
|
+
seenAddresses.add(cursorKey);
|
|
6908
|
+
contracts.push(contract);
|
|
6909
|
+
}
|
|
6910
|
+
}
|
|
6911
|
+
contracts.sort((a, b) => {
|
|
6912
|
+
if (a.chain_id !== b.chain_id) return a.chain_id - b.chain_id;
|
|
6913
|
+
const addressCompare = a.address.toLowerCase().localeCompare(b.address.toLowerCase());
|
|
6914
|
+
if (addressCompare !== 0) return addressCompare;
|
|
6915
|
+
return a.name.localeCompare(b.name);
|
|
6916
|
+
});
|
|
6917
|
+
let cursorContract = null;
|
|
6918
|
+
if (cursor) try {
|
|
6919
|
+
cursorContract = parseCursor$1(cursor);
|
|
7116
6920
|
} catch (err) {
|
|
7117
|
-
logger.error({
|
|
7118
|
-
err,
|
|
7119
|
-
msg: "Error get obligations",
|
|
7120
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7121
|
-
errorStack: err instanceof Error ? err.stack : void 0
|
|
7122
|
-
});
|
|
7123
6921
|
return failure(err);
|
|
7124
6922
|
}
|
|
6923
|
+
const startIndex = cursorContract ? findStartIndex$1(contracts, cursorContract) : 0;
|
|
6924
|
+
const page = contracts.slice(startIndex, startIndex + limit);
|
|
6925
|
+
const nextCursor = startIndex + limit < contracts.length && page.length > 0 ? formatCursor$1(page.at(-1)) : null;
|
|
6926
|
+
return success({
|
|
6927
|
+
data: page,
|
|
6928
|
+
cursor: nextCursor
|
|
6929
|
+
});
|
|
7125
6930
|
}
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
offersCallbacks: () => offersCallbacks,
|
|
7164
|
-
offsets: () => offsets,
|
|
7165
|
-
oracles: () => oracles,
|
|
7166
|
-
positionTypes: () => positionTypes,
|
|
7167
|
-
positions: () => positions,
|
|
7168
|
-
status: () => status,
|
|
7169
|
-
transfers: () => transfers,
|
|
7170
|
-
trees: () => trees,
|
|
7171
|
-
validations: () => validations
|
|
7172
|
-
});
|
|
7173
|
-
const s = (0, drizzle_orm_pg_core.pgSchema)(VERSION);
|
|
7174
|
-
var EnumTableName = /* @__PURE__ */ function(EnumTableName) {
|
|
7175
|
-
EnumTableName["OBLIGATIONS"] = "obligations";
|
|
7176
|
-
EnumTableName["GROUPS"] = "groups";
|
|
7177
|
-
EnumTableName["CONSUMED_EVENTS"] = "consumed_events";
|
|
7178
|
-
EnumTableName["OBLIGATION_COLLATERALS_V2"] = "obligation_collaterals_v2";
|
|
7179
|
-
EnumTableName["ORACLES"] = "oracles";
|
|
7180
|
-
EnumTableName["OFFERS"] = "offers";
|
|
7181
|
-
EnumTableName["OFFERS_CALLBACKS"] = "offers_callbacks";
|
|
7182
|
-
EnumTableName["CALLBACKS"] = "callbacks";
|
|
7183
|
-
EnumTableName["POSITIONS"] = "positions";
|
|
7184
|
-
EnumTableName["TRANSFERS"] = "transfers";
|
|
7185
|
-
EnumTableName["VALIDATIONS"] = "validations";
|
|
7186
|
-
EnumTableName["COLLECTORS"] = "collectors";
|
|
7187
|
-
EnumTableName["CHAINS"] = "chains";
|
|
7188
|
-
EnumTableName["LOTS"] = "lots";
|
|
7189
|
-
EnumTableName["OFFSETS"] = "offsets";
|
|
7190
|
-
EnumTableName["TREES"] = "trees";
|
|
7191
|
-
EnumTableName["MERKLE_PATHS"] = "merkle_paths";
|
|
7192
|
-
return EnumTableName;
|
|
7193
|
-
}(EnumTableName || {});
|
|
7194
|
-
const TABLE_NAMES = Object.values(EnumTableName);
|
|
7195
|
-
const VERSIONED_TABLE_NAMES = TABLE_NAMES.map((table) => `"${VERSION}"."${table}"`);
|
|
7196
|
-
const obligations = s.table(EnumTableName.OBLIGATIONS, {
|
|
7197
|
-
obligationId: (0, drizzle_orm_pg_core.varchar)("obligation_id", { length: 66 }).primaryKey(),
|
|
7198
|
-
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
7199
|
-
loanToken: (0, drizzle_orm_pg_core.varchar)("loan_token", { length: 42 }).notNull(),
|
|
7200
|
-
maturity: (0, drizzle_orm_pg_core.integer)("maturity").notNull()
|
|
7201
|
-
});
|
|
7202
|
-
const groups = s.table(EnumTableName.GROUPS, {
|
|
7203
|
-
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
7204
|
-
maker: (0, drizzle_orm_pg_core.varchar)("maker", { length: 42 }).notNull(),
|
|
7205
|
-
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
7206
|
-
consumed: (0, drizzle_orm_pg_core.numeric)("consumed", {
|
|
7207
|
-
precision: 78,
|
|
7208
|
-
scale: 0
|
|
7209
|
-
}).notNull(),
|
|
7210
|
-
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
7211
|
-
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
7212
|
-
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
7213
|
-
columns: [
|
|
7214
|
-
table.chainId,
|
|
7215
|
-
table.maker,
|
|
7216
|
-
table.group
|
|
7217
|
-
],
|
|
7218
|
-
name: "groups_pk"
|
|
7219
|
-
}), (0, drizzle_orm_pg_core.index)("groups_chain_id_maker_group_consumed_idx").on(table.chainId, table.maker, table.group, table.consumed)]);
|
|
7220
|
-
const consumedEvents = s.table(EnumTableName.CONSUMED_EVENTS, {
|
|
7221
|
-
eventId: (0, drizzle_orm_pg_core.varchar)("event_id", { length: 128 }).primaryKey(),
|
|
7222
|
-
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
7223
|
-
maker: (0, drizzle_orm_pg_core.varchar)("maker", { length: 42 }).notNull(),
|
|
7224
|
-
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
7225
|
-
amount: (0, drizzle_orm_pg_core.numeric)("amount", {
|
|
7226
|
-
precision: 78,
|
|
7227
|
-
scale: 0
|
|
7228
|
-
}).notNull(),
|
|
7229
|
-
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
7230
|
-
createdAt: (0, drizzle_orm_pg_core.timestamp)("created_at").defaultNow().notNull()
|
|
7231
|
-
}, (t) => [
|
|
7232
|
-
(0, drizzle_orm_pg_core.foreignKey)({
|
|
7233
|
-
columns: [
|
|
7234
|
-
t.chainId,
|
|
7235
|
-
t.maker,
|
|
7236
|
-
t.group
|
|
7237
|
-
],
|
|
7238
|
-
foreignColumns: [
|
|
7239
|
-
groups.chainId,
|
|
7240
|
-
groups.maker,
|
|
7241
|
-
groups.group
|
|
7242
|
-
],
|
|
7243
|
-
name: "consumed_events_groups_fk"
|
|
7244
|
-
}).onDelete("cascade"),
|
|
7245
|
-
(0, drizzle_orm_pg_core.index)("consumed_events_group_idx").on(t.chainId, t.maker, t.group),
|
|
7246
|
-
(0, drizzle_orm_pg_core.index)("consumed_events_block_number_idx").on(t.blockNumber)
|
|
7247
|
-
]);
|
|
7248
|
-
const obligationCollateralsV2 = s.table(EnumTableName.OBLIGATION_COLLATERALS_V2, {
|
|
7249
|
-
obligationId: (0, drizzle_orm_pg_core.varchar)("obligation_id", { length: 66 }).notNull().references(() => obligations.obligationId, { onDelete: "cascade" }),
|
|
7250
|
-
asset: (0, drizzle_orm_pg_core.varchar)("asset", { length: 42 }).notNull(),
|
|
7251
|
-
oracleChainId: (0, drizzle_orm_pg_core.bigint)("oracle_chain_id", { mode: "number" }).$type().notNull(),
|
|
7252
|
-
oracleAddress: (0, drizzle_orm_pg_core.varchar)("oracle_address", { length: 42 }).notNull(),
|
|
7253
|
-
lltv: (0, drizzle_orm_pg_core.bigint)("lltv", { mode: "bigint" }).notNull(),
|
|
7254
|
-
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
7255
|
-
}, (table) => [
|
|
7256
|
-
(0, drizzle_orm_pg_core.primaryKey)({
|
|
7257
|
-
columns: [table.obligationId, table.asset],
|
|
7258
|
-
name: "obligation_collaterals_v2_pk"
|
|
7259
|
-
}),
|
|
7260
|
-
(0, drizzle_orm_pg_core.foreignKey)({
|
|
7261
|
-
columns: [table.oracleChainId, table.oracleAddress],
|
|
7262
|
-
foreignColumns: [oracles.chainId, oracles.address],
|
|
7263
|
-
name: "obligation_collaterals_v2_oracles_fk"
|
|
7264
|
-
}),
|
|
7265
|
-
(0, drizzle_orm_pg_core.index)("obligation_collaterals_v2_obligation_id_idx").on(table.obligationId),
|
|
7266
|
-
(0, drizzle_orm_pg_core.index)("obligation_collaterals_v2_oracle_fk_idx").on(table.oracleChainId, table.oracleAddress)
|
|
7267
|
-
]);
|
|
7268
|
-
const oracles = s.table(EnumTableName.ORACLES, {
|
|
7269
|
-
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
7270
|
-
address: (0, drizzle_orm_pg_core.varchar)("address", { length: 42 }).notNull(),
|
|
7271
|
-
price: (0, drizzle_orm_pg_core.numeric)("price", {
|
|
7272
|
-
precision: 78,
|
|
7273
|
-
scale: 0
|
|
7274
|
-
}),
|
|
7275
|
-
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
7276
|
-
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
7277
|
-
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
7278
|
-
columns: [table.chainId, table.address],
|
|
7279
|
-
name: "oracles_pk"
|
|
7280
|
-
})]);
|
|
7281
|
-
const offers = s.table(EnumTableName.OFFERS, {
|
|
7282
|
-
hash: (0, drizzle_orm_pg_core.varchar)("hash", { length: 66 }).primaryKey(),
|
|
7283
|
-
obligationId: (0, drizzle_orm_pg_core.varchar)("obligation_id", { length: 66 }).notNull().references(() => obligations.obligationId, { onDelete: "cascade" }),
|
|
7284
|
-
assets: (0, drizzle_orm_pg_core.numeric)("assets", {
|
|
7285
|
-
precision: 78,
|
|
7286
|
-
scale: 0
|
|
7287
|
-
}).notNull(),
|
|
7288
|
-
obligationUnits: (0, drizzle_orm_pg_core.numeric)("obligation_units", {
|
|
7289
|
-
precision: 78,
|
|
7290
|
-
scale: 0
|
|
7291
|
-
}).notNull().default("0"),
|
|
7292
|
-
obligationShares: (0, drizzle_orm_pg_core.numeric)("obligation_shares", {
|
|
7293
|
-
precision: 78,
|
|
7294
|
-
scale: 0
|
|
7295
|
-
}).notNull().default("0"),
|
|
7296
|
-
price: (0, drizzle_orm_pg_core.numeric)("price", {
|
|
7297
|
-
precision: 78,
|
|
7298
|
-
scale: 0
|
|
7299
|
-
}).notNull(),
|
|
7300
|
-
maturity: (0, drizzle_orm_pg_core.integer)("maturity").notNull(),
|
|
7301
|
-
expiry: (0, drizzle_orm_pg_core.integer)("expiry").notNull(),
|
|
7302
|
-
start: (0, drizzle_orm_pg_core.integer)("start").notNull(),
|
|
7303
|
-
groupChainId: (0, drizzle_orm_pg_core.bigint)("group_chain_id", { mode: "number" }).$type().notNull(),
|
|
7304
|
-
groupMaker: (0, drizzle_orm_pg_core.varchar)("group_maker", { length: 42 }).notNull(),
|
|
7305
|
-
group: (0, drizzle_orm_pg_core.varchar)("group_group", { length: 66 }).notNull(),
|
|
7306
|
-
session: (0, drizzle_orm_pg_core.varchar)("session", { length: 66 }).notNull(),
|
|
7307
|
-
buy: (0, drizzle_orm_pg_core.boolean)("buy").notNull(),
|
|
7308
|
-
callbackAddress: (0, drizzle_orm_pg_core.varchar)("callback_address", { length: 42 }).notNull(),
|
|
7309
|
-
callbackData: (0, drizzle_orm_pg_core.text)("callback_data").notNull(),
|
|
7310
|
-
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
7311
|
-
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
7312
|
-
}, (table) => [
|
|
7313
|
-
(0, drizzle_orm_pg_core.foreignKey)({
|
|
7314
|
-
columns: [
|
|
7315
|
-
table.groupChainId,
|
|
7316
|
-
table.groupMaker,
|
|
7317
|
-
table.group
|
|
7318
|
-
],
|
|
7319
|
-
foreignColumns: [
|
|
7320
|
-
groups.chainId,
|
|
7321
|
-
groups.maker,
|
|
7322
|
-
groups.group
|
|
7323
|
-
],
|
|
7324
|
-
name: "offers_groups_fk"
|
|
7325
|
-
}).onDelete("cascade"),
|
|
7326
|
-
(0, drizzle_orm_pg_core.index)("offers_group_fk_idx").on(table.groupChainId, table.groupMaker, table.group),
|
|
7327
|
-
(0, drizzle_orm_pg_core.index)("offers_group_and_hash_idx").on(table.groupChainId, table.groupMaker, table.group, table.hash),
|
|
7328
|
-
(0, drizzle_orm_pg_core.index)("offers_obligation_id_side_idx").on(table.obligationId, table.buy)
|
|
7329
|
-
]);
|
|
7330
|
-
const offersCallbacks = s.table(EnumTableName.OFFERS_CALLBACKS, {
|
|
7331
|
-
offerHash: (0, drizzle_orm_pg_core.varchar)("offer_hash", { length: 66 }).notNull().references(() => offers.hash, { onDelete: "cascade" }),
|
|
7332
|
-
callbackId: (0, drizzle_orm_pg_core.varchar)("callback_id", { length: 66 })
|
|
7333
|
-
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
7334
|
-
columns: [table.offerHash, table.callbackId],
|
|
7335
|
-
name: "offers_callbacks_pk"
|
|
7336
|
-
})]);
|
|
7337
|
-
const callbacks = s.table(EnumTableName.CALLBACKS, {
|
|
7338
|
-
id: (0, drizzle_orm_pg_core.varchar)("id", { length: 66 }).primaryKey(),
|
|
7339
|
-
positionChainId: (0, drizzle_orm_pg_core.bigint)("position_chain_id", { mode: "number" }).$type().notNull(),
|
|
7340
|
-
positionContract: (0, drizzle_orm_pg_core.varchar)("position_contract", { length: 42 }).notNull(),
|
|
7341
|
-
positionUser: (0, drizzle_orm_pg_core.varchar)("position_user", { length: 42 }).notNull(),
|
|
7342
|
-
amount: (0, drizzle_orm_pg_core.numeric)("amount", {
|
|
7343
|
-
precision: 78,
|
|
7344
|
-
scale: 0
|
|
7345
|
-
})
|
|
7346
|
-
}, (table) => [(0, drizzle_orm_pg_core.foreignKey)({
|
|
7347
|
-
columns: [
|
|
7348
|
-
table.positionChainId,
|
|
7349
|
-
table.positionContract,
|
|
7350
|
-
table.positionUser
|
|
6931
|
+
function parseCursor$1(cursor) {
|
|
6932
|
+
const [chain, address] = cursor.split(":", 2);
|
|
6933
|
+
if (!chain || !address) throw new BadRequestError("Cursor must be in the format chain_id:0x...");
|
|
6934
|
+
return {
|
|
6935
|
+
chain_id: Number.parseInt(chain, 10),
|
|
6936
|
+
address: address.toLowerCase()
|
|
6937
|
+
};
|
|
6938
|
+
}
|
|
6939
|
+
function formatCursor$1(contract) {
|
|
6940
|
+
return `${contract.chain_id}:${contract.address.toLowerCase()}`;
|
|
6941
|
+
}
|
|
6942
|
+
function findStartIndex$1(contracts, cursor) {
|
|
6943
|
+
let low = 0;
|
|
6944
|
+
let high = contracts.length;
|
|
6945
|
+
while (low < high) {
|
|
6946
|
+
const mid = Math.floor((low + high) / 2);
|
|
6947
|
+
const current = contracts[mid];
|
|
6948
|
+
if (compareContract(current, cursor) <= 0) low = mid + 1;
|
|
6949
|
+
else high = mid;
|
|
6950
|
+
}
|
|
6951
|
+
return low;
|
|
6952
|
+
}
|
|
6953
|
+
function compareContract(contract, cursor) {
|
|
6954
|
+
if (contract.chain_id !== cursor.chain_id) return contract.chain_id - cursor.chain_id;
|
|
6955
|
+
return contract.address.toLowerCase().localeCompare(cursor.address.toLowerCase());
|
|
6956
|
+
}
|
|
6957
|
+
|
|
6958
|
+
//#endregion
|
|
6959
|
+
//#region src/gatekeeper/GateConfig.ts
|
|
6960
|
+
const assets = {
|
|
6961
|
+
[ChainId.ETHEREUM.toString()]: [
|
|
6962
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
6963
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
6964
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
6965
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
6966
|
+
"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
|
|
6967
|
+
"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
|
|
7351
6968
|
],
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
6969
|
+
[ChainId.BASE.toString()]: [
|
|
6970
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
6971
|
+
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
|
6972
|
+
"0x4200000000000000000000000000000000000006",
|
|
6973
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
6974
|
+
"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
|
|
6975
|
+
"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
|
|
6976
|
+
"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
|
|
7356
6977
|
],
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
7364
|
-
lower: (0, drizzle_orm_pg_core.numeric)("lower", {
|
|
7365
|
-
precision: 78,
|
|
7366
|
-
scale: 0
|
|
7367
|
-
}).notNull(),
|
|
7368
|
-
upper: (0, drizzle_orm_pg_core.numeric)("upper", {
|
|
7369
|
-
precision: 78,
|
|
7370
|
-
scale: 0
|
|
7371
|
-
}).notNull()
|
|
7372
|
-
}, (table) => [
|
|
7373
|
-
(0, drizzle_orm_pg_core.primaryKey)({
|
|
7374
|
-
columns: [
|
|
7375
|
-
table.chainId,
|
|
7376
|
-
table.user,
|
|
7377
|
-
table.contract,
|
|
7378
|
-
table.group
|
|
7379
|
-
],
|
|
7380
|
-
name: "lots_pk"
|
|
7381
|
-
}),
|
|
7382
|
-
(0, drizzle_orm_pg_core.foreignKey)({
|
|
7383
|
-
columns: [
|
|
7384
|
-
table.chainId,
|
|
7385
|
-
table.contract,
|
|
7386
|
-
table.user
|
|
7387
|
-
],
|
|
7388
|
-
foreignColumns: [
|
|
7389
|
-
positions.chainId,
|
|
7390
|
-
positions.contract,
|
|
7391
|
-
positions.user
|
|
7392
|
-
],
|
|
7393
|
-
name: "lots_positions_fk"
|
|
7394
|
-
}).onDelete("cascade"),
|
|
7395
|
-
(0, drizzle_orm_pg_core.foreignKey)({
|
|
7396
|
-
columns: [
|
|
7397
|
-
table.chainId,
|
|
7398
|
-
table.user,
|
|
7399
|
-
table.group
|
|
7400
|
-
],
|
|
7401
|
-
foreignColumns: [
|
|
7402
|
-
groups.chainId,
|
|
7403
|
-
groups.maker,
|
|
7404
|
-
groups.group
|
|
7405
|
-
],
|
|
7406
|
-
name: "lots_groups_fk"
|
|
7407
|
-
}).onDelete("cascade")
|
|
7408
|
-
]);
|
|
7409
|
-
const offsets = s.table(EnumTableName.OFFSETS, {
|
|
7410
|
-
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
7411
|
-
user: (0, drizzle_orm_pg_core.varchar)("user", { length: 42 }).notNull(),
|
|
7412
|
-
contract: (0, drizzle_orm_pg_core.varchar)("contract", { length: 42 }).notNull(),
|
|
7413
|
-
group: (0, drizzle_orm_pg_core.varchar)("group", { length: 66 }).notNull(),
|
|
7414
|
-
value: (0, drizzle_orm_pg_core.numeric)("value", {
|
|
7415
|
-
precision: 78,
|
|
7416
|
-
scale: 0
|
|
7417
|
-
}).notNull()
|
|
7418
|
-
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
7419
|
-
columns: [
|
|
7420
|
-
table.chainId,
|
|
7421
|
-
table.user,
|
|
7422
|
-
table.contract,
|
|
7423
|
-
table.group
|
|
6978
|
+
[ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
|
|
6979
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
6980
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
6981
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
6982
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
6983
|
+
"0xce79ddb3152d52ff8fe65a4c7e058b035fcb560a"
|
|
7424
6984
|
],
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
6985
|
+
[ChainId.ANVIL.toString()]: [
|
|
6986
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
6987
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
6988
|
+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
6989
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
6990
|
+
]
|
|
6991
|
+
};
|
|
6992
|
+
const oracles = {
|
|
6993
|
+
[ChainId.ETHEREUM.toString()]: [
|
|
6994
|
+
"0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
|
|
6995
|
+
"0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
|
|
6996
|
+
"0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
|
|
6997
|
+
"0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
|
|
6998
|
+
"0xbD60A6770b27E084E8617335ddE769241B0e71D8",
|
|
6999
|
+
"0xAe12416c1F21B0698c27fe042D9309C83baC6597"
|
|
7431
7000
|
],
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7001
|
+
[ChainId.BASE.toString()]: [
|
|
7002
|
+
"0xD09048c8B568Dbf5f189302beA26c9edABFC4858",
|
|
7003
|
+
"0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4",
|
|
7004
|
+
"0x05D2618404668D725B66c0f32B39e4EC15B393dC",
|
|
7005
|
+
"0xE1bb8E5b4930eC9FeC7f7943FCF6227649F14B37",
|
|
7006
|
+
"0x663BECd10daE6C4A3Dcd89F1d76c1174199639B9",
|
|
7007
|
+
"0x10b95702a0ce895972C91e432C4f7E19811D320E",
|
|
7008
|
+
"0x8C87DbD7A0c647A4291592Bc2994dbF95880fE2F",
|
|
7009
|
+
"0x4A11590e5326138B514E08A9B52202D42077Ca65",
|
|
7010
|
+
"0xa54122f0E0766258377Ffe732e454A3248f454F4"
|
|
7436
7011
|
],
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
const positions = s.table(EnumTableName.POSITIONS, {
|
|
7445
|
-
chainId: (0, drizzle_orm_pg_core.bigint)("chain_id", { mode: "number" }).$type().notNull(),
|
|
7446
|
-
contract: (0, drizzle_orm_pg_core.varchar)("contract", { length: 42 }).notNull(),
|
|
7447
|
-
user: (0, drizzle_orm_pg_core.varchar)("user", { length: 42 }).notNull(),
|
|
7448
|
-
positionTypeId: (0, drizzle_orm_pg_core.integer)("position_type_id").notNull().references(() => positionTypes.id, { onDelete: "no action" }),
|
|
7449
|
-
balance: (0, drizzle_orm_pg_core.numeric)("balance", {
|
|
7450
|
-
precision: 78,
|
|
7451
|
-
scale: 0
|
|
7452
|
-
}),
|
|
7453
|
-
asset: (0, drizzle_orm_pg_core.varchar)("asset", { length: 42 }),
|
|
7454
|
-
blockNumber: (0, drizzle_orm_pg_core.bigint)("block_number", { mode: "number" }).notNull(),
|
|
7455
|
-
updatedAt: (0, drizzle_orm_pg_core.timestamp)("updated_at").defaultNow().notNull()
|
|
7456
|
-
}, (table) => [(0, drizzle_orm_pg_core.primaryKey)({
|
|
7457
|
-
columns: [
|
|
7458
|
-
table.chainId,
|
|
7459
|
-
table.contract,
|
|
7460
|
-
table.user
|
|
7012
|
+
[ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
|
|
7013
|
+
"0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
|
|
7014
|
+
"0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
|
|
7015
|
+
"0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
|
|
7016
|
+
"0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
|
|
7017
|
+
"0xbD60A6770b27E084E8617335ddE769241B0e71D8",
|
|
7018
|
+
"0xAe12416c1F21B0698c27fe042D9309C83baC6597"
|
|
7461
7019
|
],
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
]
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
]
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7020
|
+
[ChainId.ANVIL.toString()]: [
|
|
7021
|
+
"0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
|
|
7022
|
+
"0x9CB3f4276bcD149b3668e1a645a964bC12877b89",
|
|
7023
|
+
"0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2",
|
|
7024
|
+
"0x6Eb9F4128CeBc8B885A4d8562Db1Addf097f7348",
|
|
7025
|
+
"0xbD60A6770b27E084E8617335ddE769241B0e71D8",
|
|
7026
|
+
"0xAe12416c1F21B0698c27fe042D9309C83baC6597"
|
|
7027
|
+
]
|
|
7028
|
+
};
|
|
7029
|
+
const configs = {
|
|
7030
|
+
ethereum: {
|
|
7031
|
+
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
7032
|
+
maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
|
|
7033
|
+
},
|
|
7034
|
+
base: {
|
|
7035
|
+
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
7036
|
+
maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
|
|
7037
|
+
},
|
|
7038
|
+
"ethereum-virtual-testnet": {
|
|
7039
|
+
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
7040
|
+
maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
|
|
7041
|
+
},
|
|
7042
|
+
anvil: {
|
|
7043
|
+
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
7044
|
+
maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth]
|
|
7045
|
+
}
|
|
7046
|
+
};
|
|
7047
|
+
|
|
7048
|
+
//#endregion
|
|
7049
|
+
//#region src/gatekeeper/ConfigRules.ts
|
|
7050
|
+
/**
|
|
7051
|
+
* Build the configured rules (maturities + callback addresses + loan tokens + oracles) for the provided chains.
|
|
7052
|
+
* @param chains - Chains to include in the configured rules.
|
|
7053
|
+
* @returns Sorted list of config rules.
|
|
7054
|
+
*/
|
|
7055
|
+
function buildConfigRules(chains) {
|
|
7056
|
+
const rules = [];
|
|
7057
|
+
for (const chain of chains) {
|
|
7058
|
+
const maturities = configs[chain.name].maturities ?? [];
|
|
7059
|
+
for (const maturityName of maturities) rules.push({
|
|
7060
|
+
type: "maturity",
|
|
7061
|
+
chain_id: chain.id,
|
|
7062
|
+
name: maturityName,
|
|
7063
|
+
timestamp: from$16(maturityName)
|
|
7064
|
+
});
|
|
7065
|
+
const loanTokens = assets[chain.id.toString()] ?? [];
|
|
7066
|
+
for (const address of loanTokens) rules.push({
|
|
7067
|
+
type: "loan_token",
|
|
7068
|
+
chain_id: chain.id,
|
|
7069
|
+
address: normalizeAddress(address)
|
|
7070
|
+
});
|
|
7071
|
+
const oracles$2 = oracles[chain.id.toString()] ?? [];
|
|
7072
|
+
for (const address of oracles$2) rules.push({
|
|
7073
|
+
type: "oracle",
|
|
7074
|
+
chain_id: chain.id,
|
|
7075
|
+
address: normalizeAddress(address)
|
|
7076
|
+
});
|
|
7077
|
+
}
|
|
7078
|
+
rules.sort(compareConfigRules);
|
|
7079
|
+
return rules;
|
|
7080
|
+
}
|
|
7081
|
+
/**
|
|
7082
|
+
* Compute a stable checksum for the provided configured rules.
|
|
7083
|
+
* @param rules - Configured rules to checksum.
|
|
7084
|
+
* @returns MD5 checksum.
|
|
7085
|
+
*/
|
|
7086
|
+
function buildConfigRulesChecksum(rules) {
|
|
7087
|
+
const hash = (0, node_crypto.createHash)("md5");
|
|
7088
|
+
const orderedRules = [...rules].sort(compareConfigRules);
|
|
7089
|
+
for (const rule of orderedRules) {
|
|
7090
|
+
if (rule.type === "maturity") {
|
|
7091
|
+
hash.update(`maturity:${rule.chain_id}:${rule.name}:${rule.timestamp}\n`);
|
|
7092
|
+
continue;
|
|
7093
|
+
}
|
|
7094
|
+
if (rule.type === "callback") {
|
|
7095
|
+
hash.update(`callback:${rule.chain_id}:${rule.callback_type}:${rule.address}\n`);
|
|
7096
|
+
continue;
|
|
7097
|
+
}
|
|
7098
|
+
if (rule.type === "oracle") {
|
|
7099
|
+
hash.update(`oracle:${rule.chain_id}:${rule.address}\n`);
|
|
7100
|
+
continue;
|
|
7101
|
+
}
|
|
7102
|
+
hash.update(`loan_token:${rule.chain_id}:${rule.address}\n`);
|
|
7103
|
+
}
|
|
7104
|
+
return hash.digest("hex");
|
|
7105
|
+
}
|
|
7106
|
+
function normalizeAddress(address) {
|
|
7107
|
+
return address.toLowerCase();
|
|
7108
|
+
}
|
|
7109
|
+
function compareConfigRules(left, right) {
|
|
7110
|
+
if (left.chain_id !== right.chain_id) return left.chain_id - right.chain_id;
|
|
7111
|
+
if (left.type !== right.type) return left.type.localeCompare(right.type);
|
|
7112
|
+
if (left.type === "maturity" && right.type === "maturity") return left.timestamp - right.timestamp;
|
|
7113
|
+
if (left.type === "callback" && right.type === "callback") {
|
|
7114
|
+
if (left.callback_type !== right.callback_type) return left.callback_type.localeCompare(right.callback_type);
|
|
7115
|
+
return left.address.localeCompare(right.address);
|
|
7116
|
+
}
|
|
7117
|
+
if (left.type === "loan_token" && right.type === "loan_token") return left.address.localeCompare(right.address);
|
|
7118
|
+
if (left.type === "oracle" && right.type === "oracle") return left.address.localeCompare(right.address);
|
|
7119
|
+
return 0;
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
//#endregion
|
|
7123
|
+
//#region src/api/Controllers/getConfigRules.ts
|
|
7124
|
+
/**
|
|
7125
|
+
* Returns configured rules for the configured chains.
|
|
7126
|
+
* @param query - Raw query parameters containing filters/cursor/limit.
|
|
7127
|
+
* @param chains - Chains to include in the configured rules.
|
|
7128
|
+
* @returns Config rules response payload. {@link ApiPayload.Payload}
|
|
7129
|
+
*/
|
|
7130
|
+
async function getConfigRules(query, chains) {
|
|
7131
|
+
const parsed = safeParse("get_config_rules", query ?? {});
|
|
7132
|
+
if (!parsed.success) return failure(parsed.error);
|
|
7133
|
+
const { cursor, limit, types, chains: chainIds } = parsed.data;
|
|
7134
|
+
const typeFilter = types?.length ? new Set(types) : null;
|
|
7135
|
+
const chainFilter = chainIds?.length ? new Set(chainIds) : null;
|
|
7136
|
+
const filteredRules = buildConfigRules(chains).filter((rule) => {
|
|
7137
|
+
if (chainFilter && !chainFilter.has(rule.chain_id)) return false;
|
|
7138
|
+
if (typeFilter && !typeFilter.has(rule.type)) return false;
|
|
7139
|
+
return true;
|
|
7140
|
+
});
|
|
7141
|
+
const checksum = buildConfigRulesChecksum(filteredRules);
|
|
7142
|
+
let cursorRule = null;
|
|
7143
|
+
if (cursor) try {
|
|
7144
|
+
cursorRule = parseCursor(cursor);
|
|
7145
|
+
} catch (err) {
|
|
7146
|
+
return failure(err);
|
|
7147
|
+
}
|
|
7148
|
+
if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError("Cursor type must match requested rule types"));
|
|
7149
|
+
if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError("Cursor chain_id must match requested chains"));
|
|
7150
|
+
const startIndex = cursorRule ? findStartIndex(filteredRules, cursorRule) : 0;
|
|
7151
|
+
const page = filteredRules.slice(startIndex, startIndex + limit);
|
|
7152
|
+
const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor(page.at(-1)) : null;
|
|
7153
|
+
const response = success({
|
|
7154
|
+
data: page,
|
|
7155
|
+
cursor: nextCursor
|
|
7156
|
+
});
|
|
7157
|
+
response.body.meta.checksum = checksum;
|
|
7158
|
+
return response;
|
|
7159
|
+
}
|
|
7160
|
+
function formatCursor(rule) {
|
|
7161
|
+
if (rule.type === "maturity") return `maturity:${rule.chain_id}:${rule.timestamp}:${rule.name}`;
|
|
7162
|
+
if (rule.type === "callback") return `callback:${rule.chain_id}:${rule.callback_type}:${rule.address.toLowerCase()}`;
|
|
7163
|
+
if (rule.type === "oracle") return `oracle:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
7164
|
+
return `loan_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
7165
|
+
}
|
|
7166
|
+
function parseCursor(cursor) {
|
|
7167
|
+
const [type, chain, ...rest] = cursor.split(":");
|
|
7168
|
+
if (!type || !chain || rest.length === 0) throw new BadRequestError("Cursor must be in the format type:chain_id:<value>");
|
|
7169
|
+
if (!isConfigRuleType(type)) throw new BadRequestError("Cursor has an invalid rule type");
|
|
7170
|
+
const chain_id = Number.parseInt(chain, 10);
|
|
7171
|
+
if (!Number.isFinite(chain_id)) throw new BadRequestError("Cursor has an invalid chain_id");
|
|
7172
|
+
if (type === "maturity") {
|
|
7173
|
+
const timestampValue = Number.parseInt(rest[0] ?? "", 10);
|
|
7174
|
+
const nameValue = rest.slice(1).join(":");
|
|
7175
|
+
if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError("Cursor must be in the format maturity:chain_id:timestamp:name");
|
|
7176
|
+
if (!isMaturityType(nameValue)) throw new BadRequestError("Cursor has an invalid maturity name");
|
|
7177
|
+
return {
|
|
7178
|
+
type,
|
|
7179
|
+
chain_id,
|
|
7180
|
+
timestamp: parseMaturity(timestampValue),
|
|
7181
|
+
name: nameValue
|
|
7182
|
+
};
|
|
7183
|
+
}
|
|
7184
|
+
if (type === "callback") {
|
|
7185
|
+
const callbackTypeValue = rest[0] ?? "";
|
|
7186
|
+
const addressValue = rest.slice(1).join(":");
|
|
7187
|
+
if (!callbackTypeValue || !addressValue) throw new BadRequestError("Cursor must be in the format callback:chain_id:callback_type:address");
|
|
7188
|
+
if (!isCallbackType(callbackTypeValue)) throw new BadRequestError("Cursor has an invalid callback type");
|
|
7189
|
+
return {
|
|
7190
|
+
type,
|
|
7191
|
+
chain_id,
|
|
7192
|
+
callback_type: callbackTypeValue,
|
|
7193
|
+
address: parseAddress(addressValue, "Cursor address")
|
|
7194
|
+
};
|
|
7195
|
+
}
|
|
7196
|
+
if (type === "loan_token" || type === "oracle") {
|
|
7197
|
+
const addressValue = rest.join(":");
|
|
7198
|
+
if (!addressValue) throw new BadRequestError(`Cursor must be in the format ${type}:chain_id:address`);
|
|
7199
|
+
return {
|
|
7200
|
+
type,
|
|
7201
|
+
chain_id,
|
|
7202
|
+
address: parseAddress(addressValue, "Cursor address")
|
|
7203
|
+
};
|
|
7204
|
+
}
|
|
7205
|
+
throw new BadRequestError("Cursor has an invalid rule type");
|
|
7206
|
+
}
|
|
7207
|
+
function findStartIndex(rules, cursor) {
|
|
7208
|
+
let low = 0;
|
|
7209
|
+
let high = rules.length;
|
|
7210
|
+
while (low < high) {
|
|
7211
|
+
const mid = Math.floor((low + high) / 2);
|
|
7212
|
+
const current = rules[mid];
|
|
7213
|
+
if (compareConfigRules(current, cursor) <= 0) low = mid + 1;
|
|
7214
|
+
else high = mid;
|
|
7215
|
+
}
|
|
7216
|
+
return low;
|
|
7217
|
+
}
|
|
7218
|
+
function parseAddress(address, label) {
|
|
7219
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError(`${label} must be a valid 20-byte address`);
|
|
7220
|
+
return address.toLowerCase();
|
|
7221
|
+
}
|
|
7222
|
+
function isConfigRuleType(value) {
|
|
7223
|
+
return value === "maturity" || value === "callback" || value === "loan_token" || value === "oracle";
|
|
7224
|
+
}
|
|
7225
|
+
function isMaturityType(value) {
|
|
7226
|
+
return Object.values(MaturityType).includes(value);
|
|
7227
|
+
}
|
|
7228
|
+
function parseMaturity(value) {
|
|
7229
|
+
try {
|
|
7230
|
+
return from$16(value);
|
|
7231
|
+
} catch (err) {
|
|
7232
|
+
throw new BadRequestError(err instanceof Error ? err.message : "Invalid maturity timestamp");
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
7235
|
+
function isCallbackType(value) {
|
|
7236
|
+
if (value === Type$1.BuyWithEmptyCallback) return false;
|
|
7237
|
+
return Object.values(Type$1).includes(value);
|
|
7238
|
+
}
|
|
7239
|
+
|
|
7240
|
+
//#endregion
|
|
7241
|
+
//#region src/api/Controllers/getDocs.ts
|
|
7242
|
+
const __dirname$1 = (() => {
|
|
7243
|
+
try {
|
|
7244
|
+
return (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
7245
|
+
} catch {
|
|
7246
|
+
return process.cwd();
|
|
7247
|
+
}
|
|
7248
|
+
})();
|
|
7249
|
+
/**
|
|
7250
|
+
* Build the OpenAPI document for the router.
|
|
7251
|
+
* @returns OpenAPI document. {@link OpenAPIDocument}
|
|
7252
|
+
*/
|
|
7253
|
+
async function getSwaggerJson() {
|
|
7254
|
+
return OpenApi();
|
|
7255
|
+
}
|
|
7256
|
+
/**
|
|
7257
|
+
* Render the API documentation HTML page.
|
|
7258
|
+
* @returns HTML page as string.
|
|
7259
|
+
*/
|
|
7260
|
+
async function getDocsHtml() {
|
|
7261
|
+
const spec = await OpenApi();
|
|
7262
|
+
return `<!DOCTYPE html>
|
|
7263
|
+
<html>
|
|
7264
|
+
<head>
|
|
7265
|
+
<meta charset="UTF-8">
|
|
7266
|
+
<title>Router API Docs (Scalar)</title>
|
|
7267
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"><\/script>
|
|
7268
|
+
<style>
|
|
7269
|
+
html, body { margin: 0; height: 100%; }
|
|
7270
|
+
api-reference { height: 100%; width: 100%; }
|
|
7271
|
+
</style>
|
|
7272
|
+
</head>
|
|
7273
|
+
<body>
|
|
7274
|
+
<div id="api-container" style="height:100%;width:100%;"></div>
|
|
7275
|
+
<script>
|
|
7276
|
+
window.addEventListener('load', function () {
|
|
7277
|
+
const spec = ${JSON.stringify(spec)};
|
|
7278
|
+
Scalar.createApiReference('#api-container', { spec: { content: spec, hideModels: true } });
|
|
7279
|
+
});
|
|
7280
|
+
<\/script>
|
|
7281
|
+
</body>
|
|
7282
|
+
</html>`;
|
|
7283
|
+
}
|
|
7284
|
+
/**
|
|
7285
|
+
* Finds the integrator.md file.
|
|
7286
|
+
* Handles source, bundled CLI, and Lambda scenarios.
|
|
7287
|
+
*/
|
|
7288
|
+
function findIntegratorMd() {
|
|
7289
|
+
const candidates = [
|
|
7290
|
+
(0, node_path.resolve)(__dirname$1, "../../../docs/integrator.md"),
|
|
7291
|
+
(0, node_path.resolve)(__dirname$1, "../docs/integrator.md"),
|
|
7292
|
+
(0, node_path.resolve)(process.cwd(), "docs/integrator.md")
|
|
7293
|
+
];
|
|
7294
|
+
for (const candidate of candidates) if ((0, node_fs.existsSync)(candidate)) return candidate;
|
|
7295
|
+
throw new Error(`integrator.md not found. Tried: ${candidates.join(", ")}`);
|
|
7296
|
+
}
|
|
7297
|
+
/**
|
|
7298
|
+
* Renders the integrator documentation as HTML.
|
|
7299
|
+
* @returns HTML page with the rendered markdown documentation.
|
|
7300
|
+
*/
|
|
7301
|
+
async function getIntegratorDocsHtml() {
|
|
7302
|
+
return `<!DOCTYPE html>
|
|
7303
|
+
<html>
|
|
7304
|
+
<head>
|
|
7305
|
+
<meta charset="UTF-8">
|
|
7306
|
+
<title>Documentation</title>
|
|
7307
|
+
<style>
|
|
7308
|
+
body { font-family: system-ui, sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.6; }
|
|
7309
|
+
pre { background: #f4f4f4; padding: 1rem; overflow-x: auto; }
|
|
7310
|
+
code { background: #f4f4f4; padding: 0.2rem 0.4rem; }
|
|
7311
|
+
a { color: #0066cc; }
|
|
7312
|
+
</style>
|
|
7313
|
+
</head>
|
|
7314
|
+
<body>
|
|
7315
|
+
<nav><a href="/docs/api">API Reference →</a></nav>
|
|
7316
|
+
${await (0, marked.marked)(await (0, node_fs_promises.readFile)(findIntegratorMd(), "utf-8"))}
|
|
7317
|
+
</body>
|
|
7318
|
+
</html>`;
|
|
7319
|
+
}
|
|
7320
|
+
|
|
7321
|
+
//#endregion
|
|
7322
|
+
//#region src/api/Controllers/getHealth.ts
|
|
7323
|
+
async function getHealth(query, db, chainRegistry) {
|
|
7324
|
+
const logger = getLogger();
|
|
7325
|
+
try {
|
|
7326
|
+
const parsed = safeParse("get_health", query);
|
|
7327
|
+
if (!parsed.success) return failure(parsed.error);
|
|
7328
|
+
const snapshot = await create$16({
|
|
7329
|
+
db,
|
|
7330
|
+
chainRegistry
|
|
7331
|
+
}).getSnapshot();
|
|
7332
|
+
if (parsed.data.strict && !snapshot.initialized) return failure(new APIError(STATUS_CODE.INTERNAL_SERVER_ERROR, "Indexer block state is not initialized", "INTERNAL_SERVER_ERROR", toSnakeCase$1({
|
|
7333
|
+
missingChains: snapshot.missingChains,
|
|
7334
|
+
missingCollectors: snapshot.missingCollectors
|
|
7335
|
+
})));
|
|
7336
|
+
return success({ data: toSnakeCase$1({
|
|
7337
|
+
status: snapshot.status,
|
|
7338
|
+
initialized: snapshot.initialized,
|
|
7339
|
+
missingChains: snapshot.missingChains,
|
|
7340
|
+
missingCollectors: snapshot.missingCollectors
|
|
7341
|
+
}) });
|
|
7342
|
+
} catch (err) {
|
|
7343
|
+
logger.error({
|
|
7344
|
+
err,
|
|
7345
|
+
msg: "Error getting health status",
|
|
7346
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7347
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
7348
|
+
});
|
|
7349
|
+
return failure(err);
|
|
7350
|
+
}
|
|
7351
|
+
}
|
|
7352
|
+
async function getHealthChains(query, db, healthClients, chainRegistry) {
|
|
7353
|
+
const logger = getLogger();
|
|
7354
|
+
try {
|
|
7355
|
+
const parsed = safeParse("get_health_chains", query);
|
|
7356
|
+
if (!parsed.success) return failure(parsed.error);
|
|
7357
|
+
const snapshot = await create$16({
|
|
7358
|
+
db,
|
|
7359
|
+
healthClients,
|
|
7360
|
+
chainRegistry
|
|
7361
|
+
}).getSnapshot();
|
|
7362
|
+
if (parsed.data.strict && !snapshot.initialized) return failure(new APIError(STATUS_CODE.INTERNAL_SERVER_ERROR, "Indexer block state is not initialized", "INTERNAL_SERVER_ERROR", toSnakeCase$1({
|
|
7363
|
+
missingChains: snapshot.missingChains,
|
|
7364
|
+
missingCollectors: snapshot.missingCollectors
|
|
7365
|
+
})));
|
|
7366
|
+
const chains = snapshot.chains;
|
|
7367
|
+
return success({ data: chains.map(({ chainId, localBlockNumber, remoteBlockNumber, updatedAt, initialized }) => toSnakeCase$1({
|
|
7368
|
+
chainId,
|
|
7369
|
+
localBlockNumber,
|
|
7370
|
+
remoteBlockNumber,
|
|
7371
|
+
updatedAt,
|
|
7372
|
+
initialized
|
|
7373
|
+
})) });
|
|
7374
|
+
} catch (err) {
|
|
7375
|
+
logger.error({
|
|
7376
|
+
err,
|
|
7377
|
+
msg: "Error getting health status for chains",
|
|
7378
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7379
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
7380
|
+
});
|
|
7381
|
+
return failure(err);
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
async function getHealthCollectors(query, db, chainRegistry) {
|
|
7385
|
+
const logger = getLogger();
|
|
7386
|
+
try {
|
|
7387
|
+
const parsed = safeParse("get_health_collectors", query);
|
|
7388
|
+
if (!parsed.success) return failure(parsed.error);
|
|
7389
|
+
const snapshot = await create$16({
|
|
7390
|
+
db,
|
|
7391
|
+
chainRegistry
|
|
7392
|
+
}).getSnapshot();
|
|
7393
|
+
if (parsed.data.strict && !snapshot.initialized) return failure(new APIError(STATUS_CODE.INTERNAL_SERVER_ERROR, "Indexer block state is not initialized", "INTERNAL_SERVER_ERROR", toSnakeCase$1({
|
|
7394
|
+
missingChains: snapshot.missingChains,
|
|
7395
|
+
missingCollectors: snapshot.missingCollectors
|
|
7396
|
+
})));
|
|
7397
|
+
const collectors = snapshot.collectors;
|
|
7398
|
+
return success({ data: collectors.map(({ name, chainId, blockNumber, updatedAt, lag, status, initialized }) => toSnakeCase$1({
|
|
7399
|
+
name,
|
|
7400
|
+
chainId,
|
|
7401
|
+
blockNumber,
|
|
7402
|
+
updatedAt,
|
|
7403
|
+
lag,
|
|
7404
|
+
status,
|
|
7405
|
+
initialized
|
|
7406
|
+
})) });
|
|
7407
|
+
} catch (err) {
|
|
7408
|
+
logger.error({
|
|
7409
|
+
err,
|
|
7410
|
+
msg: "Error getting health status for collectors",
|
|
7411
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7412
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
7413
|
+
});
|
|
7414
|
+
return failure(err);
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
|
|
7418
|
+
//#endregion
|
|
7419
|
+
//#region src/api/Controllers/getObligation.ts
|
|
7420
|
+
async function getObligation(params, db) {
|
|
7421
|
+
const logger = getLogger();
|
|
7422
|
+
const result = safeParse("get_obligation", params, (issue) => issue.message);
|
|
7423
|
+
if (!result.success) return failure(result.error);
|
|
7424
|
+
const query = result.data;
|
|
7425
|
+
try {
|
|
7426
|
+
const { obligations } = await db.offers.getObligations({ ids: [query.obligation_id] });
|
|
7427
|
+
if (obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
|
|
7428
|
+
const obligation = obligations[0];
|
|
7429
|
+
const [quote] = await db.offers.getQuotes({ obligationIds: [id(obligation)] });
|
|
7430
|
+
return success({
|
|
7431
|
+
data: from$4(obligation, quote ?? {
|
|
7432
|
+
obligationId: id(obligation),
|
|
7433
|
+
ask: { price: 0n },
|
|
7434
|
+
bid: { price: 0n }
|
|
7435
|
+
}),
|
|
7436
|
+
cursor: null
|
|
7437
|
+
});
|
|
7438
|
+
} catch (err) {
|
|
7439
|
+
logger.error({
|
|
7440
|
+
err,
|
|
7441
|
+
msg: "Error get obligation",
|
|
7442
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7443
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
7444
|
+
});
|
|
7445
|
+
return failure(err);
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7448
|
+
|
|
7449
|
+
//#endregion
|
|
7450
|
+
//#region src/api/Controllers/getObligations.ts
|
|
7451
|
+
async function getObligations$1(queryParameters, db) {
|
|
7452
|
+
const logger = getLogger();
|
|
7453
|
+
const result = safeParse("get_obligations", queryParameters, (issue) => issue.message);
|
|
7454
|
+
if (!result.success) return failure(result.error);
|
|
7455
|
+
const query = result.data;
|
|
7456
|
+
try {
|
|
7457
|
+
const chainIds = query.chains?.length ? query.chains : void 0;
|
|
7458
|
+
const loanTokens = query.loan_tokens?.length ? query.loan_tokens : void 0;
|
|
7459
|
+
const collateralTokens = query.collateral_tokens?.length ? query.collateral_tokens : void 0;
|
|
7460
|
+
const maturities = query.maturities?.length ? query.maturities : void 0;
|
|
7461
|
+
const { obligations, nextCursor } = await db.offers.getObligations({
|
|
7462
|
+
cursor: query.cursor,
|
|
7463
|
+
limit: query.limit,
|
|
7464
|
+
chainId: chainIds,
|
|
7465
|
+
loanToken: loanTokens,
|
|
7466
|
+
collateralToken: collateralTokens,
|
|
7467
|
+
maturity: maturities
|
|
7468
|
+
});
|
|
7469
|
+
const quotes = await db.offers.getQuotes({ obligationIds: obligations.map((o) => id(o)) });
|
|
7470
|
+
return success({
|
|
7471
|
+
data: obligations.map((o) => from$4(o, quotes.find((q) => q.obligationId === id(o)) ?? {
|
|
7472
|
+
obligationId: id(o),
|
|
7473
|
+
ask: { price: 0n },
|
|
7474
|
+
bid: { price: 0n }
|
|
7475
|
+
})),
|
|
7476
|
+
cursor: nextCursor ?? null
|
|
7477
|
+
});
|
|
7478
|
+
} catch (err) {
|
|
7479
|
+
logger.error({
|
|
7480
|
+
err,
|
|
7481
|
+
msg: "Error get obligations",
|
|
7482
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
7483
|
+
errorStack: err instanceof Error ? err.stack : void 0
|
|
7484
|
+
});
|
|
7485
|
+
return failure(err);
|
|
7486
|
+
}
|
|
7487
|
+
}
|
|
7488
|
+
|
|
7489
|
+
//#endregion
|
|
7490
|
+
//#region src/database/constants.ts
|
|
7491
|
+
/**
|
|
7492
|
+
* Default batch size for bulk database inserts.
|
|
7493
|
+
*
|
|
7494
|
+
* PostgreSQL limits a single query to at most 65,535 parameters
|
|
7495
|
+
* (e.g. $1, $2, ...). In bulk inserts, each row consumes one
|
|
7496
|
+
* parameter per column, so inserting too many rows at once can
|
|
7497
|
+
* exceed this limit.
|
|
7498
|
+
*
|
|
7499
|
+
* Our largest batched insert is into the `offers` table with 15 columns.
|
|
7500
|
+
* 15 cols × 4,000 rows = 60,000 parameters, safely under 65,535.
|
|
7501
|
+
*/
|
|
7502
|
+
const DEFAULT_BATCH_SIZE$1 = 4e3;
|
|
7545
7503
|
|
|
7546
7504
|
//#endregion
|
|
7547
7505
|
//#region src/database/domains/Offers.ts
|
|
@@ -7596,13 +7554,13 @@ function create$15(config) {
|
|
|
7596
7554
|
jsonb_agg(
|
|
7597
7555
|
jsonb_build_object(
|
|
7598
7556
|
'asset', ${obligationCollateralsV2.asset},
|
|
7599
|
-
'oracle', ${oracles.address},
|
|
7557
|
+
'oracle', ${oracles$1.address},
|
|
7600
7558
|
'lltv', ${obligationCollateralsV2.lltv}
|
|
7601
7559
|
)
|
|
7602
7560
|
),
|
|
7603
7561
|
'[]'::jsonb
|
|
7604
|
-
)`.as("collaterals") }).from(obligationCollateralsV2).innerJoin(oracles, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles.chainId}
|
|
7605
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).where((0, drizzle_orm.eq)(obligationCollateralsV2.obligationId, offers.obligationId)).as("collaterals_lateral");
|
|
7562
|
+
)`.as("collaterals") }).from(obligationCollateralsV2).innerJoin(oracles$1, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
|
|
7563
|
+
AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).where((0, drizzle_orm.eq)(obligationCollateralsV2.obligationId, offers.obligationId)).as("collaterals_lateral");
|
|
7606
7564
|
const rows = (await db.select({
|
|
7607
7565
|
hash: offers.hash,
|
|
7608
7566
|
maker: offers.groupMaker,
|
|
@@ -7684,10 +7642,10 @@ function create$15(config) {
|
|
|
7684
7642
|
obligationId: obligations.obligationId,
|
|
7685
7643
|
chainId: obligations.chainId,
|
|
7686
7644
|
loanToken: obligations.loanToken,
|
|
7687
|
-
collaterals: drizzle_orm.sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
7645
|
+
collaterals: drizzle_orm.sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles$1.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
7688
7646
|
maturity: obligations.maturity
|
|
7689
|
-
}).from(obligations).innerJoin(obligationCollateralsV2, (0, drizzle_orm.eq)(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles.chainId}
|
|
7690
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).groupBy(obligations.obligationId).where((0, drizzle_orm.and)(cursor !== null && cursor !== void 0 ? (0, drizzle_orm.gt)(obligations.obligationId, cursor) : drizzle_orm.sql`true`, ids !== void 0 && ids.length > 0 ? (0, drizzle_orm.inArray)(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? (0, drizzle_orm.inArray)(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? (0, drizzle_orm.inArray)(obligations.maturity, maturities) : (0, drizzle_orm.gte)(obligations.maturity, now$1), collateralFilter)).orderBy((0, drizzle_orm.asc)(obligations.obligationId)).limit(limit);
|
|
7647
|
+
}).from(obligations).innerJoin(obligationCollateralsV2, (0, drizzle_orm.eq)(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles$1, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
|
|
7648
|
+
AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).groupBy(obligations.obligationId).where((0, drizzle_orm.and)(cursor !== null && cursor !== void 0 ? (0, drizzle_orm.gt)(obligations.obligationId, cursor) : drizzle_orm.sql`true`, ids !== void 0 && ids.length > 0 ? (0, drizzle_orm.inArray)(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? (0, drizzle_orm.inArray)(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? (0, drizzle_orm.inArray)(obligations.maturity, maturities) : (0, drizzle_orm.gte)(obligations.maturity, now$1), collateralFilter)).orderBy((0, drizzle_orm.asc)(obligations.obligationId)).limit(limit);
|
|
7691
7649
|
const items = [];
|
|
7692
7650
|
for (const row of result) items.push(from$15({
|
|
7693
7651
|
chainId: row.chainId,
|
|
@@ -7758,40 +7716,28 @@ async function getOffersQuery(db, parameters) {
|
|
|
7758
7716
|
if (cursor !== null && cursor !== void 0) {
|
|
7759
7717
|
if (!cursor.startsWith("0x") || cursor.length !== 66) throw new Error("Invalid cursor format");
|
|
7760
7718
|
}
|
|
7719
|
+
const now = Math.floor((Date.now() - 1) / 1e3);
|
|
7761
7720
|
const collateralsLateral = db.select({ collaterals: drizzle_orm.sql`COALESCE(
|
|
7762
7721
|
jsonb_agg(
|
|
7763
7722
|
jsonb_build_object(
|
|
7764
7723
|
'asset', ${obligationCollateralsV2.asset},
|
|
7765
|
-
'oracle', ${oracles.address},
|
|
7724
|
+
'oracle', ${oracles$1.address},
|
|
7766
7725
|
'lltv', ${obligationCollateralsV2.lltv}
|
|
7767
7726
|
)
|
|
7768
7727
|
),
|
|
7769
7728
|
'[]'::jsonb
|
|
7770
|
-
)`.as("collaterals") }).from(obligationCollateralsV2).innerJoin(oracles, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles.chainId}
|
|
7771
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).where((0, drizzle_orm.eq)(obligationCollateralsV2.obligationId, offers.obligationId)).as("collaterals_lateral");
|
|
7729
|
+
)`.as("collaterals") }).from(obligationCollateralsV2).innerJoin(oracles$1, drizzle_orm.sql`${obligationCollateralsV2.oracleChainId} = ${oracles$1.chainId}
|
|
7730
|
+
AND ${obligationCollateralsV2.oracleAddress} = ${oracles$1.address}`).where((0, drizzle_orm.eq)(obligationCollateralsV2.obligationId, offers.obligationId)).as("collaterals_lateral");
|
|
7772
7731
|
const availableLateral = db.select({ available: drizzle_orm.sql`COALESCE(SUM(
|
|
7773
7732
|
CASE
|
|
7774
|
-
-- If asset is null, position available is 0
|
|
7775
7733
|
WHEN ${positions.asset} IS NULL THEN 0
|
|
7776
|
-
|
|
7777
|
-
-- Position asset matches loan token: no conversion needed
|
|
7778
|
-
WHEN ${positions.asset} = ${obligations.loanToken} THEN
|
|
7734
|
+
ELSE
|
|
7779
7735
|
CASE
|
|
7780
7736
|
WHEN ${callbacks.amount} IS NULL THEN COALESCE(${positions.balance}, 0)::numeric
|
|
7781
7737
|
ELSE LEAST(${callbacks.amount}::numeric, COALESCE(${positions.balance}, 0)::numeric)
|
|
7782
7738
|
END
|
|
7783
|
-
|
|
7784
|
-
-- Position asset is collateral: apply oracle price * lltv
|
|
7785
|
-
-- Formula: balance * price / 1e36 * lltv / 1e18
|
|
7786
|
-
ELSE
|
|
7787
|
-
(CASE
|
|
7788
|
-
WHEN ${callbacks.amount} IS NULL THEN COALESCE(${positions.balance}, 0)::numeric
|
|
7789
|
-
ELSE LEAST(${callbacks.amount}::numeric, COALESCE(${positions.balance}, 0)::numeric)
|
|
7790
|
-
END)
|
|
7791
|
-
* COALESCE(${oracles.price}, 0)::numeric / 1e36
|
|
7792
|
-
* COALESCE(${obligationCollateralsV2.lltv}, 0)::numeric / 1e18
|
|
7793
7739
|
END
|
|
7794
|
-
), 0)`.as("available") }).from(offersCallbacks).innerJoin(callbacks, (0, drizzle_orm.eq)(offersCallbacks.callbackId, callbacks.id)).innerJoin(positions, (0, drizzle_orm.and)((0, drizzle_orm.eq)(callbacks.positionChainId, positions.chainId), (0, drizzle_orm.eq)(callbacks.positionContract, positions.contract), (0, drizzle_orm.eq)(callbacks.positionUser, positions.user))).
|
|
7740
|
+
), 0)`.as("available") }).from(offersCallbacks).innerJoin(callbacks, (0, drizzle_orm.eq)(offersCallbacks.callbackId, callbacks.id)).innerJoin(positions, (0, drizzle_orm.and)((0, drizzle_orm.eq)(callbacks.positionChainId, positions.chainId), (0, drizzle_orm.eq)(callbacks.positionContract, positions.contract), (0, drizzle_orm.eq)(callbacks.positionUser, positions.user))).where((0, drizzle_orm.eq)(offersCallbacks.offerHash, offers.hash)).as("available_lateral");
|
|
7795
7741
|
const rows = (await db.select({
|
|
7796
7742
|
hash: offers.hash,
|
|
7797
7743
|
maker: offers.groupMaker,
|
|
@@ -7813,17 +7759,24 @@ async function getOffersQuery(db, parameters) {
|
|
|
7813
7759
|
collaterals: collateralsLateral.collaterals,
|
|
7814
7760
|
blockNumber: offers.blockNumber,
|
|
7815
7761
|
available: drizzle_orm.sql`COALESCE(${availableLateral.available}::numeric, 0)`.as("available"),
|
|
7816
|
-
takeable: drizzle_orm.sql`FLOOR(GREATEST(
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7762
|
+
takeable: drizzle_orm.sql`FLOOR(GREATEST(0,
|
|
7763
|
+
CASE WHEN ${offers.buy} = false
|
|
7764
|
+
THEN ${offers.assets}::numeric - ${groups.consumed}::numeric
|
|
7765
|
+
ELSE LEAST(
|
|
7766
|
+
${offers.assets}::numeric - ${groups.consumed}::numeric,
|
|
7767
|
+
COALESCE(${availableLateral.available}::numeric, 0)
|
|
7768
|
+
)
|
|
7769
|
+
END
|
|
7822
7770
|
))`.as("takeable")
|
|
7823
|
-
}).from(offers).innerJoin(obligations, (0, drizzle_orm.eq)(offers.obligationId, obligations.obligationId)).innerJoin(groups, (0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.groupChainId, groups.chainId), (0, drizzle_orm.eq)(offers.groupMaker, groups.maker), (0, drizzle_orm.eq)(offers.group, groups.group))).innerJoinLateral(collateralsLateral, drizzle_orm.sql`true`).leftJoinLateral(availableLateral, drizzle_orm.sql`true`).where((0, drizzle_orm.and)(cursor !== null && cursor !== void 0 ? (0, drizzle_orm.gt)(offers.hash, cursor) : void 0, maker !== void 0 ? (0, drizzle_orm.eq)(offers.groupMaker, maker.toLowerCase()) : void 0, maker === void 0 ? drizzle_orm.sql`GREATEST(0,
|
|
7824
|
-
${offers.
|
|
7825
|
-
|
|
7826
|
-
|
|
7771
|
+
}).from(offers).innerJoin(obligations, (0, drizzle_orm.eq)(offers.obligationId, obligations.obligationId)).innerJoin(groups, (0, drizzle_orm.and)((0, drizzle_orm.eq)(offers.groupChainId, groups.chainId), (0, drizzle_orm.eq)(offers.groupMaker, groups.maker), (0, drizzle_orm.eq)(offers.group, groups.group))).innerJoinLateral(collateralsLateral, drizzle_orm.sql`true`).leftJoinLateral(availableLateral, drizzle_orm.sql`true`).where((0, drizzle_orm.and)(cursor !== null && cursor !== void 0 ? (0, drizzle_orm.gt)(offers.hash, cursor) : void 0, maker !== void 0 ? (0, drizzle_orm.eq)(offers.groupMaker, maker.toLowerCase()) : void 0, (0, drizzle_orm.gte)(offers.expiry, now), (0, drizzle_orm.gte)(offers.maturity, now), maker === void 0 ? drizzle_orm.sql`GREATEST(0,
|
|
7772
|
+
CASE WHEN ${offers.buy} = false
|
|
7773
|
+
THEN ${offers.assets}::numeric - ${groups.consumed}::numeric
|
|
7774
|
+
ELSE LEAST(
|
|
7775
|
+
${offers.assets}::numeric - ${groups.consumed}::numeric,
|
|
7776
|
+
COALESCE(${availableLateral.available}::numeric, 0)
|
|
7777
|
+
)
|
|
7778
|
+
END
|
|
7779
|
+
) > 0` : void 0)).orderBy((0, drizzle_orm.asc)(offers.hash)).limit(limit)).map((row) => {
|
|
7827
7780
|
return {
|
|
7828
7781
|
hash: row.hash,
|
|
7829
7782
|
maker: row.maker,
|
|
@@ -7933,64 +7886,6 @@ async function getUserPositions(queryParameters, db) {
|
|
|
7933
7886
|
}
|
|
7934
7887
|
}
|
|
7935
7888
|
|
|
7936
|
-
//#endregion
|
|
7937
|
-
//#region src/gatekeeper/CallbackTypes.ts
|
|
7938
|
-
/**
|
|
7939
|
-
* Resolve callback types for a list of callback addresses grouped by chain.
|
|
7940
|
-
* @param parameters - Resolve parameters. {@link resolveCallbackTypes.Parameters}
|
|
7941
|
-
* @returns Callback types grouped by chain. {@link resolveCallbackTypes.ReturnType}
|
|
7942
|
-
* @throws If a chain id is unknown.
|
|
7943
|
-
*/
|
|
7944
|
-
function resolveCallbackTypes$1(parameters) {
|
|
7945
|
-
const { chains, request } = parameters;
|
|
7946
|
-
const chainsById = new Map(chains.map((chain) => [chain.id, chain]));
|
|
7947
|
-
return request.callbacks.map(({ chain_id, addresses }) => {
|
|
7948
|
-
const chain = chainsById.get(chain_id);
|
|
7949
|
-
if (!chain) throw new Error(`Unknown chain id ${chain_id}`);
|
|
7950
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
7951
|
-
const uniqueAddresses = new Set(addresses.map((address) => address.toLowerCase()));
|
|
7952
|
-
for (const address of uniqueAddresses) {
|
|
7953
|
-
const bucketKey = getCallbackType(chain.name, address) ?? "not_supported";
|
|
7954
|
-
const list = buckets.get(bucketKey) ?? [];
|
|
7955
|
-
list.push(address);
|
|
7956
|
-
buckets.set(bucketKey, list);
|
|
7957
|
-
}
|
|
7958
|
-
const response = { chain_id };
|
|
7959
|
-
for (const [type, list] of buckets.entries()) response[type] = list;
|
|
7960
|
-
if (!response.not_supported) response.not_supported = [];
|
|
7961
|
-
return response;
|
|
7962
|
-
});
|
|
7963
|
-
}
|
|
7964
|
-
|
|
7965
|
-
//#endregion
|
|
7966
|
-
//#region src/api/Controllers/resolveCallbackTypes.ts
|
|
7967
|
-
/**
|
|
7968
|
-
* Resolve callback types for a list of callback addresses grouped by chain.
|
|
7969
|
-
* @param body - Request body with callback addresses. {@link CallbackTypesRequest}
|
|
7970
|
-
* @param chains - Chains to resolve callback types against. {@link Chain.Chain}
|
|
7971
|
-
* @returns Callback types grouped by chain. {@link CallbackTypesPayload}
|
|
7972
|
-
*/
|
|
7973
|
-
async function resolveCallbackTypes(body, chains) {
|
|
7974
|
-
const result = safeParse("callback_types", body, (issue) => issue.message);
|
|
7975
|
-
if (!result.success) return failure(result.error);
|
|
7976
|
-
const request = result.data;
|
|
7977
|
-
const chainIds = new Set(chains.map((chain) => chain.id));
|
|
7978
|
-
const unknown = request.callbacks.find((entry) => !chainIds.has(entry.chain_id));
|
|
7979
|
-
if (unknown) return failure(new BadRequestError(`Unknown chain id ${unknown.chain_id}`));
|
|
7980
|
-
try {
|
|
7981
|
-
const data = resolveCallbackTypes$1({
|
|
7982
|
-
chains,
|
|
7983
|
-
request
|
|
7984
|
-
});
|
|
7985
|
-
return success({
|
|
7986
|
-
data,
|
|
7987
|
-
cursor: null
|
|
7988
|
-
});
|
|
7989
|
-
} catch (err) {
|
|
7990
|
-
return failure(err);
|
|
7991
|
-
}
|
|
7992
|
-
}
|
|
7993
|
-
|
|
7994
7889
|
//#endregion
|
|
7995
7890
|
//#region src/api/Controllers/validateOffers.ts
|
|
7996
7891
|
async function validateOffers(body, gatekeeper) {
|
|
@@ -8070,7 +7965,6 @@ var Controllers_exports = /* @__PURE__ */ __exportAll({
|
|
|
8070
7965
|
getOffersQuery: () => getOffersQuery,
|
|
8071
7966
|
getSwaggerJson: () => getSwaggerJson,
|
|
8072
7967
|
getUserPositions: () => getUserPositions,
|
|
8073
|
-
resolveCallbackTypes: () => resolveCallbackTypes,
|
|
8074
7968
|
validateOffers: () => validateOffers
|
|
8075
7969
|
});
|
|
8076
7970
|
|
|
@@ -8143,24 +8037,9 @@ function serve(parameters) {
|
|
|
8143
8037
|
const { statusCode, body } = await gatekeeper.validate(reqBody);
|
|
8144
8038
|
return c.json(body, statusCode);
|
|
8145
8039
|
} catch (err) {
|
|
8146
|
-
const failure$
|
|
8147
|
-
return c.json(failure$1.body, failure$1.statusCode);
|
|
8148
|
-
}
|
|
8149
|
-
});
|
|
8150
|
-
app.post("/v1/callbacks", async (c) => {
|
|
8151
|
-
let body;
|
|
8152
|
-
try {
|
|
8153
|
-
body = await c.req.json();
|
|
8154
|
-
} catch (err) {
|
|
8155
|
-
const failure$3 = failure(err);
|
|
8156
|
-
return c.json(failure$3.body, failure$3.statusCode);
|
|
8157
|
-
}
|
|
8158
|
-
if (body === null || typeof body !== "object") {
|
|
8159
|
-
const failure$2 = failure(new BadRequestError("Request body must be a JSON object"));
|
|
8040
|
+
const failure$2 = failure(err);
|
|
8160
8041
|
return c.json(failure$2.body, failure$2.statusCode);
|
|
8161
8042
|
}
|
|
8162
|
-
const { statusCode, body: responseBody } = await resolveCallbackTypes(body, chainRegistry.list());
|
|
8163
|
-
return c.json(responseBody, statusCode);
|
|
8164
8043
|
});
|
|
8165
8044
|
app.get("/v1/users/:userAddress/positions", async (c) => {
|
|
8166
8045
|
const query = c.req.query();
|
|
@@ -8192,8 +8071,8 @@ function serve(parameters) {
|
|
|
8192
8071
|
const { statusCode, body } = await gatekeeper.getConfigRules(c.req.query());
|
|
8193
8072
|
return c.json(body, statusCode);
|
|
8194
8073
|
} catch (err) {
|
|
8195
|
-
const failure$
|
|
8196
|
-
return c.json(failure$
|
|
8074
|
+
const failure$1 = failure(err);
|
|
8075
|
+
return c.json(failure$1.body, failure$1.statusCode);
|
|
8197
8076
|
}
|
|
8198
8077
|
});
|
|
8199
8078
|
app.get("/docs/openapi", async (c) => c.text(JSON.stringify(await getSwaggerJson()), 200, { "Content-Type": "application/json; charset=utf-8" }));
|
|
@@ -8210,7 +8089,6 @@ function serve(parameters) {
|
|
|
8210
8089
|
var RouterApi_exports = /* @__PURE__ */ __exportAll({
|
|
8211
8090
|
BookResponse: () => BookResponse_exports,
|
|
8212
8091
|
BooksController: () => BooksController,
|
|
8213
|
-
CallbacksController: () => CallbacksController,
|
|
8214
8092
|
ChainHealth: () => ChainHealth,
|
|
8215
8093
|
ChainsHealthResponse: () => ChainsHealthResponse,
|
|
8216
8094
|
CollectorHealth: () => CollectorHealth,
|
|
@@ -8432,7 +8310,7 @@ var drizzle_exports = /* @__PURE__ */ __exportAll({
|
|
|
8432
8310
|
offers: () => offers,
|
|
8433
8311
|
offersCallbacks: () => offersCallbacks,
|
|
8434
8312
|
offsets: () => offsets,
|
|
8435
|
-
oracles: () => oracles,
|
|
8313
|
+
oracles: () => oracles$1,
|
|
8436
8314
|
positionTypes: () => positionTypes,
|
|
8437
8315
|
positions: () => positions,
|
|
8438
8316
|
status: () => status,
|
|
@@ -8717,7 +8595,7 @@ async function _getOffers(db, params) {
|
|
|
8717
8595
|
'lltv', oc.lltv
|
|
8718
8596
|
) ORDER BY oc.asset), '[]'::jsonb) AS collaterals
|
|
8719
8597
|
FROM ${obligationCollateralsV2} oc
|
|
8720
|
-
JOIN ${oracles} oracle
|
|
8598
|
+
JOIN ${oracles$1} oracle
|
|
8721
8599
|
ON oracle.chain_id = oc.oracle_chain_id
|
|
8722
8600
|
AND oracle.address = oc.oracle_address
|
|
8723
8601
|
WHERE oc.obligation_id = ${obligationId}
|
|
@@ -8872,35 +8750,15 @@ async function _getOffers(db, params) {
|
|
|
8872
8750
|
AND LOWER(pc.contract) = LOWER(c.position_contract)
|
|
8873
8751
|
AND LOWER(pc."user") = LOWER(c.position_user)
|
|
8874
8752
|
),
|
|
8875
|
-
-- Compute contribution per callback in loan terms (
|
|
8753
|
+
-- Compute contribution per callback in loan terms (loan token only — collateral positions are not indexed)
|
|
8876
8754
|
callback_loan_contribution AS (
|
|
8877
8755
|
SELECT
|
|
8878
8756
|
cc.*,
|
|
8879
8757
|
CASE
|
|
8880
|
-
-- No lot exists: contribution is 0
|
|
8881
8758
|
WHEN cc.lot_lower IS NULL THEN 0
|
|
8882
|
-
|
|
8883
|
-
WHEN LOWER(cc.position_asset) = LOWER(cc.loan_token) THEN
|
|
8884
|
-
LEAST(
|
|
8885
|
-
cc.lot_balance,
|
|
8886
|
-
COALESCE(cc.callback_amount::numeric, cc.lot_balance)
|
|
8887
|
-
)
|
|
8888
|
-
-- Collateral position: convert to loan using (amount * price / 10^36) * lltv / 10^18
|
|
8889
|
-
ELSE
|
|
8890
|
-
(
|
|
8891
|
-
LEAST(
|
|
8892
|
-
cc.lot_balance,
|
|
8893
|
-
COALESCE(cc.callback_amount::numeric, cc.lot_balance)
|
|
8894
|
-
) * COALESCE(collat_oracle.price::numeric, 0) / 1e36
|
|
8895
|
-
) * COALESCE(collat_info.lltv::numeric, 0) / 1e18
|
|
8759
|
+
ELSE LEAST(cc.lot_balance, COALESCE(cc.callback_amount::numeric, cc.lot_balance))
|
|
8896
8760
|
END AS contribution_in_loan
|
|
8897
8761
|
FROM callback_contributions cc
|
|
8898
|
-
LEFT JOIN ${obligationCollateralsV2} collat_info
|
|
8899
|
-
ON collat_info.obligation_id = cc.obligation_id
|
|
8900
|
-
AND LOWER(collat_info.asset) = LOWER(cc.position_asset)
|
|
8901
|
-
LEFT JOIN ${oracles} collat_oracle
|
|
8902
|
-
ON collat_oracle.chain_id = collat_info.oracle_chain_id
|
|
8903
|
-
AND LOWER(collat_oracle.address) = LOWER(collat_info.oracle_address)
|
|
8904
8762
|
),
|
|
8905
8763
|
-- Aggregate contributions per offer, deduplicating by position using DISTINCT ON
|
|
8906
8764
|
offer_contributions AS (
|
|
@@ -8937,6 +8795,22 @@ async function _getOffers(db, params) {
|
|
|
8937
8795
|
GROUP BY hash, obligation_id, assets, price, obligation_units, obligation_shares, maturity, expiry, start, group_group, buy,
|
|
8938
8796
|
callback_address, callback_data, block_number, group_chain_id, group_maker,
|
|
8939
8797
|
consumed, chain_id, loan_token, session
|
|
8798
|
+
UNION ALL
|
|
8799
|
+
-- Sell offers without callbacks: collateral positions not indexed, takeable = assets - consumed
|
|
8800
|
+
SELECT
|
|
8801
|
+
p.hash, p.obligation_id, p.assets, p.price,
|
|
8802
|
+
p.obligation_units, p.obligation_shares,
|
|
8803
|
+
p.maturity, p.expiry, p.start, p.group_group,
|
|
8804
|
+
p.buy, p.callback_address, p.callback_data,
|
|
8805
|
+
p.block_number, p.group_chain_id, p.group_maker,
|
|
8806
|
+
p.consumed, p.chain_id, p.loan_token, p.session,
|
|
8807
|
+
0 AS total_available
|
|
8808
|
+
FROM paged p
|
|
8809
|
+
WHERE p.buy = false
|
|
8810
|
+
AND NOT EXISTS (
|
|
8811
|
+
SELECT 1 FROM ${offersCallbacks} oc2
|
|
8812
|
+
WHERE oc2.offer_hash = p.hash
|
|
8813
|
+
)
|
|
8940
8814
|
)
|
|
8941
8815
|
-- Final SELECT with inline takeable computation
|
|
8942
8816
|
SELECT
|
|
@@ -8959,18 +8833,24 @@ async function _getOffers(db, params) {
|
|
|
8959
8833
|
oc.block_number,
|
|
8960
8834
|
oc.session,
|
|
8961
8835
|
COALESCE(oc.total_available, 0) AS available,
|
|
8962
|
-
-- takeable
|
|
8963
|
-
|
|
8964
|
-
oc.assets::numeric - oc.consumed::numeric
|
|
8965
|
-
|
|
8966
|
-
|
|
8836
|
+
-- takeable: sell offers use assets - consumed directly (collateral positions not indexed yet)
|
|
8837
|
+
CASE WHEN oc.buy = false
|
|
8838
|
+
THEN GREATEST(0, oc.assets::numeric - oc.consumed::numeric)
|
|
8839
|
+
ELSE GREATEST(0, LEAST(
|
|
8840
|
+
oc.assets::numeric - oc.consumed::numeric,
|
|
8841
|
+
COALESCE(oc.total_available, 0)
|
|
8842
|
+
))
|
|
8843
|
+
END AS takeable,
|
|
8967
8844
|
c.collaterals
|
|
8968
8845
|
FROM offer_contributions oc
|
|
8969
8846
|
LEFT JOIN collats c ON c.obligation_id = oc.obligation_id
|
|
8970
|
-
WHERE
|
|
8971
|
-
oc.assets::numeric - oc.consumed::numeric
|
|
8972
|
-
|
|
8973
|
-
|
|
8847
|
+
WHERE CASE WHEN oc.buy = false
|
|
8848
|
+
THEN GREATEST(0, oc.assets::numeric - oc.consumed::numeric)
|
|
8849
|
+
ELSE GREATEST(0, LEAST(
|
|
8850
|
+
oc.assets::numeric - oc.consumed::numeric,
|
|
8851
|
+
COALESCE(oc.total_available, 0)
|
|
8852
|
+
))
|
|
8853
|
+
END > 0
|
|
8974
8854
|
ORDER BY
|
|
8975
8855
|
oc.price::numeric ${priceSortDirection === "asc" ? drizzle_orm.sql`ASC` : drizzle_orm.sql`DESC`},
|
|
8976
8856
|
oc.block_number ASC,
|
|
@@ -9311,32 +9191,32 @@ function create$5(db) {
|
|
|
9311
9191
|
return {
|
|
9312
9192
|
get: async ({ chainId }) => {
|
|
9313
9193
|
return (await db.select({
|
|
9314
|
-
address: oracles.address,
|
|
9315
|
-
price: oracles.price,
|
|
9316
|
-
blockNumber: oracles.blockNumber,
|
|
9317
|
-
chainId: oracles.chainId
|
|
9318
|
-
}).from(oracles).where((0, drizzle_orm.eq)(oracles.chainId, chainId))).map((r) => from$13({
|
|
9194
|
+
address: oracles$1.address,
|
|
9195
|
+
price: oracles$1.price,
|
|
9196
|
+
blockNumber: oracles$1.blockNumber,
|
|
9197
|
+
chainId: oracles$1.chainId
|
|
9198
|
+
}).from(oracles$1).where((0, drizzle_orm.eq)(oracles$1.chainId, chainId))).map((r) => from$13({
|
|
9319
9199
|
chainId: r.chainId,
|
|
9320
9200
|
address: r.address,
|
|
9321
9201
|
price: r.price,
|
|
9322
9202
|
blockNumber: r.blockNumber
|
|
9323
9203
|
}));
|
|
9324
9204
|
},
|
|
9325
|
-
upsert: async (oracles
|
|
9326
|
-
if (oracles
|
|
9327
|
-
const rows = oracles
|
|
9205
|
+
upsert: async (oracles) => {
|
|
9206
|
+
if (oracles.length === 0) return;
|
|
9207
|
+
const rows = oracles.map((o) => ({
|
|
9328
9208
|
chainId: o.chainId,
|
|
9329
9209
|
address: o.address.toLowerCase(),
|
|
9330
9210
|
price: o.price !== null ? o.price.toString() : null,
|
|
9331
9211
|
blockNumber: o.blockNumber
|
|
9332
9212
|
}));
|
|
9333
9213
|
await db.transaction(async (dbTx) => {
|
|
9334
|
-
for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(oracles).values(batch).onConflictDoUpdate({
|
|
9335
|
-
target: [oracles.chainId, oracles.address],
|
|
9214
|
+
for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE$1)) await dbTx.insert(oracles$1).values(batch).onConflictDoUpdate({
|
|
9215
|
+
target: [oracles$1.chainId, oracles$1.address],
|
|
9336
9216
|
set: {
|
|
9337
|
-
price: drizzle_orm.sql`COALESCE(EXCLUDED.price, ${oracles.price})`,
|
|
9217
|
+
price: drizzle_orm.sql`COALESCE(EXCLUDED.price, ${oracles$1.price})`,
|
|
9338
9218
|
blockNumber: drizzle_orm.sql`CASE
|
|
9339
|
-
WHEN EXCLUDED.price IS NULL THEN ${oracles.blockNumber}
|
|
9219
|
+
WHEN EXCLUDED.price IS NULL THEN ${oracles$1.blockNumber}
|
|
9340
9220
|
ELSE EXCLUDED.block_number
|
|
9341
9221
|
END`,
|
|
9342
9222
|
updatedAt: drizzle_orm.sql`NOW()`
|
|
@@ -10375,23 +10255,11 @@ function createHttpClient(config) {
|
|
|
10375
10255
|
issues: []
|
|
10376
10256
|
};
|
|
10377
10257
|
};
|
|
10378
|
-
const getCallbackTypes = async (requestPayload) => {
|
|
10379
|
-
const response = await request("/v1/callbacks", {
|
|
10380
|
-
method: "POST",
|
|
10381
|
-
headers: { "content-type": "application/json" },
|
|
10382
|
-
body: JSON.stringify(requestPayload)
|
|
10383
|
-
});
|
|
10384
|
-
const json = await response.json();
|
|
10385
|
-
if (!response.ok) throw new Error(`Gatekeeper callbacks request failed: ${extractErrorMessage(json) ?? response.statusText}`);
|
|
10386
|
-
if (!("data" in json) || !Array.isArray(json.data)) throw new Error("Gatekeeper callbacks response is invalid.");
|
|
10387
|
-
return json.data;
|
|
10388
|
-
};
|
|
10389
10258
|
return {
|
|
10390
10259
|
baseUrl,
|
|
10391
10260
|
validate,
|
|
10392
10261
|
getConfigRules,
|
|
10393
|
-
isAllowed
|
|
10394
|
-
getCallbackTypes
|
|
10262
|
+
isAllowed
|
|
10395
10263
|
};
|
|
10396
10264
|
}
|
|
10397
10265
|
function mergeHeaders(base, extra) {
|
|
@@ -10520,6 +10388,7 @@ var Rules_exports = /* @__PURE__ */ __exportAll({
|
|
|
10520
10388
|
callback: () => callback,
|
|
10521
10389
|
chains: () => chains,
|
|
10522
10390
|
maturity: () => maturity,
|
|
10391
|
+
oracle: () => oracle,
|
|
10523
10392
|
sameMaker: () => sameMaker,
|
|
10524
10393
|
token: () => token,
|
|
10525
10394
|
validity: () => validity
|
|
@@ -10527,109 +10396,13 @@ var Rules_exports = /* @__PURE__ */ __exportAll({
|
|
|
10527
10396
|
/**
|
|
10528
10397
|
* set of rules to validate offers.
|
|
10529
10398
|
*
|
|
10530
|
-
* @param
|
|
10399
|
+
* @param _parameters - Validity parameters with chain and client
|
|
10531
10400
|
* @returns Array of validation rules to evaluate against offers
|
|
10532
10401
|
*/
|
|
10533
|
-
function validity(
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
if (callbackType !== Type$1.SellERC20Callback) return;
|
|
10538
|
-
const decoded = decode$2(callbackType, offer.callback.data);
|
|
10539
|
-
if (decoded.length === 0) return { message: "Callback data cannot be decoded or is empty." };
|
|
10540
|
-
if (callbackType === Type$1.SellERC20Callback) {
|
|
10541
|
-
const offerCollaterals = new Set(offer.collaterals.map((c) => c.asset.toLowerCase()));
|
|
10542
|
-
if (decoded.length !== offer.collaterals.length) return { message: `Sell callback collateral length mismatch. Expected ${offer.collaterals.length}, got ${decoded.length}.` };
|
|
10543
|
-
for (const { contract } of decoded) if (!offerCollaterals.has(contract.toLowerCase())) return { message: "Sell callback collateral is not part of offer collaterals." };
|
|
10544
|
-
}
|
|
10545
|
-
});
|
|
10546
|
-
const buyCallbackVaultInvalid = batch("buy_offers_callback_vault_invalid", "Validates that buy offers have valid vault callbacks registered in allowed factories with matching assets", async (offers) => {
|
|
10547
|
-
const validationIssues = /* @__PURE__ */ new Map();
|
|
10548
|
-
const offersByVaultAddress = /* @__PURE__ */ new Map();
|
|
10549
|
-
for (let i = 0; i < offers.length; i++) {
|
|
10550
|
-
const offer = offers[i];
|
|
10551
|
-
if (getCallbackType(client.chain.name, offer.callback.address) !== Type$1.BuyVaultV1Callback) continue;
|
|
10552
|
-
try {
|
|
10553
|
-
const callbackVaults = decodeBuyVaultV1Callback(offer.callback.data);
|
|
10554
|
-
for (const { contract } of callbackVaults) {
|
|
10555
|
-
const normalizedVaultAddress = contract.toLowerCase();
|
|
10556
|
-
if (!offersByVaultAddress.has(normalizedVaultAddress)) offersByVaultAddress.set(normalizedVaultAddress, []);
|
|
10557
|
-
offersByVaultAddress.get(normalizedVaultAddress).push({
|
|
10558
|
-
index: i,
|
|
10559
|
-
offer
|
|
10560
|
-
});
|
|
10561
|
-
}
|
|
10562
|
-
} catch (_) {}
|
|
10563
|
-
}
|
|
10564
|
-
const uniqueVaultAddresses = Array.from(offersByVaultAddress.keys());
|
|
10565
|
-
if (uniqueVaultAddresses.length === 0) return validationIssues;
|
|
10566
|
-
const allowedFactories = getCallback(client.chain.name, Type$1.BuyVaultV1Callback)?.vaultFactories.map((f) => f.toLowerCase());
|
|
10567
|
-
if (!allowedFactories) return validationIssues;
|
|
10568
|
-
const multicallContracts = [];
|
|
10569
|
-
for (const vaultAddress of uniqueVaultAddresses) {
|
|
10570
|
-
multicallContracts.push({
|
|
10571
|
-
address: vaultAddress,
|
|
10572
|
-
abi: ERC4626,
|
|
10573
|
-
functionName: "asset"
|
|
10574
|
-
});
|
|
10575
|
-
for (const factoryAddress of allowedFactories) multicallContracts.push({
|
|
10576
|
-
address: factoryAddress,
|
|
10577
|
-
abi: MetaMorphoFactory,
|
|
10578
|
-
functionName: "isMetaMorpho",
|
|
10579
|
-
args: [vaultAddress]
|
|
10580
|
-
});
|
|
10581
|
-
}
|
|
10582
|
-
const multicallResults = await (0, viem_actions.multicall)(client, {
|
|
10583
|
-
contracts: multicallContracts,
|
|
10584
|
-
allowFailure: true
|
|
10585
|
-
});
|
|
10586
|
-
const vaultAssetByAddress = /* @__PURE__ */ new Map();
|
|
10587
|
-
const registeredVaults = /* @__PURE__ */ new Set();
|
|
10588
|
-
const numberOfFactories = allowedFactories.length;
|
|
10589
|
-
let resultIndex = 0;
|
|
10590
|
-
for (const vaultAddress of uniqueVaultAddresses) {
|
|
10591
|
-
const assetCallResult = multicallResults[resultIndex++];
|
|
10592
|
-
const assetAddress = assetCallResult.status === "success" ? assetCallResult.result : null;
|
|
10593
|
-
vaultAssetByAddress.set(vaultAddress, assetAddress);
|
|
10594
|
-
let isRegisteredInFactory = false;
|
|
10595
|
-
for (let factoryIndex = 0; factoryIndex < numberOfFactories; factoryIndex++) {
|
|
10596
|
-
const factoryCallResult = multicallResults[resultIndex++];
|
|
10597
|
-
if (factoryCallResult.status === "success" && factoryCallResult.result === true) isRegisteredInFactory = true;
|
|
10598
|
-
}
|
|
10599
|
-
if (isRegisteredInFactory) registeredVaults.add(vaultAddress);
|
|
10600
|
-
}
|
|
10601
|
-
const uniqueOffers = /* @__PURE__ */ new Map();
|
|
10602
|
-
for (const offersArray of offersByVaultAddress.values()) for (const { index, offer } of offersArray) uniqueOffers.set(index, offer);
|
|
10603
|
-
for (const [index, offer] of uniqueOffers) try {
|
|
10604
|
-
const callbackVaults = decodeBuyVaultV1Callback(offer.callback.data);
|
|
10605
|
-
const vaultsWithIssues = [];
|
|
10606
|
-
for (const { contract } of callbackVaults) {
|
|
10607
|
-
const normalizedVaultAddress = contract.toLowerCase();
|
|
10608
|
-
const assetAddress = vaultAssetByAddress.get(normalizedVaultAddress);
|
|
10609
|
-
const isRegistered = registeredVaults.has(normalizedVaultAddress);
|
|
10610
|
-
const failureReasons = [];
|
|
10611
|
-
if (assetAddress === null) failureReasons.push("asset call failed");
|
|
10612
|
-
else if (assetAddress && assetAddress.toLowerCase() !== offer.loanToken.toLowerCase()) failureReasons.push("asset mismatch");
|
|
10613
|
-
if (!isRegistered) failureReasons.push("not registered in factory");
|
|
10614
|
-
if (failureReasons.length > 0) vaultsWithIssues.push({
|
|
10615
|
-
vaultAddress: contract,
|
|
10616
|
-
failureReasons: failureReasons.join(", ")
|
|
10617
|
-
});
|
|
10618
|
-
}
|
|
10619
|
-
if (vaultsWithIssues.length > 0) {
|
|
10620
|
-
const failureDetails = vaultsWithIssues.map((v) => `${v.vaultAddress} (${v.failureReasons})`).join("; ");
|
|
10621
|
-
validationIssues.set(index, { message: `Buy offer callback vaults are invalid: ${failureDetails}` });
|
|
10622
|
-
}
|
|
10623
|
-
} catch (_) {}
|
|
10624
|
-
return validationIssues;
|
|
10625
|
-
});
|
|
10626
|
-
return [
|
|
10627
|
-
single("expiry", "Validates that offer has not expired", (offer) => {
|
|
10628
|
-
if (offer.expiry < Math.floor(Date.now() / 1e3)) return { message: "Expiry mismatch" };
|
|
10629
|
-
}),
|
|
10630
|
-
sellErc20CallbackInvalid,
|
|
10631
|
-
buyCallbackVaultInvalid
|
|
10632
|
-
];
|
|
10402
|
+
function validity(_parameters) {
|
|
10403
|
+
return [single("expiry", "Validates that offer has not expired", (offer) => {
|
|
10404
|
+
if (offer.expiry < Math.floor(Date.now() / 1e3)) return { message: "Expiry mismatch" };
|
|
10405
|
+
})];
|
|
10633
10406
|
}
|
|
10634
10407
|
const chains = ({ chains }) => single("chain_ids", `Validates that offer chain is one of: [${chains.map((c) => c.id).join(", ")}]`, (offer) => {
|
|
10635
10408
|
const allowedChainIds = chains.map((c) => c.id);
|
|
@@ -10639,12 +10412,10 @@ const maturity = ({ maturities }) => single("maturity", `Validates that offer ma
|
|
|
10639
10412
|
const allowedMaturities = maturities.map((m) => from$16(m));
|
|
10640
10413
|
if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}` };
|
|
10641
10414
|
});
|
|
10642
|
-
const callback = ({ callbacks
|
|
10643
|
-
if (isEmptyCallback(offer)
|
|
10644
|
-
if (isEmptyCallback(offer) &&
|
|
10645
|
-
if (
|
|
10646
|
-
if (!allowedAddresses.includes(offer.callback.address?.toLowerCase())) return { message: `Callback address ${offer.callback.address} is not allowed.` };
|
|
10647
|
-
}
|
|
10415
|
+
const callback = ({ callbacks }) => single("callback", `Validates callbacks: buy empty callback is ${callbacks.includes(Type$1.BuyWithEmptyCallback) ? "allowed" : "not allowed"}; sell empty callback is ${callbacks.includes(Type$1.SellWithEmptyCallback) ? "allowed" : "not allowed"}; non-empty callbacks are rejected`, (offer) => {
|
|
10416
|
+
if (!isEmptyCallback(offer)) return { message: "Non-empty callbacks are not supported." };
|
|
10417
|
+
if (isEmptyCallback(offer) && offer.buy && !callbacks.includes(Type$1.BuyWithEmptyCallback)) return { message: "Buy offers with empty callback not allowed." };
|
|
10418
|
+
if (isEmptyCallback(offer) && !offer.buy && !callbacks.includes(Type$1.SellWithEmptyCallback)) return { message: "Sell offers with empty callback not allowed." };
|
|
10648
10419
|
});
|
|
10649
10420
|
/**
|
|
10650
10421
|
* A validation rule that checks if the offer's tokens are allowed for its chain.
|
|
@@ -10658,6 +10429,16 @@ const token = ({ assetsByChainId }) => single("token", "Validates that offer loa
|
|
|
10658
10429
|
if (offer.collaterals.some((collateral) => !allowedAssets.includes(collateral.asset.toLowerCase()))) return { message: "Collateral is not allowed" };
|
|
10659
10430
|
});
|
|
10660
10431
|
/**
|
|
10432
|
+
* A validation rule that checks if the offer's oracle addresses are allowed for its chain.
|
|
10433
|
+
* @param oraclesByChainId - Allowed oracles indexed by chain id.
|
|
10434
|
+
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
10435
|
+
*/
|
|
10436
|
+
const oracle = ({ oraclesByChainId }) => single("oracle", "Validates that offer collateral oracles are in the allowed oracle list for the offer chain", (offer) => {
|
|
10437
|
+
const allowedOracles = oraclesByChainId[offer.chainId]?.map((oracle) => oracle.toLowerCase());
|
|
10438
|
+
if (!allowedOracles || allowedOracles.length === 0) return { message: `No allowed oracles for chain ${offer.chainId}` };
|
|
10439
|
+
if (offer.collaterals.some((collateral) => !allowedOracles.includes(collateral.oracle.toLowerCase()))) return { message: "Oracle is not allowed" };
|
|
10440
|
+
});
|
|
10441
|
+
/**
|
|
10661
10442
|
* A batch validation rule that ensures all offers in a tree have the same maker address.
|
|
10662
10443
|
* Returns an issue only for the first non-conforming offer.
|
|
10663
10444
|
* This rule is signing-agnostic; signer verification is handled at the collector level.
|
|
@@ -10688,21 +10469,22 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
|
|
|
10688
10469
|
//#region src/gatekeeper/morphoRules.ts
|
|
10689
10470
|
const morphoRules = (chains$3) => {
|
|
10690
10471
|
const assetsByChainId = {};
|
|
10691
|
-
|
|
10472
|
+
const oraclesByChainId = {};
|
|
10473
|
+
for (const chain of chains$3) {
|
|
10474
|
+
assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
|
|
10475
|
+
oraclesByChainId[chain.id] = oracles[chain.id.toString()] ?? [];
|
|
10476
|
+
}
|
|
10692
10477
|
return [
|
|
10693
10478
|
sameMaker(),
|
|
10694
10479
|
amountMutualExclusivity(),
|
|
10695
10480
|
chains({ chains: chains$3 }),
|
|
10696
10481
|
maturity({ maturities: [MaturityType.EndOfMonth, MaturityType.EndOfNextMonth] }),
|
|
10697
10482
|
callback({
|
|
10698
|
-
callbacks: [
|
|
10699
|
-
|
|
10700
|
-
Type$1.BuyVaultV1Callback,
|
|
10701
|
-
Type$1.SellERC20Callback
|
|
10702
|
-
],
|
|
10703
|
-
allowedAddresses: chains$3.flatMap((c) => getCallbackAddresses(c.name))
|
|
10483
|
+
callbacks: [Type$1.BuyWithEmptyCallback, Type$1.SellWithEmptyCallback],
|
|
10484
|
+
allowedAddresses: []
|
|
10704
10485
|
}),
|
|
10705
|
-
token({ assetsByChainId })
|
|
10486
|
+
token({ assetsByChainId }),
|
|
10487
|
+
oracle({ oraclesByChainId })
|
|
10706
10488
|
];
|
|
10707
10489
|
};
|
|
10708
10490
|
|
|
@@ -10909,12 +10691,6 @@ Object.defineProperty(exports, 'Callback', {
|
|
|
10909
10691
|
return Callback_exports;
|
|
10910
10692
|
}
|
|
10911
10693
|
});
|
|
10912
|
-
Object.defineProperty(exports, 'CallbacksController', {
|
|
10913
|
-
enumerable: true,
|
|
10914
|
-
get: function () {
|
|
10915
|
-
return CallbacksController;
|
|
10916
|
-
}
|
|
10917
|
-
});
|
|
10918
10694
|
Object.defineProperty(exports, 'Chain', {
|
|
10919
10695
|
enumerable: true,
|
|
10920
10696
|
get: function () {
|