@morpho-dev/router 0.9.0 → 0.10.0
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 +1896 -784
- package/dist/drizzle/migrations/0027_debt-to-transfers.sql +239 -0
- package/dist/drizzle/migrations/0028_obligation_id_and_keys.sql +247 -0
- package/dist/drizzle/migrations/0029_collateral-positions.sql +248 -0
- package/dist/drizzle/migrations/0030_remove_chain_id_from_offer.sql +37 -0
- package/dist/drizzle/migrations/meta/0027_snapshot.json +1581 -0
- package/dist/drizzle/migrations/meta/0028_snapshot.json +1632 -0
- package/dist/drizzle/migrations/meta/0029_snapshot.json +1619 -0
- package/dist/drizzle/migrations/meta/0030_snapshot.json +1652 -0
- package/dist/drizzle/migrations/meta/_journal.json +28 -0
- package/dist/index.browser.d.mts +464 -279
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +460 -275
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +3965 -3845
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +3964 -3850
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +920 -409
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +920 -409
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +1952 -1019
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +1949 -1022
- package/dist/index.node.mjs.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import os from "node:os";
|
|
|
16
16
|
import path, { dirname, resolve } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
19
|
-
import { bytesToHex, createPublicClient, createWalletClient, decodeAbiParameters, encodeAbiParameters, erc20Abi, getAddress, hashTypedData, hexToBytes, http, isAddress, isHex, keccak256, maxUint256, numberToHex, pad, parseAbi, parseEventLogs, publicActions, recoverAddress, stringify, toHex, zeroAddress } from "viem";
|
|
19
|
+
import { bytesToHex, concatHex, createPublicClient, createWalletClient, decodeAbiParameters, encodeAbiParameters, erc20Abi, getAddress, hashTypedData, hexToBytes, http, isAddress, isHex, keccak256, maxUint256, numberToHex, pad, parseAbi, parseEventLogs, publicActions, recoverAddress, stringify, toHex, zeroAddress } from "viem";
|
|
20
20
|
import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts";
|
|
21
21
|
import { getBlock, getBlockNumber, getLogs, multicall } from "viem/actions";
|
|
22
22
|
import { anvil, base, mainnet } from "viem/chains";
|
|
@@ -39,7 +39,7 @@ import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
|
39
39
|
import { drizzle as drizzle$1 } from "drizzle-orm/pglite";
|
|
40
40
|
import { migrate as migrate$1 } from "drizzle-orm/pglite/migrator";
|
|
41
41
|
import { Pool } from "pg";
|
|
42
|
-
import { and, asc, desc, eq, gt, gte, inArray, lte, ne, sql } from "drizzle-orm";
|
|
42
|
+
import { and, asc, desc, eq, gt, gte, inArray, lte, ne, or, sql } from "drizzle-orm";
|
|
43
43
|
import { bigint, boolean, foreignKey, index, integer, numeric, pgSchema, primaryKey, serial, text, timestamp, uniqueIndex, varchar } from "drizzle-orm/pg-core";
|
|
44
44
|
import { parse } from "smol-toml";
|
|
45
45
|
import { cors } from "hono/cors";
|
|
@@ -152,7 +152,7 @@ function startActiveSpan(tracer, name, fn) {
|
|
|
152
152
|
//#endregion
|
|
153
153
|
//#region package.json
|
|
154
154
|
var name = "@morpho-dev/router";
|
|
155
|
-
var version = "0.
|
|
155
|
+
var version = "0.10.0";
|
|
156
156
|
var description = "Router package for Morpho protocol";
|
|
157
157
|
|
|
158
158
|
//#endregion
|
|
@@ -264,7 +264,7 @@ const ChainId = {
|
|
|
264
264
|
const chainNames = Object.keys(ChainId).map((key) => key.toLowerCase());
|
|
265
265
|
const chainIds = Object.values(ChainId);
|
|
266
266
|
const chainNameLookup = new Map(Object.entries(ChainId).map(([key, value]) => [value, key.toLowerCase()]));
|
|
267
|
-
const chains$
|
|
267
|
+
const chains$1 = {
|
|
268
268
|
ethereum: {
|
|
269
269
|
...mainnet,
|
|
270
270
|
id: ChainId.ETHEREUM,
|
|
@@ -301,16 +301,16 @@ const chains$2 = {
|
|
|
301
301
|
name: "base",
|
|
302
302
|
custom: {
|
|
303
303
|
morpho: {
|
|
304
|
-
address: "
|
|
305
|
-
blockCreated:
|
|
304
|
+
address: "0x3F067BC9D8898F6ec02D6480c3fF1026E512BcBF",
|
|
305
|
+
blockCreated: 41799989
|
|
306
306
|
},
|
|
307
307
|
morphoBlue: {
|
|
308
308
|
address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
|
|
309
309
|
blockCreated: 0
|
|
310
310
|
},
|
|
311
311
|
mempool: {
|
|
312
|
-
address: "
|
|
313
|
-
blockCreated:
|
|
312
|
+
address: "0xd25c7512EA5035bef4F18c708C0862E1B6151765",
|
|
313
|
+
blockCreated: 41800226
|
|
314
314
|
},
|
|
315
315
|
vaults: { factories: {
|
|
316
316
|
v1_0: {
|
|
@@ -513,7 +513,7 @@ async function deployContracts(parameters) {
|
|
|
513
513
|
const deployClient = client ?? createWalletClient({
|
|
514
514
|
account,
|
|
515
515
|
chain: {
|
|
516
|
-
...chains$
|
|
516
|
+
...chains$1.anvil,
|
|
517
517
|
id: chainId
|
|
518
518
|
},
|
|
519
519
|
transport: http(rpcUrl)
|
|
@@ -1081,7 +1081,7 @@ async function readLogs(parameters) {
|
|
|
1081
1081
|
return readFile(logPath, "utf8");
|
|
1082
1082
|
}
|
|
1083
1083
|
async function resolveForkContracts(parameters) {
|
|
1084
|
-
const chain = parameters.forkChain ? chains$
|
|
1084
|
+
const chain = parameters.forkChain ? chains$1[parameters.forkChain] : void 0;
|
|
1085
1085
|
const mempoolAddress = parameters.mempoolAddress ?? chain?.custom.mempool.address;
|
|
1086
1086
|
const morphoAddress = parameters.morphoAddress ?? chain?.custom.morpho.address;
|
|
1087
1087
|
const multicall3Address = parameters.multicall3Address ?? chain?.contracts?.multicall3?.address;
|
|
@@ -1112,8 +1112,8 @@ async function resolveForkContracts(parameters) {
|
|
|
1112
1112
|
}
|
|
1113
1113
|
async function validateForkContracts(parameters) {
|
|
1114
1114
|
const client = createPublicClient({
|
|
1115
|
-
chain: parameters.forkChain ? chains$
|
|
1116
|
-
...chains$
|
|
1115
|
+
chain: parameters.forkChain ? chains$1[parameters.forkChain] : {
|
|
1116
|
+
...chains$1.anvil,
|
|
1117
1117
|
id: parameters.chainId
|
|
1118
1118
|
},
|
|
1119
1119
|
transport: http(parameters.rpcUrl)
|
|
@@ -1470,11 +1470,12 @@ async function run(parameters) {
|
|
|
1470
1470
|
* @returns Gatekeeper instance. {@link Gatekeeper}
|
|
1471
1471
|
*/
|
|
1472
1472
|
function create$21(parameters) {
|
|
1473
|
-
const { rules } = parameters;
|
|
1474
|
-
return { isAllowed: async (
|
|
1473
|
+
const { rules: rulesFactory } = parameters;
|
|
1474
|
+
return { isAllowed: async (parameters) => {
|
|
1475
|
+
const { offers, chainId } = parameters;
|
|
1475
1476
|
return await run({
|
|
1476
1477
|
items: offers,
|
|
1477
|
-
rules
|
|
1478
|
+
rules: rulesFactory(chainId)
|
|
1478
1479
|
});
|
|
1479
1480
|
} };
|
|
1480
1481
|
}
|
|
@@ -1918,6 +1919,20 @@ const LLTVSchema = z$2.bigint({ coerce: true }).refine((lltv) => {
|
|
|
1918
1919
|
|
|
1919
1920
|
//#endregion
|
|
1920
1921
|
//#region src/core/Collateral.ts
|
|
1922
|
+
const abi$1 = [
|
|
1923
|
+
{
|
|
1924
|
+
type: "address",
|
|
1925
|
+
name: "token"
|
|
1926
|
+
},
|
|
1927
|
+
{
|
|
1928
|
+
type: "uint256",
|
|
1929
|
+
name: "lltv"
|
|
1930
|
+
},
|
|
1931
|
+
{
|
|
1932
|
+
type: "address",
|
|
1933
|
+
name: "oracle"
|
|
1934
|
+
}
|
|
1935
|
+
];
|
|
1921
1936
|
const CollateralSchema = z$2.object({
|
|
1922
1937
|
asset: z$2.string().transform(transformAddress),
|
|
1923
1938
|
oracle: z$2.string().transform(transformAddress),
|
|
@@ -2028,11 +2043,29 @@ function stringifyBigint(value) {
|
|
|
2028
2043
|
//#endregion
|
|
2029
2044
|
//#region src/core/Obligation.ts
|
|
2030
2045
|
const ObligationSchema = z$2.object({
|
|
2031
|
-
chainId: z$2.number().min(0).max(Number.MAX_SAFE_INTEGER),
|
|
2032
2046
|
loanToken: z$2.string().transform(transformAddress),
|
|
2033
2047
|
collaterals: CollateralsSchema,
|
|
2034
2048
|
maturity: MaturitySchema
|
|
2035
2049
|
});
|
|
2050
|
+
const abi = [
|
|
2051
|
+
{
|
|
2052
|
+
type: "address",
|
|
2053
|
+
name: "loanToken"
|
|
2054
|
+
},
|
|
2055
|
+
{
|
|
2056
|
+
type: "tuple[]",
|
|
2057
|
+
name: "collaterals",
|
|
2058
|
+
components: abi$1
|
|
2059
|
+
},
|
|
2060
|
+
{
|
|
2061
|
+
type: "uint256",
|
|
2062
|
+
name: "maturity"
|
|
2063
|
+
}
|
|
2064
|
+
];
|
|
2065
|
+
const tupleAbi = [{
|
|
2066
|
+
type: "tuple",
|
|
2067
|
+
components: abi
|
|
2068
|
+
}];
|
|
2036
2069
|
/**
|
|
2037
2070
|
* Creates an obligation from the given parameters.
|
|
2038
2071
|
* @constructor
|
|
@@ -2043,7 +2076,6 @@ const ObligationSchema = z$2.object({
|
|
|
2043
2076
|
* @example
|
|
2044
2077
|
* ```ts
|
|
2045
2078
|
* const obligation = Obligation.from({
|
|
2046
|
-
* chainId: 1,
|
|
2047
2079
|
* loanToken: privateKeyToAccount(generatePrivateKey()).address,
|
|
2048
2080
|
* collaterals: [
|
|
2049
2081
|
* Collateral.from({
|
|
@@ -2063,7 +2095,6 @@ function from$13(parameters) {
|
|
|
2063
2095
|
maturity: from$16(parameters.maturity)
|
|
2064
2096
|
});
|
|
2065
2097
|
return {
|
|
2066
|
-
chainId: parsedObligation.chainId,
|
|
2067
2098
|
loanToken: parsedObligation.loanToken.toLowerCase(),
|
|
2068
2099
|
collaterals: parsedObligation.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)),
|
|
2069
2100
|
maturity: parsedObligation.maturity
|
|
@@ -2073,49 +2104,27 @@ function from$13(parameters) {
|
|
|
2073
2104
|
}
|
|
2074
2105
|
}
|
|
2075
2106
|
/**
|
|
2076
|
-
* Calculates
|
|
2077
|
-
* The
|
|
2107
|
+
* Calculates a canonical key for an obligation payload.
|
|
2108
|
+
* The key is computed as keccak256(abi.encode(loanToken, collaterals, maturity)).
|
|
2078
2109
|
* @throws If the collaterals are not sorted alphabetically by address. {@link CollateralsAreNotSortedError}
|
|
2079
|
-
* @param parameters - {@link
|
|
2080
|
-
* @returns The obligation
|
|
2110
|
+
* @param parameters - {@link key.Parameters}
|
|
2111
|
+
* @returns The obligation key as a 32-byte hex string. {@link key.ReturnType}
|
|
2081
2112
|
*
|
|
2082
2113
|
* @example
|
|
2083
2114
|
* ```ts
|
|
2084
2115
|
* const obligation = Obligation.random();
|
|
2085
|
-
* const
|
|
2086
|
-
* console.log(
|
|
2116
|
+
* const key = Obligation.key(obligation);
|
|
2117
|
+
* console.log(key); // 0x1234567890123456789012345678901234567890123456789012345678901234
|
|
2087
2118
|
* ```
|
|
2088
2119
|
*/
|
|
2089
|
-
function
|
|
2120
|
+
function key(parameters) {
|
|
2090
2121
|
let lastAsset = "";
|
|
2091
2122
|
for (const collateral of parameters.collaterals) {
|
|
2092
2123
|
const newAsset = collateral.asset.toLowerCase();
|
|
2093
2124
|
if (newAsset.localeCompare(lastAsset) < 0) throw new CollateralsAreNotSortedError();
|
|
2094
2125
|
lastAsset = newAsset;
|
|
2095
2126
|
}
|
|
2096
|
-
return keccak256(encodeAbiParameters([
|
|
2097
|
-
{ type: "uint256" },
|
|
2098
|
-
{ type: "address" },
|
|
2099
|
-
{
|
|
2100
|
-
type: "tuple[]",
|
|
2101
|
-
components: [
|
|
2102
|
-
{
|
|
2103
|
-
type: "address",
|
|
2104
|
-
name: "token"
|
|
2105
|
-
},
|
|
2106
|
-
{
|
|
2107
|
-
type: "uint256",
|
|
2108
|
-
name: "lltv"
|
|
2109
|
-
},
|
|
2110
|
-
{
|
|
2111
|
-
type: "address",
|
|
2112
|
-
name: "oracle"
|
|
2113
|
-
}
|
|
2114
|
-
]
|
|
2115
|
-
},
|
|
2116
|
-
{ type: "uint256" }
|
|
2117
|
-
], [
|
|
2118
|
-
BigInt(parameters.chainId),
|
|
2127
|
+
return keccak256(encodeAbiParameters(abi, [
|
|
2119
2128
|
parameters.loanToken.toLowerCase(),
|
|
2120
2129
|
parameters.collaterals.map((c) => ({
|
|
2121
2130
|
token: c.asset.toLowerCase(),
|
|
@@ -2138,6 +2147,43 @@ var CollateralsAreNotSortedError = class extends BaseError {
|
|
|
2138
2147
|
}
|
|
2139
2148
|
};
|
|
2140
2149
|
|
|
2150
|
+
//#endregion
|
|
2151
|
+
//#region src/core/Id.ts
|
|
2152
|
+
const CREATION_CODE_PREFIX = "0x603f380380603f5f395ff3";
|
|
2153
|
+
/**
|
|
2154
|
+
* Builds the same creation code as `IdLib.creationCode` in Solidity.
|
|
2155
|
+
*
|
|
2156
|
+
* Layout: `prefix (11 bytes) + chainId (32 bytes) + morphoV2 (20 bytes) + abi.encode(obligation)`.
|
|
2157
|
+
*
|
|
2158
|
+
* @param parameters - {@link creationCode.Parameters}
|
|
2159
|
+
* @returns The CREATE2 init code bytes. {@link creationCode.ReturnType}
|
|
2160
|
+
*/
|
|
2161
|
+
function creationCode(parameters) {
|
|
2162
|
+
const encodedObligation = encodeAbiParameters(tupleAbi, [{
|
|
2163
|
+
loanToken: parameters.obligation.loanToken.toLowerCase(),
|
|
2164
|
+
collaterals: parameters.obligation.collaterals.map((collateral) => ({
|
|
2165
|
+
token: collateral.asset.toLowerCase(),
|
|
2166
|
+
lltv: collateral.lltv,
|
|
2167
|
+
oracle: collateral.oracle.toLowerCase()
|
|
2168
|
+
})),
|
|
2169
|
+
maturity: BigInt(parameters.obligation.maturity)
|
|
2170
|
+
}]);
|
|
2171
|
+
return concatHex([
|
|
2172
|
+
CREATION_CODE_PREFIX,
|
|
2173
|
+
numberToHex(BigInt(parameters.chainId), { size: 32 }),
|
|
2174
|
+
parameters.morphoV2.toLowerCase(),
|
|
2175
|
+
encodedObligation
|
|
2176
|
+
]);
|
|
2177
|
+
}
|
|
2178
|
+
/**
|
|
2179
|
+
* Computes the same id as `IdLib.toId` in Solidity.
|
|
2180
|
+
* @param parameters - {@link toId.Parameters}
|
|
2181
|
+
* @returns The obligation id. {@link toId.ReturnType}
|
|
2182
|
+
*/
|
|
2183
|
+
function toId(parameters) {
|
|
2184
|
+
return keccak256(creationCode(parameters));
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2141
2187
|
//#endregion
|
|
2142
2188
|
//#region src/core/Offer.ts
|
|
2143
2189
|
/** Internal symbol for caching the computed hash. */
|
|
@@ -2168,7 +2214,6 @@ const OfferSchema = () => {
|
|
|
2168
2214
|
z$2.bigint()
|
|
2169
2215
|
]).optional().default("0x0000000000000000000000000000000000000000000000000000000000000000").transform(transformBytes32),
|
|
2170
2216
|
buy: z$2.boolean(),
|
|
2171
|
-
chainId: z$2.number().min(0).max(Number.MAX_SAFE_INTEGER),
|
|
2172
2217
|
loanToken: z$2.string().transform(transformAddress),
|
|
2173
2218
|
collaterals: CollateralsSchema,
|
|
2174
2219
|
callback: z$2.object({
|
|
@@ -2237,7 +2282,6 @@ const serialize = (offer) => ({
|
|
|
2237
2282
|
group: offer.group,
|
|
2238
2283
|
session: offer.session,
|
|
2239
2284
|
buy: offer.buy,
|
|
2240
|
-
chainId: offer.chainId,
|
|
2241
2285
|
loanToken: offer.loanToken,
|
|
2242
2286
|
collaterals: offer.collaterals.map((c) => ({
|
|
2243
2287
|
asset: c.asset,
|
|
@@ -2258,7 +2302,6 @@ const serialize = (offer) => ({
|
|
|
2258
2302
|
* @returns {Offer} A randomly generated Offer object.
|
|
2259
2303
|
*/
|
|
2260
2304
|
function random(config) {
|
|
2261
|
-
const chain = config?.chains ? config.chains[int(config.chains.length)] : chains$2.ethereum;
|
|
2262
2305
|
const loanToken = config?.loanTokens ? config.loanTokens[int(config.loanTokens.length)] : address();
|
|
2263
2306
|
const collateralCandidates = config?.collateralTokens ? config.collateralTokens.filter((a) => a !== loanToken) : [address()];
|
|
2264
2307
|
collateralCandidates[int(collateralCandidates.length)];
|
|
@@ -2304,7 +2347,6 @@ function random(config) {
|
|
|
2304
2347
|
group: config?.group ?? hex(32),
|
|
2305
2348
|
session: config?.session ?? hex(32),
|
|
2306
2349
|
buy,
|
|
2307
|
-
chainId: chain.id,
|
|
2308
2350
|
loanToken,
|
|
2309
2351
|
collaterals: config?.collaterals ?? Array.from({ length: int(3) + 1 }, () => ({
|
|
2310
2352
|
...random$1(),
|
|
@@ -2324,150 +2366,138 @@ const weightedChoice = (pairs) => {
|
|
|
2324
2366
|
return pairs[0][0];
|
|
2325
2367
|
};
|
|
2326
2368
|
/**
|
|
2327
|
-
*
|
|
2328
|
-
*
|
|
2329
|
-
*
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
chainId: BigInt(chainId),
|
|
2333
|
-
verifyingContract: zeroAddress
|
|
2334
|
-
});
|
|
2335
|
-
/**
|
|
2336
|
-
* The EIP-712 types for the offer.
|
|
2337
|
-
* @warning The ordering of the types should NEVER be changed. The offer hash is computed based on the order of the types.
|
|
2338
|
-
* @returns The EIP-712 types.
|
|
2369
|
+
* Computes the canonical chain-agnostic offer hash.
|
|
2370
|
+
* The hash is `keccak256(abi.encode(offer))` using {@link encode}.
|
|
2371
|
+
*
|
|
2372
|
+
* @param offer - Offer payload to hash.
|
|
2373
|
+
* @returns 32-byte offer hash.
|
|
2339
2374
|
*/
|
|
2340
|
-
const types = {
|
|
2341
|
-
EIP712Domain: [{
|
|
2342
|
-
name: "chainId",
|
|
2343
|
-
type: "uint256"
|
|
2344
|
-
}, {
|
|
2345
|
-
name: "verifyingContract",
|
|
2346
|
-
type: "address"
|
|
2347
|
-
}],
|
|
2348
|
-
Offer: [
|
|
2349
|
-
{
|
|
2350
|
-
name: "maker",
|
|
2351
|
-
type: "address"
|
|
2352
|
-
},
|
|
2353
|
-
{
|
|
2354
|
-
name: "assets",
|
|
2355
|
-
type: "uint256"
|
|
2356
|
-
},
|
|
2357
|
-
{
|
|
2358
|
-
name: "obligationUnits",
|
|
2359
|
-
type: "uint256"
|
|
2360
|
-
},
|
|
2361
|
-
{
|
|
2362
|
-
name: "obligationShares",
|
|
2363
|
-
type: "uint256"
|
|
2364
|
-
},
|
|
2365
|
-
{
|
|
2366
|
-
name: "tick",
|
|
2367
|
-
type: "uint256"
|
|
2368
|
-
},
|
|
2369
|
-
{
|
|
2370
|
-
name: "maturity",
|
|
2371
|
-
type: "uint256"
|
|
2372
|
-
},
|
|
2373
|
-
{
|
|
2374
|
-
name: "expiry",
|
|
2375
|
-
type: "uint256"
|
|
2376
|
-
},
|
|
2377
|
-
{
|
|
2378
|
-
name: "group",
|
|
2379
|
-
type: "bytes32"
|
|
2380
|
-
},
|
|
2381
|
-
{
|
|
2382
|
-
name: "session",
|
|
2383
|
-
type: "bytes32"
|
|
2384
|
-
},
|
|
2385
|
-
{
|
|
2386
|
-
name: "buy",
|
|
2387
|
-
type: "bool"
|
|
2388
|
-
},
|
|
2389
|
-
{
|
|
2390
|
-
name: "loanToken",
|
|
2391
|
-
type: "address"
|
|
2392
|
-
},
|
|
2393
|
-
{
|
|
2394
|
-
name: "collaterals",
|
|
2395
|
-
type: "Collateral[]"
|
|
2396
|
-
},
|
|
2397
|
-
{
|
|
2398
|
-
name: "callback",
|
|
2399
|
-
type: "Callback"
|
|
2400
|
-
},
|
|
2401
|
-
{
|
|
2402
|
-
name: "receiverIfMakerIsSeller",
|
|
2403
|
-
type: "address"
|
|
2404
|
-
}
|
|
2405
|
-
],
|
|
2406
|
-
Collateral: [
|
|
2407
|
-
{
|
|
2408
|
-
name: "asset",
|
|
2409
|
-
type: "address"
|
|
2410
|
-
},
|
|
2411
|
-
{
|
|
2412
|
-
name: "oracle",
|
|
2413
|
-
type: "address"
|
|
2414
|
-
},
|
|
2415
|
-
{
|
|
2416
|
-
name: "lltv",
|
|
2417
|
-
type: "uint256"
|
|
2418
|
-
}
|
|
2419
|
-
],
|
|
2420
|
-
Callback: [{
|
|
2421
|
-
name: "address",
|
|
2422
|
-
type: "address"
|
|
2423
|
-
}, {
|
|
2424
|
-
name: "data",
|
|
2425
|
-
type: "bytes"
|
|
2426
|
-
}]
|
|
2427
|
-
};
|
|
2428
2375
|
function hash(offer) {
|
|
2429
2376
|
const cached = offer[HASH_CACHE];
|
|
2430
2377
|
if (cached) return cached;
|
|
2431
|
-
const computed =
|
|
2432
|
-
domain: domain(offer.chainId),
|
|
2433
|
-
message: {
|
|
2434
|
-
maker: offer.maker.toLowerCase(),
|
|
2435
|
-
assets: offer.assets,
|
|
2436
|
-
obligationUnits: offer.obligationUnits,
|
|
2437
|
-
obligationShares: offer.obligationShares,
|
|
2438
|
-
tick: BigInt(offer.tick),
|
|
2439
|
-
maturity: BigInt(offer.maturity),
|
|
2440
|
-
expiry: BigInt(offer.expiry),
|
|
2441
|
-
group: offer.group,
|
|
2442
|
-
session: offer.session,
|
|
2443
|
-
buy: offer.buy,
|
|
2444
|
-
loanToken: offer.loanToken.toLowerCase(),
|
|
2445
|
-
collaterals: offer.collaterals,
|
|
2446
|
-
callback: {
|
|
2447
|
-
address: offer.callback.address.toLowerCase(),
|
|
2448
|
-
data: offer.callback.data
|
|
2449
|
-
},
|
|
2450
|
-
receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase()
|
|
2451
|
-
},
|
|
2452
|
-
primaryType: "Offer",
|
|
2453
|
-
types
|
|
2454
|
-
});
|
|
2378
|
+
const computed = keccak256(encode(offer));
|
|
2455
2379
|
offer[HASH_CACHE] = computed;
|
|
2456
2380
|
return computed;
|
|
2457
2381
|
}
|
|
2458
2382
|
/**
|
|
2459
|
-
* Calculates the obligation id for an offer
|
|
2460
|
-
* The id is computed
|
|
2383
|
+
* Calculates the onchain obligation id for an offer.
|
|
2384
|
+
* The id is computed with {@link Id.toId}.
|
|
2461
2385
|
* @param offer - The offer to calculate the obligation id for.
|
|
2386
|
+
* @param parameters - The chain context used by the onchain id function.
|
|
2462
2387
|
* @returns The obligation id as a 32-byte hex string.
|
|
2463
2388
|
*/
|
|
2464
|
-
function obligationId(offer) {
|
|
2465
|
-
return
|
|
2466
|
-
chainId:
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2389
|
+
function obligationId(offer, parameters) {
|
|
2390
|
+
return toId({
|
|
2391
|
+
chainId: parameters.chainId,
|
|
2392
|
+
morphoV2: parameters.morphoV2,
|
|
2393
|
+
obligation: from$13({
|
|
2394
|
+
loanToken: offer.loanToken,
|
|
2395
|
+
collaterals: offer.collaterals,
|
|
2396
|
+
maturity: offer.maturity
|
|
2397
|
+
})
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
const OfferAbi = [
|
|
2401
|
+
{
|
|
2402
|
+
name: "maker",
|
|
2403
|
+
type: "address"
|
|
2404
|
+
},
|
|
2405
|
+
{
|
|
2406
|
+
name: "assets",
|
|
2407
|
+
type: "uint256"
|
|
2408
|
+
},
|
|
2409
|
+
{
|
|
2410
|
+
name: "obligationUnits",
|
|
2411
|
+
type: "uint256"
|
|
2412
|
+
},
|
|
2413
|
+
{
|
|
2414
|
+
name: "obligationShares",
|
|
2415
|
+
type: "uint256"
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
name: "tick",
|
|
2419
|
+
type: "uint256"
|
|
2420
|
+
},
|
|
2421
|
+
{
|
|
2422
|
+
name: "maturity",
|
|
2423
|
+
type: "uint256"
|
|
2424
|
+
},
|
|
2425
|
+
{
|
|
2426
|
+
name: "expiry",
|
|
2427
|
+
type: "uint256"
|
|
2428
|
+
},
|
|
2429
|
+
{
|
|
2430
|
+
name: "group",
|
|
2431
|
+
type: "bytes32"
|
|
2432
|
+
},
|
|
2433
|
+
{
|
|
2434
|
+
name: "session",
|
|
2435
|
+
type: "bytes32"
|
|
2436
|
+
},
|
|
2437
|
+
{
|
|
2438
|
+
name: "buy",
|
|
2439
|
+
type: "bool"
|
|
2440
|
+
},
|
|
2441
|
+
{
|
|
2442
|
+
name: "loanToken",
|
|
2443
|
+
type: "address"
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
name: "start",
|
|
2447
|
+
type: "uint256"
|
|
2448
|
+
},
|
|
2449
|
+
{
|
|
2450
|
+
name: "collaterals",
|
|
2451
|
+
type: "tuple[]",
|
|
2452
|
+
components: [
|
|
2453
|
+
{
|
|
2454
|
+
name: "asset",
|
|
2455
|
+
type: "address"
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
name: "oracle",
|
|
2459
|
+
type: "address"
|
|
2460
|
+
},
|
|
2461
|
+
{
|
|
2462
|
+
name: "lltv",
|
|
2463
|
+
type: "uint256"
|
|
2464
|
+
}
|
|
2465
|
+
]
|
|
2466
|
+
},
|
|
2467
|
+
{
|
|
2468
|
+
name: "callback",
|
|
2469
|
+
type: "tuple",
|
|
2470
|
+
components: [{
|
|
2471
|
+
name: "address",
|
|
2472
|
+
type: "address"
|
|
2473
|
+
}, {
|
|
2474
|
+
name: "data",
|
|
2475
|
+
type: "bytes"
|
|
2476
|
+
}]
|
|
2477
|
+
},
|
|
2478
|
+
{
|
|
2479
|
+
name: "receiverIfMakerIsSeller",
|
|
2480
|
+
type: "address"
|
|
2481
|
+
}
|
|
2482
|
+
];
|
|
2483
|
+
function encode(offer) {
|
|
2484
|
+
return encodeAbiParameters(OfferAbi, [
|
|
2485
|
+
offer.maker,
|
|
2486
|
+
offer.assets,
|
|
2487
|
+
offer.obligationUnits,
|
|
2488
|
+
offer.obligationShares,
|
|
2489
|
+
BigInt(offer.tick),
|
|
2490
|
+
BigInt(offer.maturity),
|
|
2491
|
+
BigInt(offer.expiry),
|
|
2492
|
+
offer.group,
|
|
2493
|
+
offer.session,
|
|
2494
|
+
offer.buy,
|
|
2495
|
+
offer.loanToken,
|
|
2496
|
+
BigInt(offer.start),
|
|
2497
|
+
offer.collaterals,
|
|
2498
|
+
offer.callback,
|
|
2499
|
+
offer.receiverIfMakerIsSeller
|
|
2500
|
+
]);
|
|
2471
2501
|
}
|
|
2472
2502
|
/**
|
|
2473
2503
|
* ABI for the Take event emitted by the Morpho V2 contract.
|
|
@@ -2591,6 +2621,189 @@ const consumedEvent = {
|
|
|
2591
2621
|
],
|
|
2592
2622
|
anonymous: false
|
|
2593
2623
|
};
|
|
2624
|
+
/**
|
|
2625
|
+
* ABI for the Repay event emitted by the MorphoV2 contract.
|
|
2626
|
+
*/
|
|
2627
|
+
const repayEvent = {
|
|
2628
|
+
type: "event",
|
|
2629
|
+
name: "Repay",
|
|
2630
|
+
inputs: [
|
|
2631
|
+
{
|
|
2632
|
+
name: "caller",
|
|
2633
|
+
type: "address",
|
|
2634
|
+
indexed: true,
|
|
2635
|
+
internalType: "address"
|
|
2636
|
+
},
|
|
2637
|
+
{
|
|
2638
|
+
name: "id",
|
|
2639
|
+
type: "bytes32",
|
|
2640
|
+
indexed: true,
|
|
2641
|
+
internalType: "bytes32"
|
|
2642
|
+
},
|
|
2643
|
+
{
|
|
2644
|
+
name: "obligationUnits",
|
|
2645
|
+
type: "uint256",
|
|
2646
|
+
indexed: false,
|
|
2647
|
+
internalType: "uint256"
|
|
2648
|
+
},
|
|
2649
|
+
{
|
|
2650
|
+
name: "onBehalf",
|
|
2651
|
+
type: "address",
|
|
2652
|
+
indexed: true,
|
|
2653
|
+
internalType: "address"
|
|
2654
|
+
}
|
|
2655
|
+
],
|
|
2656
|
+
anonymous: false
|
|
2657
|
+
};
|
|
2658
|
+
/**
|
|
2659
|
+
* ABI for the Liquidate event emitted by the MorphoV2 contract.
|
|
2660
|
+
*/
|
|
2661
|
+
const liquidateEvent = {
|
|
2662
|
+
type: "event",
|
|
2663
|
+
name: "Liquidate",
|
|
2664
|
+
inputs: [
|
|
2665
|
+
{
|
|
2666
|
+
name: "caller",
|
|
2667
|
+
type: "address",
|
|
2668
|
+
indexed: true,
|
|
2669
|
+
internalType: "address"
|
|
2670
|
+
},
|
|
2671
|
+
{
|
|
2672
|
+
name: "id",
|
|
2673
|
+
type: "bytes32",
|
|
2674
|
+
indexed: true,
|
|
2675
|
+
internalType: "bytes32"
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
name: "seizures",
|
|
2679
|
+
type: "tuple[]",
|
|
2680
|
+
indexed: false,
|
|
2681
|
+
internalType: "struct IMorphoV2.Seizure[]",
|
|
2682
|
+
components: [
|
|
2683
|
+
{
|
|
2684
|
+
name: "collateralIndex",
|
|
2685
|
+
type: "uint256",
|
|
2686
|
+
internalType: "uint256"
|
|
2687
|
+
},
|
|
2688
|
+
{
|
|
2689
|
+
name: "repaid",
|
|
2690
|
+
type: "uint256",
|
|
2691
|
+
internalType: "uint256"
|
|
2692
|
+
},
|
|
2693
|
+
{
|
|
2694
|
+
name: "seized",
|
|
2695
|
+
type: "uint256",
|
|
2696
|
+
internalType: "uint256"
|
|
2697
|
+
}
|
|
2698
|
+
]
|
|
2699
|
+
},
|
|
2700
|
+
{
|
|
2701
|
+
name: "borrower",
|
|
2702
|
+
type: "address",
|
|
2703
|
+
indexed: true,
|
|
2704
|
+
internalType: "address"
|
|
2705
|
+
},
|
|
2706
|
+
{
|
|
2707
|
+
name: "totalRepaid",
|
|
2708
|
+
type: "uint256",
|
|
2709
|
+
indexed: false,
|
|
2710
|
+
internalType: "uint256"
|
|
2711
|
+
},
|
|
2712
|
+
{
|
|
2713
|
+
name: "badDebt",
|
|
2714
|
+
type: "uint256",
|
|
2715
|
+
indexed: false,
|
|
2716
|
+
internalType: "uint256"
|
|
2717
|
+
}
|
|
2718
|
+
],
|
|
2719
|
+
anonymous: false
|
|
2720
|
+
};
|
|
2721
|
+
/**
|
|
2722
|
+
* ABI for the SupplyCollateral event emitted by the MorphoV2 contract.
|
|
2723
|
+
*/
|
|
2724
|
+
const supplyCollateralEvent = {
|
|
2725
|
+
type: "event",
|
|
2726
|
+
name: "SupplyCollateral",
|
|
2727
|
+
inputs: [
|
|
2728
|
+
{
|
|
2729
|
+
name: "caller",
|
|
2730
|
+
type: "address",
|
|
2731
|
+
indexed: false,
|
|
2732
|
+
internalType: "address"
|
|
2733
|
+
},
|
|
2734
|
+
{
|
|
2735
|
+
name: "id",
|
|
2736
|
+
type: "bytes32",
|
|
2737
|
+
indexed: true,
|
|
2738
|
+
internalType: "bytes32"
|
|
2739
|
+
},
|
|
2740
|
+
{
|
|
2741
|
+
name: "collateral",
|
|
2742
|
+
type: "address",
|
|
2743
|
+
indexed: true,
|
|
2744
|
+
internalType: "address"
|
|
2745
|
+
},
|
|
2746
|
+
{
|
|
2747
|
+
name: "assets",
|
|
2748
|
+
type: "uint256",
|
|
2749
|
+
indexed: false,
|
|
2750
|
+
internalType: "uint256"
|
|
2751
|
+
},
|
|
2752
|
+
{
|
|
2753
|
+
name: "onBehalf",
|
|
2754
|
+
type: "address",
|
|
2755
|
+
indexed: true,
|
|
2756
|
+
internalType: "address"
|
|
2757
|
+
}
|
|
2758
|
+
],
|
|
2759
|
+
anonymous: false
|
|
2760
|
+
};
|
|
2761
|
+
/**
|
|
2762
|
+
* ABI for the WithdrawCollateral event emitted by the MorphoV2 contract.
|
|
2763
|
+
*/
|
|
2764
|
+
const withdrawCollateralEvent = {
|
|
2765
|
+
type: "event",
|
|
2766
|
+
name: "WithdrawCollateral",
|
|
2767
|
+
inputs: [
|
|
2768
|
+
{
|
|
2769
|
+
name: "caller",
|
|
2770
|
+
type: "address",
|
|
2771
|
+
indexed: false,
|
|
2772
|
+
internalType: "address"
|
|
2773
|
+
},
|
|
2774
|
+
{
|
|
2775
|
+
name: "id",
|
|
2776
|
+
type: "bytes32",
|
|
2777
|
+
indexed: true,
|
|
2778
|
+
internalType: "bytes32"
|
|
2779
|
+
},
|
|
2780
|
+
{
|
|
2781
|
+
name: "collateral",
|
|
2782
|
+
type: "address",
|
|
2783
|
+
indexed: true,
|
|
2784
|
+
internalType: "address"
|
|
2785
|
+
},
|
|
2786
|
+
{
|
|
2787
|
+
name: "assets",
|
|
2788
|
+
type: "uint256",
|
|
2789
|
+
indexed: false,
|
|
2790
|
+
internalType: "uint256"
|
|
2791
|
+
},
|
|
2792
|
+
{
|
|
2793
|
+
name: "onBehalf",
|
|
2794
|
+
type: "address",
|
|
2795
|
+
indexed: true,
|
|
2796
|
+
internalType: "address"
|
|
2797
|
+
},
|
|
2798
|
+
{
|
|
2799
|
+
name: "receiver",
|
|
2800
|
+
type: "address",
|
|
2801
|
+
indexed: false,
|
|
2802
|
+
internalType: "address"
|
|
2803
|
+
}
|
|
2804
|
+
],
|
|
2805
|
+
anonymous: false
|
|
2806
|
+
};
|
|
2594
2807
|
var InvalidOfferError = class InvalidOfferError extends BaseError {
|
|
2595
2808
|
name = "Offer.InvalidOfferError";
|
|
2596
2809
|
constructor(error) {
|
|
@@ -2649,6 +2862,8 @@ let Conversion;
|
|
|
2649
2862
|
let Type = /* @__PURE__ */ function(Type) {
|
|
2650
2863
|
Type["ERC20"] = "erc20";
|
|
2651
2864
|
Type["VAULT_V1"] = "vault_v1";
|
|
2865
|
+
Type["DEBT_OF"] = "debtOf";
|
|
2866
|
+
Type["COLLATERAL_OF"] = "collateralOf";
|
|
2652
2867
|
return Type;
|
|
2653
2868
|
}({});
|
|
2654
2869
|
/**
|
|
@@ -2664,10 +2879,16 @@ function from$10(parameters) {
|
|
|
2664
2879
|
user: parameters.user.toLowerCase(),
|
|
2665
2880
|
type: parameters.type,
|
|
2666
2881
|
balance: parameters.balance,
|
|
2667
|
-
|
|
2882
|
+
asset: parameters.asset.toLowerCase(),
|
|
2668
2883
|
blockNumber: parameters.blockNumber
|
|
2669
2884
|
};
|
|
2670
2885
|
}
|
|
2886
|
+
/**
|
|
2887
|
+
* Maps a {@link Type} enum value to its 1-based integer ID used in the database.
|
|
2888
|
+
* @param type - The position type.
|
|
2889
|
+
* @returns The 1-based integer ID.
|
|
2890
|
+
*/
|
|
2891
|
+
const positionTypeId = (type) => Object.values(Type).indexOf(type) + 1;
|
|
2671
2892
|
|
|
2672
2893
|
//#endregion
|
|
2673
2894
|
//#region src/core/Tick.ts
|
|
@@ -2784,6 +3005,8 @@ function from$8(parameters) {
|
|
|
2784
3005
|
from: parameters.from.toLowerCase(),
|
|
2785
3006
|
to: parameters.to.toLowerCase(),
|
|
2786
3007
|
value: parameters.value,
|
|
3008
|
+
type: parameters.type,
|
|
3009
|
+
asset: parameters.asset.toLowerCase(),
|
|
2787
3010
|
blockNumber: parameters.blockNumber
|
|
2788
3011
|
};
|
|
2789
3012
|
}
|
|
@@ -2912,14 +3135,10 @@ const encodeUnsigned = (tree) => {
|
|
|
2912
3135
|
validateTreeForEncoding(tree);
|
|
2913
3136
|
return bytesToHex(encodeUnsignedBytes(tree));
|
|
2914
3137
|
};
|
|
2915
|
-
const validateTreeForEncoding = (tree
|
|
3138
|
+
const validateTreeForEncoding = (tree) => {
|
|
2916
3139
|
if (VERSION$1 > 255) throw new EncodeError(`version overflow: ${VERSION$1} exceeds 255`);
|
|
2917
3140
|
const computed = from$7(tree.offers);
|
|
2918
3141
|
if (tree.root !== computed.root) throw new EncodeError(`root mismatch: expected ${computed.root}, got ${tree.root}`);
|
|
2919
|
-
if (domain) {
|
|
2920
|
-
const mismatched = tree.offers.find((offer) => BigInt(offer.chainId) !== domain.chainId);
|
|
2921
|
-
if (mismatched) throw new EncodeError(`chainId mismatch: expected ${domain.chainId}, got ${mismatched.chainId}`);
|
|
2922
|
-
}
|
|
2923
3142
|
};
|
|
2924
3143
|
const encodeUnsignedBytes = (tree) => {
|
|
2925
3144
|
const offersPayload = tree.offers.map(serialize);
|
|
@@ -2986,8 +3205,6 @@ const decode = async (encoded, domain) => {
|
|
|
2986
3205
|
}
|
|
2987
3206
|
const tree = from$7(rawOffers.map((o) => OfferSchema().parse(o)));
|
|
2988
3207
|
if (root !== tree.root) throw new DecodeError(`root mismatch: expected ${tree.root}, got ${root}`);
|
|
2989
|
-
const chainIdMismatch = tree.offers.find((offer) => BigInt(offer.chainId) !== normalizedDomain.chainId);
|
|
2990
|
-
if (chainIdMismatch) throw new DecodeError(`chainId mismatch: expected ${normalizedDomain.chainId}, got ${chainIdMismatch.chainId}`);
|
|
2991
3208
|
return {
|
|
2992
3209
|
tree,
|
|
2993
3210
|
signature,
|
|
@@ -3031,10 +3248,6 @@ const BrandTypeId = Symbol.for("mempool/Brand");
|
|
|
3031
3248
|
|
|
3032
3249
|
//#endregion
|
|
3033
3250
|
//#region src/gatekeeper/Rules.ts
|
|
3034
|
-
const chains$1 = ({ chains }) => single("chain_ids", `Validates that offer chain is one of: [${chains.map((c) => c.id).join(", ")}]`, (offer) => {
|
|
3035
|
-
const allowedChainIds = chains.map((c) => c.id);
|
|
3036
|
-
if (!allowedChainIds.some((id) => id === offer.chainId)) return { message: `Chain ID ${offer.chainId} is not in the allowed chains (${allowedChainIds.join(", ")})` };
|
|
3037
|
-
});
|
|
3038
3251
|
const maturity = ({ maturities }) => single("maturity", `Validates that offer maturity is one of: [${maturities.join(", ")}]`, (offer) => {
|
|
3039
3252
|
const allowedMaturities = maturities.map((m) => from$16(m));
|
|
3040
3253
|
if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be one of (${allowedMaturities.join(", ")}). Got: ${offer.maturity}` };
|
|
@@ -3049,9 +3262,9 @@ const callback = ({ callbacks }) => single("callback", `Validates callbacks: buy
|
|
|
3049
3262
|
* @param assetsByChainId - Allowed loan tokens indexed by chain id.
|
|
3050
3263
|
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
3051
3264
|
*/
|
|
3052
|
-
const loanToken = ({ assetsByChainId }) => single("loan_token", "Validates that offer loan token is in the allowed token list for the
|
|
3053
|
-
const allowedLoanTokens = assetsByChainId[
|
|
3054
|
-
if (!allowedLoanTokens || allowedLoanTokens.length === 0) return { message: `No allowed loan tokens for chain ${
|
|
3265
|
+
const loanToken = ({ assetsByChainId, chainId }) => single("loan_token", "Validates that offer loan token is in the allowed token list for the request chain", (offer) => {
|
|
3266
|
+
const allowedLoanTokens = assetsByChainId[chainId]?.map((asset) => asset.toLowerCase());
|
|
3267
|
+
if (!allowedLoanTokens || allowedLoanTokens.length === 0) return { message: `No allowed loan tokens for chain ${chainId}` };
|
|
3055
3268
|
if (!allowedLoanTokens.includes(offer.loanToken.toLowerCase())) return { message: "Loan token is not allowed" };
|
|
3056
3269
|
});
|
|
3057
3270
|
/**
|
|
@@ -3059,9 +3272,9 @@ const loanToken = ({ assetsByChainId }) => single("loan_token", "Validates that
|
|
|
3059
3272
|
* @param collateralAssetsByChainId - Allowed collateral tokens indexed by chain id.
|
|
3060
3273
|
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
3061
3274
|
*/
|
|
3062
|
-
const collateralToken = ({ collateralAssetsByChainId }) => single("collateral_token", "Validates that offer collateral tokens are in the allowed token list for the
|
|
3063
|
-
const allowedCollateralTokens = collateralAssetsByChainId[
|
|
3064
|
-
if (allowedCollateralTokens.length === 0) return { message: `No allowed collateral tokens for chain ${
|
|
3275
|
+
const collateralToken = ({ collateralAssetsByChainId, chainId }) => single("collateral_token", "Validates that offer collateral tokens are in the allowed token list for the request chain", (offer) => {
|
|
3276
|
+
const allowedCollateralTokens = collateralAssetsByChainId[chainId]?.map((asset) => asset.toLowerCase()) ?? [];
|
|
3277
|
+
if (allowedCollateralTokens.length === 0) return { message: `No allowed collateral tokens for chain ${chainId}` };
|
|
3065
3278
|
if (offer.collaterals.length === 0) return { message: "At least one collateral token is required" };
|
|
3066
3279
|
if (offer.collaterals.some((collateral) => !allowedCollateralTokens.includes(collateral.asset.toLowerCase()))) return { message: "Collateral token is not allowed" };
|
|
3067
3280
|
});
|
|
@@ -3070,9 +3283,9 @@ const collateralToken = ({ collateralAssetsByChainId }) => single("collateral_to
|
|
|
3070
3283
|
* @param oraclesByChainId - Allowed oracles indexed by chain id.
|
|
3071
3284
|
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
3072
3285
|
*/
|
|
3073
|
-
const oracle = ({ oraclesByChainId }) => single("oracle", "Validates that offer collateral oracles are in the allowed oracle list for the
|
|
3074
|
-
const allowedOracles = oraclesByChainId[
|
|
3075
|
-
if (!allowedOracles || allowedOracles.length === 0) return { message: `No allowed oracles for chain ${
|
|
3286
|
+
const oracle = ({ oraclesByChainId, chainId }) => single("oracle", "Validates that offer collateral oracles are in the allowed oracle list for the request chain", (offer) => {
|
|
3287
|
+
const allowedOracles = oraclesByChainId[chainId]?.map((oracle) => oracle.toLowerCase());
|
|
3288
|
+
if (!allowedOracles || allowedOracles.length === 0) return { message: `No allowed oracles for chain ${chainId}` };
|
|
3076
3289
|
if (offer.collaterals.some((collateral) => !allowedOracles.includes(collateral.oracle.toLowerCase()))) return { message: "Oracle is not allowed" };
|
|
3077
3290
|
});
|
|
3078
3291
|
/**
|
|
@@ -3104,7 +3317,8 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
|
|
|
3104
3317
|
|
|
3105
3318
|
//#endregion
|
|
3106
3319
|
//#region src/gatekeeper/morphoRules.ts
|
|
3107
|
-
const morphoRules = (
|
|
3320
|
+
const morphoRules = (parameters) => {
|
|
3321
|
+
const { chains, chainId } = parameters;
|
|
3108
3322
|
const assetsByChainId = {};
|
|
3109
3323
|
const collateralAssetsByChainId = {};
|
|
3110
3324
|
const oraclesByChainId = {};
|
|
@@ -3116,15 +3330,23 @@ const morphoRules = (chains) => {
|
|
|
3116
3330
|
return [
|
|
3117
3331
|
sameMaker(),
|
|
3118
3332
|
amountMutualExclusivity(),
|
|
3119
|
-
chains$1({ chains }),
|
|
3120
3333
|
maturity({ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek] }),
|
|
3121
3334
|
callback({
|
|
3122
3335
|
callbacks: [Type$1.BuyWithEmptyCallback, Type$1.SellWithEmptyCallback],
|
|
3123
3336
|
allowedAddresses: []
|
|
3124
3337
|
}),
|
|
3125
|
-
loanToken({
|
|
3126
|
-
|
|
3127
|
-
|
|
3338
|
+
loanToken({
|
|
3339
|
+
assetsByChainId,
|
|
3340
|
+
chainId
|
|
3341
|
+
}),
|
|
3342
|
+
collateralToken({
|
|
3343
|
+
collateralAssetsByChainId,
|
|
3344
|
+
chainId
|
|
3345
|
+
}),
|
|
3346
|
+
oracle({
|
|
3347
|
+
oraclesByChainId,
|
|
3348
|
+
chainId
|
|
3349
|
+
})
|
|
3128
3350
|
];
|
|
3129
3351
|
};
|
|
3130
3352
|
|
|
@@ -3266,12 +3488,13 @@ const RouterStatusResponse = z$1.object({
|
|
|
3266
3488
|
* @constructor
|
|
3267
3489
|
* @param obligation - {@link Obligation}
|
|
3268
3490
|
* @param quote - {@link Quote}
|
|
3491
|
+
* @param chainId - The chain id used to compute `id`.
|
|
3269
3492
|
* @returns The created `ObligationResponse`. {@link ObligationResponse}
|
|
3270
3493
|
*/
|
|
3271
|
-
function from$5(obligation, quote) {
|
|
3494
|
+
function from$5(obligation, quote, chainId) {
|
|
3272
3495
|
return {
|
|
3273
3496
|
id: quote.obligationId,
|
|
3274
|
-
chain_id:
|
|
3497
|
+
chain_id: chainId,
|
|
3275
3498
|
loan_token: obligation.loanToken,
|
|
3276
3499
|
collaterals: obligation.collaterals.map((c) => ({
|
|
3277
3500
|
token: c.asset,
|
|
@@ -3337,12 +3560,7 @@ function from$4(input) {
|
|
|
3337
3560
|
receiver_if_maker_is_seller: input.receiverIfMakerIsSeller
|
|
3338
3561
|
},
|
|
3339
3562
|
offer_hash: input.hash,
|
|
3340
|
-
obligation_id:
|
|
3341
|
-
chainId,
|
|
3342
|
-
loanToken: input.loanToken,
|
|
3343
|
-
collaterals: [...input.collaterals],
|
|
3344
|
-
maturity: input.maturity
|
|
3345
|
-
}),
|
|
3563
|
+
obligation_id: input.obligationId,
|
|
3346
3564
|
chain_id: chainId,
|
|
3347
3565
|
consumed: input.consumed.toString(),
|
|
3348
3566
|
takeable: input.takeable.toString(),
|
|
@@ -3969,10 +4187,6 @@ __decorate([ApiProperty({
|
|
|
3969
4187
|
type: "boolean",
|
|
3970
4188
|
example: validateOfferExample.buy
|
|
3971
4189
|
})], ValidateOfferRequest.prototype, "buy", void 0);
|
|
3972
|
-
__decorate([ApiProperty({
|
|
3973
|
-
type: "number",
|
|
3974
|
-
example: validateOfferExample.chain_id
|
|
3975
|
-
})], ValidateOfferRequest.prototype, "chain_id", void 0);
|
|
3976
4190
|
__decorate([ApiProperty({
|
|
3977
4191
|
type: "string",
|
|
3978
4192
|
example: validateOfferExample.loan_token
|
|
@@ -3990,6 +4204,11 @@ __decorate([ApiProperty({
|
|
|
3990
4204
|
example: validateOfferExample.receiver_if_maker_is_seller
|
|
3991
4205
|
})], ValidateOfferRequest.prototype, "receiver_if_maker_is_seller", void 0);
|
|
3992
4206
|
var ValidateOffersRequest = class {};
|
|
4207
|
+
__decorate([ApiProperty({
|
|
4208
|
+
type: "number",
|
|
4209
|
+
description: "Chain id used for chain-scoped validation rules.",
|
|
4210
|
+
example: validateOfferExample.chain_id
|
|
4211
|
+
})], ValidateOffersRequest.prototype, "chain_id", void 0);
|
|
3993
4212
|
__decorate([ApiProperty({
|
|
3994
4213
|
type: () => [ValidateOfferRequest],
|
|
3995
4214
|
description: "Array of offers in snake_case format. Required, non-empty.",
|
|
@@ -4755,6 +4974,12 @@ function isValidBase64urlJson(val) {
|
|
|
4755
4974
|
function isValidOfferHashCursor(val) {
|
|
4756
4975
|
return /^0x[a-f0-9]{64}$/i.test(val);
|
|
4757
4976
|
}
|
|
4977
|
+
function isValidOfferCursor(val) {
|
|
4978
|
+
const [hash, obligationId, ...rest] = val.split(":");
|
|
4979
|
+
if (rest.length !== 0) return false;
|
|
4980
|
+
if (!hash || !obligationId) return false;
|
|
4981
|
+
return isValidOfferHashCursor(hash) && isValidOfferHashCursor(obligationId);
|
|
4982
|
+
}
|
|
4758
4983
|
const csvArray = (schema) => z$2.preprocess((value) => {
|
|
4759
4984
|
if (value === void 0) return void 0;
|
|
4760
4985
|
if (Array.isArray(value)) {
|
|
@@ -4818,7 +5043,7 @@ const GetConfigContractsQueryParams = z$2.object({
|
|
|
4818
5043
|
});
|
|
4819
5044
|
const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend({
|
|
4820
5045
|
cursor: z$2.string().optional().meta({
|
|
4821
|
-
description: "Pagination cursor. Use offer hash
|
|
5046
|
+
description: "Pagination cursor. Use offer hash:obligation_id for maker queries, base64url for obligation queries.",
|
|
4822
5047
|
example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
|
|
4823
5048
|
}),
|
|
4824
5049
|
side: z$2.enum(["buy", "sell"]).optional().meta({
|
|
@@ -4845,10 +5070,10 @@ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend
|
|
|
4845
5070
|
return;
|
|
4846
5071
|
}
|
|
4847
5072
|
if (hasMaker) {
|
|
4848
|
-
if (val.cursor !== void 0 && !
|
|
5073
|
+
if (val.cursor !== void 0 && !isValidOfferCursor(val.cursor)) ctx.addIssue({
|
|
4849
5074
|
code: "custom",
|
|
4850
5075
|
path: ["cursor"],
|
|
4851
|
-
message: "Cursor must be
|
|
5076
|
+
message: "Cursor must be in the offer hash:obligation_id format when filtering by maker"
|
|
4852
5077
|
});
|
|
4853
5078
|
return;
|
|
4854
5079
|
}
|
|
@@ -4958,7 +5183,13 @@ const GetBookParams = z$2.object({
|
|
|
4958
5183
|
example: "buy"
|
|
4959
5184
|
})
|
|
4960
5185
|
});
|
|
4961
|
-
const ValidateOffersBody = z$2.object({
|
|
5186
|
+
const ValidateOffersBody = z$2.object({
|
|
5187
|
+
chain_id: z$2.number().int().positive("chain_id must be a positive integer").meta({
|
|
5188
|
+
description: "Chain id used for chain-scoped validation rules.",
|
|
5189
|
+
example: 1
|
|
5190
|
+
}),
|
|
5191
|
+
offers: z$2.array(z$2.unknown()).min(1, { message: "'offers' must contain at least 1 offer" })
|
|
5192
|
+
}).strict();
|
|
4962
5193
|
const GetUserPositionsParams = z$2.object({
|
|
4963
5194
|
...PaginationQueryParams.shape,
|
|
4964
5195
|
user_address: z$2.string().regex(/^0x[a-fA-F0-9]{40}$/, { error: "User address must be a valid 20-byte address" }).transform((val) => val.toLowerCase()).meta({
|
|
@@ -5005,7 +5236,7 @@ async function getConfigRules(query, chains) {
|
|
|
5005
5236
|
const checksum = buildConfigRulesChecksum(filteredRules);
|
|
5006
5237
|
let cursorRule = null;
|
|
5007
5238
|
if (cursor) try {
|
|
5008
|
-
cursorRule = parseCursor$
|
|
5239
|
+
cursorRule = parseCursor$3(cursor);
|
|
5009
5240
|
} catch (err) {
|
|
5010
5241
|
return failure(err);
|
|
5011
5242
|
}
|
|
@@ -5013,7 +5244,7 @@ async function getConfigRules(query, chains) {
|
|
|
5013
5244
|
if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError$1("Cursor chain_id must match requested chains"));
|
|
5014
5245
|
const startIndex = cursorRule ? findStartIndex$1(filteredRules, cursorRule) : 0;
|
|
5015
5246
|
const page = filteredRules.slice(startIndex, startIndex + limit);
|
|
5016
|
-
const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor$
|
|
5247
|
+
const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor$3(page.at(-1)) : null;
|
|
5017
5248
|
const response = success({
|
|
5018
5249
|
data: page,
|
|
5019
5250
|
cursor: nextCursor
|
|
@@ -5021,14 +5252,14 @@ async function getConfigRules(query, chains) {
|
|
|
5021
5252
|
response.body.meta.checksum = checksum;
|
|
5022
5253
|
return response;
|
|
5023
5254
|
}
|
|
5024
|
-
function formatCursor$
|
|
5255
|
+
function formatCursor$3(rule) {
|
|
5025
5256
|
if (rule.type === "maturity") return `maturity:${rule.chain_id}:${rule.timestamp}:${rule.name}`;
|
|
5026
5257
|
if (rule.type === "callback") return `callback:${rule.chain_id}:${rule.callback_type}:${rule.address.toLowerCase()}`;
|
|
5027
5258
|
if (rule.type === "oracle") return `oracle:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
5028
5259
|
if (rule.type === "collateral_token") return `collateral_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
5029
5260
|
return `loan_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
5030
5261
|
}
|
|
5031
|
-
function parseCursor$
|
|
5262
|
+
function parseCursor$3(cursor) {
|
|
5032
5263
|
const [type, chain, ...rest] = cursor.split(":");
|
|
5033
5264
|
if (!type || !chain || rest.length === 0) throw new BadRequestError$1("Cursor must be in the format type:chain_id:<value>");
|
|
5034
5265
|
if (!isConfigRuleType(type)) throw new BadRequestError$1("Cursor has an invalid rule type");
|
|
@@ -5178,16 +5409,17 @@ async function validateOffers(body, gatekeeper) {
|
|
|
5178
5409
|
const logger = getLogger();
|
|
5179
5410
|
const result = safeParse("validate_offers", body, (issue) => issue.message);
|
|
5180
5411
|
if (!result.success) return failure(new BadRequestError$1(result.error.issues[0]?.message ?? "Invalid request body"));
|
|
5181
|
-
const { offers: rawOffers } = result.data;
|
|
5412
|
+
const { offers: rawOffers, chain_id: rawChainId } = result.data;
|
|
5413
|
+
const chainId = rawChainId;
|
|
5182
5414
|
const parsedOffers = [];
|
|
5183
5415
|
const offerIndexByHash = /* @__PURE__ */ new Map();
|
|
5184
5416
|
for (let i = 0; i < rawOffers.length; i++) {
|
|
5185
5417
|
const rawOffer = rawOffers[i];
|
|
5186
5418
|
try {
|
|
5187
5419
|
const offer = fromSnakeCase(rawOffer);
|
|
5188
|
-
const hash$
|
|
5189
|
-
if (!offerIndexByHash.has(hash$
|
|
5190
|
-
offerIndexByHash.set(hash$
|
|
5420
|
+
const hash$1 = hash(offer);
|
|
5421
|
+
if (!offerIndexByHash.has(hash$1)) {
|
|
5422
|
+
offerIndexByHash.set(hash$1, i);
|
|
5191
5423
|
parsedOffers.push(offer);
|
|
5192
5424
|
}
|
|
5193
5425
|
} catch (err) {
|
|
@@ -5197,7 +5429,10 @@ async function validateOffers(body, gatekeeper) {
|
|
|
5197
5429
|
}
|
|
5198
5430
|
}
|
|
5199
5431
|
try {
|
|
5200
|
-
const { issues } = await gatekeeper.isAllowed(
|
|
5432
|
+
const { issues } = await gatekeeper.isAllowed({
|
|
5433
|
+
offers: parsedOffers,
|
|
5434
|
+
chainId
|
|
5435
|
+
});
|
|
5201
5436
|
if (issues.length > 0) {
|
|
5202
5437
|
const mappedIssues = issues.map((issue) => {
|
|
5203
5438
|
const index = offerIndexByHash.get(hash(issue.item));
|
|
@@ -5358,7 +5593,7 @@ function now() {
|
|
|
5358
5593
|
|
|
5359
5594
|
//#endregion
|
|
5360
5595
|
//#region src/database/drizzle/VERSION.ts
|
|
5361
|
-
const VERSION = "router_v1.
|
|
5596
|
+
const VERSION = "router_v1.11";
|
|
5362
5597
|
|
|
5363
5598
|
//#endregion
|
|
5364
5599
|
//#region src/database/drizzle/schema.ts
|
|
@@ -5373,8 +5608,10 @@ var schema_exports = /* @__PURE__ */ __exportAll({
|
|
|
5373
5608
|
consumedEvents: () => consumedEvents,
|
|
5374
5609
|
groups: () => groups,
|
|
5375
5610
|
lots: () => lots,
|
|
5611
|
+
lotsPositions: () => lotsPositions,
|
|
5376
5612
|
merklePaths: () => merklePaths,
|
|
5377
5613
|
obligationCollateralsV2: () => obligationCollateralsV2,
|
|
5614
|
+
obligationIdKeys: () => obligationIdKeys,
|
|
5378
5615
|
obligations: () => obligations,
|
|
5379
5616
|
offers: () => offers,
|
|
5380
5617
|
offersCallbacks: () => offersCallbacks,
|
|
@@ -5390,6 +5627,7 @@ var schema_exports = /* @__PURE__ */ __exportAll({
|
|
|
5390
5627
|
const s = pgSchema(VERSION);
|
|
5391
5628
|
var EnumTableName = /* @__PURE__ */ function(EnumTableName) {
|
|
5392
5629
|
EnumTableName["OBLIGATIONS"] = "obligations";
|
|
5630
|
+
EnumTableName["OBLIGATION_ID_KEYS"] = "obligation_id_keys";
|
|
5393
5631
|
EnumTableName["GROUPS"] = "groups";
|
|
5394
5632
|
EnumTableName["CONSUMED_EVENTS"] = "consumed_events";
|
|
5395
5633
|
EnumTableName["OBLIGATION_COLLATERALS_V2"] = "obligation_collaterals_v2";
|
|
@@ -5403,6 +5641,7 @@ var EnumTableName = /* @__PURE__ */ function(EnumTableName) {
|
|
|
5403
5641
|
EnumTableName["COLLECTORS"] = "collectors";
|
|
5404
5642
|
EnumTableName["CHAINS"] = "chains";
|
|
5405
5643
|
EnumTableName["LOTS"] = "lots";
|
|
5644
|
+
EnumTableName["LOTS_POSITIONS"] = "lots_positions";
|
|
5406
5645
|
EnumTableName["OFFSETS"] = "offsets";
|
|
5407
5646
|
EnumTableName["TREES"] = "trees";
|
|
5408
5647
|
EnumTableName["MERKLE_PATHS"] = "merkle_paths";
|
|
@@ -5411,11 +5650,16 @@ var EnumTableName = /* @__PURE__ */ function(EnumTableName) {
|
|
|
5411
5650
|
const TABLE_NAMES = Object.values(EnumTableName);
|
|
5412
5651
|
const VERSIONED_TABLE_NAMES = TABLE_NAMES.map((table) => `"${VERSION}"."${table}"`);
|
|
5413
5652
|
const obligations = s.table(EnumTableName.OBLIGATIONS, {
|
|
5414
|
-
|
|
5415
|
-
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5653
|
+
obligationKey: varchar("obligation_key", { length: 66 }).primaryKey(),
|
|
5416
5654
|
loanToken: varchar("loan_token", { length: 42 }).notNull(),
|
|
5417
5655
|
maturity: integer("maturity").notNull()
|
|
5418
5656
|
});
|
|
5657
|
+
const obligationIdKeys = s.table(EnumTableName.OBLIGATION_ID_KEYS, {
|
|
5658
|
+
obligationId: varchar("obligation_id", { length: 66 }).primaryKey(),
|
|
5659
|
+
obligationKey: varchar("obligation_key", { length: 66 }).notNull().references(() => obligations.obligationKey, { onDelete: "cascade" }),
|
|
5660
|
+
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5661
|
+
morphoV2: varchar("morpho_v2", { length: 42 }).notNull()
|
|
5662
|
+
}, (table) => [index("obligation_id_keys_obligation_key_idx").on(table.obligationKey), index("obligation_id_keys_chain_id_idx").on(table.chainId)]);
|
|
5419
5663
|
const groups = s.table(EnumTableName.GROUPS, {
|
|
5420
5664
|
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5421
5665
|
maker: varchar("maker", { length: 42 }).notNull(),
|
|
@@ -5463,24 +5707,19 @@ const consumedEvents = s.table(EnumTableName.CONSUMED_EVENTS, {
|
|
|
5463
5707
|
index("consumed_events_block_number_idx").on(t.blockNumber)
|
|
5464
5708
|
]);
|
|
5465
5709
|
const obligationCollateralsV2 = s.table(EnumTableName.OBLIGATION_COLLATERALS_V2, {
|
|
5466
|
-
|
|
5710
|
+
obligationKey: varchar("obligation_key", { length: 66 }).notNull().references(() => obligations.obligationKey, { onDelete: "cascade" }),
|
|
5467
5711
|
asset: varchar("asset", { length: 42 }).notNull(),
|
|
5468
|
-
oracleChainId: bigint("oracle_chain_id", { mode: "number" }).$type().notNull(),
|
|
5469
5712
|
oracleAddress: varchar("oracle_address", { length: 42 }).notNull(),
|
|
5470
5713
|
lltv: bigint("lltv", { mode: "bigint" }).notNull(),
|
|
5714
|
+
collateralIndex: integer("collateral_index").notNull(),
|
|
5471
5715
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
5472
5716
|
}, (table) => [
|
|
5473
5717
|
primaryKey({
|
|
5474
|
-
columns: [table.
|
|
5718
|
+
columns: [table.obligationKey, table.asset],
|
|
5475
5719
|
name: "obligation_collaterals_v2_pk"
|
|
5476
5720
|
}),
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
foreignColumns: [oracles.chainId, oracles.address],
|
|
5480
|
-
name: "obligation_collaterals_v2_oracles_fk"
|
|
5481
|
-
}),
|
|
5482
|
-
index("obligation_collaterals_v2_obligation_id_idx").on(table.obligationId),
|
|
5483
|
-
index("obligation_collaterals_v2_oracle_fk_idx").on(table.oracleChainId, table.oracleAddress)
|
|
5721
|
+
index("obligation_collaterals_v2_obligation_key_idx").on(table.obligationKey),
|
|
5722
|
+
index("obligation_collaterals_v2_oracle_address_idx").on(table.oracleAddress)
|
|
5484
5723
|
]);
|
|
5485
5724
|
const oracles = s.table(EnumTableName.ORACLES, {
|
|
5486
5725
|
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
@@ -5496,8 +5735,8 @@ const oracles = s.table(EnumTableName.ORACLES, {
|
|
|
5496
5735
|
name: "oracles_pk"
|
|
5497
5736
|
})]);
|
|
5498
5737
|
const offers = s.table(EnumTableName.OFFERS, {
|
|
5499
|
-
hash: varchar("hash", { length: 66 }).
|
|
5500
|
-
obligationId: varchar("obligation_id", { length: 66 }).notNull().references(() =>
|
|
5738
|
+
hash: varchar("hash", { length: 66 }).notNull(),
|
|
5739
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull().references(() => obligationIdKeys.obligationId, { onDelete: "cascade" }),
|
|
5501
5740
|
assets: numeric("assets", {
|
|
5502
5741
|
precision: 78,
|
|
5503
5742
|
scale: 0
|
|
@@ -5525,6 +5764,10 @@ const offers = s.table(EnumTableName.OFFERS, {
|
|
|
5525
5764
|
blockNumber: bigint("block_number", { mode: "number" }).notNull(),
|
|
5526
5765
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
5527
5766
|
}, (table) => [
|
|
5767
|
+
primaryKey({
|
|
5768
|
+
columns: [table.hash, table.obligationId],
|
|
5769
|
+
name: "offers_pk"
|
|
5770
|
+
}),
|
|
5528
5771
|
foreignKey({
|
|
5529
5772
|
columns: [
|
|
5530
5773
|
table.groupChainId,
|
|
@@ -5543,10 +5786,19 @@ const offers = s.table(EnumTableName.OFFERS, {
|
|
|
5543
5786
|
index("offers_obligation_id_side_idx").on(table.obligationId, table.buy)
|
|
5544
5787
|
]);
|
|
5545
5788
|
const offersCallbacks = s.table(EnumTableName.OFFERS_CALLBACKS, {
|
|
5546
|
-
offerHash: varchar("offer_hash", { length: 66 }).notNull()
|
|
5789
|
+
offerHash: varchar("offer_hash", { length: 66 }).notNull(),
|
|
5790
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5547
5791
|
callbackId: varchar("callback_id", { length: 66 })
|
|
5548
|
-
}, (table) => [
|
|
5549
|
-
columns: [table.offerHash, table.
|
|
5792
|
+
}, (table) => [foreignKey({
|
|
5793
|
+
columns: [table.offerHash, table.obligationId],
|
|
5794
|
+
foreignColumns: [offers.hash, offers.obligationId],
|
|
5795
|
+
name: "offers_callbacks_offer_fk"
|
|
5796
|
+
}).onDelete("cascade"), primaryKey({
|
|
5797
|
+
columns: [
|
|
5798
|
+
table.offerHash,
|
|
5799
|
+
table.obligationId,
|
|
5800
|
+
table.callbackId
|
|
5801
|
+
],
|
|
5550
5802
|
name: "offers_callbacks_pk"
|
|
5551
5803
|
})]);
|
|
5552
5804
|
const callbacks = s.table(EnumTableName.CALLBACKS, {
|
|
@@ -5554,23 +5806,25 @@ const callbacks = s.table(EnumTableName.CALLBACKS, {
|
|
|
5554
5806
|
positionChainId: bigint("position_chain_id", { mode: "number" }).$type().notNull(),
|
|
5555
5807
|
positionContract: varchar("position_contract", { length: 42 }).notNull(),
|
|
5556
5808
|
positionUser: varchar("position_user", { length: 42 }).notNull(),
|
|
5809
|
+
positionTypeId: integer("position_type_id").notNull().references(() => positionTypes.id, { onDelete: "no action" }),
|
|
5557
5810
|
amount: numeric("amount", {
|
|
5558
5811
|
precision: 78,
|
|
5559
5812
|
scale: 0
|
|
5560
5813
|
})
|
|
5561
|
-
}
|
|
5814
|
+
});
|
|
5815
|
+
const lotsPositions = s.table(EnumTableName.LOTS_POSITIONS, {
|
|
5816
|
+
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5817
|
+
contract: varchar("contract", { length: 42 }).notNull(),
|
|
5818
|
+
user: varchar("user", { length: 42 }).notNull(),
|
|
5819
|
+
positionTypeId: integer("position_type_id").notNull().references(() => positionTypes.id, { onDelete: "no action" })
|
|
5820
|
+
}, (table) => [primaryKey({
|
|
5562
5821
|
columns: [
|
|
5563
|
-
table.
|
|
5564
|
-
table.
|
|
5565
|
-
table.
|
|
5566
|
-
],
|
|
5567
|
-
foreignColumns: [
|
|
5568
|
-
positions.chainId,
|
|
5569
|
-
positions.contract,
|
|
5570
|
-
positions.user
|
|
5822
|
+
table.chainId,
|
|
5823
|
+
table.contract,
|
|
5824
|
+
table.user
|
|
5571
5825
|
],
|
|
5572
|
-
name: "
|
|
5573
|
-
})
|
|
5826
|
+
name: "lots_positions_pk"
|
|
5827
|
+
})]);
|
|
5574
5828
|
const lots = s.table(EnumTableName.LOTS, {
|
|
5575
5829
|
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5576
5830
|
user: varchar("user", { length: 42 }).notNull(),
|
|
@@ -5603,11 +5857,11 @@ const lots = s.table(EnumTableName.LOTS, {
|
|
|
5603
5857
|
table.user
|
|
5604
5858
|
],
|
|
5605
5859
|
foreignColumns: [
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5860
|
+
lotsPositions.chainId,
|
|
5861
|
+
lotsPositions.contract,
|
|
5862
|
+
lotsPositions.user
|
|
5609
5863
|
],
|
|
5610
|
-
name: "
|
|
5864
|
+
name: "lots_lots_positions_fk"
|
|
5611
5865
|
}).onDelete("cascade"),
|
|
5612
5866
|
foreignKey({
|
|
5613
5867
|
columns: [
|
|
@@ -5649,11 +5903,11 @@ const offsets = s.table(EnumTableName.OFFSETS, {
|
|
|
5649
5903
|
table.user
|
|
5650
5904
|
],
|
|
5651
5905
|
foreignColumns: [
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5906
|
+
lotsPositions.chainId,
|
|
5907
|
+
lotsPositions.contract,
|
|
5908
|
+
lotsPositions.user
|
|
5655
5909
|
],
|
|
5656
|
-
name: "
|
|
5910
|
+
name: "offsets_lots_positions_fk"
|
|
5657
5911
|
}).onDelete("cascade")]);
|
|
5658
5912
|
const PositionTypes = s.enum("position_type", Object.values(Type));
|
|
5659
5913
|
const positionTypes = s.table("position_types", {
|
|
@@ -5662,34 +5916,38 @@ const positionTypes = s.table("position_types", {
|
|
|
5662
5916
|
});
|
|
5663
5917
|
const positions = s.table(EnumTableName.POSITIONS, {
|
|
5664
5918
|
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5665
|
-
contract: varchar("contract", { length:
|
|
5919
|
+
contract: varchar("contract", { length: 66 }).notNull(),
|
|
5666
5920
|
user: varchar("user", { length: 42 }).notNull(),
|
|
5667
5921
|
positionTypeId: integer("position_type_id").notNull().references(() => positionTypes.id, { onDelete: "no action" }),
|
|
5668
5922
|
balance: numeric("balance", {
|
|
5669
5923
|
precision: 78,
|
|
5670
5924
|
scale: 0
|
|
5671
5925
|
}),
|
|
5672
|
-
asset: varchar("asset", { length: 42 }),
|
|
5926
|
+
asset: varchar("asset", { length: 42 }).notNull(),
|
|
5673
5927
|
blockNumber: bigint("block_number", { mode: "number" }).notNull(),
|
|
5674
5928
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
5675
5929
|
}, (table) => [primaryKey({
|
|
5676
5930
|
columns: [
|
|
5677
5931
|
table.chainId,
|
|
5678
5932
|
table.contract,
|
|
5679
|
-
table.user
|
|
5933
|
+
table.user,
|
|
5934
|
+
table.positionTypeId,
|
|
5935
|
+
table.asset
|
|
5680
5936
|
],
|
|
5681
5937
|
name: "positions_pk"
|
|
5682
5938
|
})]);
|
|
5683
5939
|
const transfers = s.table(EnumTableName.TRANSFERS, {
|
|
5684
5940
|
eventId: varchar("event_id", { length: 128 }).primaryKey(),
|
|
5685
5941
|
chainId: bigint("chain_id", { mode: "number" }).$type().notNull(),
|
|
5686
|
-
contract: varchar("contract", { length:
|
|
5942
|
+
contract: varchar("contract", { length: 66 }).notNull(),
|
|
5687
5943
|
from: varchar("from", { length: 42 }).notNull(),
|
|
5688
5944
|
to: varchar("to", { length: 42 }).notNull(),
|
|
5689
5945
|
value: numeric("value", {
|
|
5690
5946
|
precision: 78,
|
|
5691
5947
|
scale: 0
|
|
5692
5948
|
}).notNull(),
|
|
5949
|
+
positionTypeId: integer("position_type_id").notNull().references(() => positionTypes.id, { onDelete: "no action" }),
|
|
5950
|
+
asset: varchar("asset", { length: 42 }).notNull(),
|
|
5693
5951
|
blockNumber: bigint("block_number", { mode: "number" }).notNull(),
|
|
5694
5952
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
5695
5953
|
}, (table) => [
|
|
@@ -5697,12 +5955,16 @@ const transfers = s.table(EnumTableName.TRANSFERS, {
|
|
|
5697
5955
|
columns: [
|
|
5698
5956
|
table.chainId,
|
|
5699
5957
|
table.contract,
|
|
5700
|
-
table.from
|
|
5958
|
+
table.from,
|
|
5959
|
+
table.positionTypeId,
|
|
5960
|
+
table.asset
|
|
5701
5961
|
],
|
|
5702
5962
|
foreignColumns: [
|
|
5703
5963
|
positions.chainId,
|
|
5704
5964
|
positions.contract,
|
|
5705
|
-
positions.user
|
|
5965
|
+
positions.user,
|
|
5966
|
+
positions.positionTypeId,
|
|
5967
|
+
positions.asset
|
|
5706
5968
|
],
|
|
5707
5969
|
name: "transfers_positions_from_fk"
|
|
5708
5970
|
}).onDelete("cascade"),
|
|
@@ -5710,16 +5972,21 @@ const transfers = s.table(EnumTableName.TRANSFERS, {
|
|
|
5710
5972
|
columns: [
|
|
5711
5973
|
table.chainId,
|
|
5712
5974
|
table.contract,
|
|
5713
|
-
table.to
|
|
5975
|
+
table.to,
|
|
5976
|
+
table.positionTypeId,
|
|
5977
|
+
table.asset
|
|
5714
5978
|
],
|
|
5715
5979
|
foreignColumns: [
|
|
5716
5980
|
positions.chainId,
|
|
5717
5981
|
positions.contract,
|
|
5718
|
-
positions.user
|
|
5982
|
+
positions.user,
|
|
5983
|
+
positions.positionTypeId,
|
|
5984
|
+
positions.asset
|
|
5719
5985
|
],
|
|
5720
5986
|
name: "transfers_positions_to_fk"
|
|
5721
5987
|
}).onDelete("cascade"),
|
|
5722
|
-
index("transfers_chain_contract_user_idx").on(table.chainId, table.contract, table.from, table.to, table.blockNumber)
|
|
5988
|
+
index("transfers_chain_contract_user_idx").on(table.chainId, table.contract, table.from, table.to, table.blockNumber),
|
|
5989
|
+
index("transfers_chain_type_block_idx").on(table.chainId, table.positionTypeId, table.blockNumber)
|
|
5723
5990
|
]);
|
|
5724
5991
|
const StatusCode = s.enum("status_code", Object.values(Status));
|
|
5725
5992
|
const status = s.table("status", {
|
|
@@ -5727,10 +5994,18 @@ const status = s.table("status", {
|
|
|
5727
5994
|
code: StatusCode("code").unique()
|
|
5728
5995
|
});
|
|
5729
5996
|
const validations = s.table("validations", {
|
|
5730
|
-
offerHash: varchar("offer_hash", { length: 66 }).
|
|
5997
|
+
offerHash: varchar("offer_hash", { length: 66 }).notNull(),
|
|
5998
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5731
5999
|
statusId: integer("status_id").notNull().references(() => status.id, { onDelete: "no action" }),
|
|
5732
6000
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
5733
|
-
})
|
|
6001
|
+
}, (table) => [primaryKey({
|
|
6002
|
+
columns: [table.offerHash, table.obligationId],
|
|
6003
|
+
name: "validations_pk"
|
|
6004
|
+
}), foreignKey({
|
|
6005
|
+
columns: [table.offerHash, table.obligationId],
|
|
6006
|
+
foreignColumns: [offers.hash, offers.obligationId],
|
|
6007
|
+
name: "validations_offer_fk"
|
|
6008
|
+
}).onDelete("cascade")]);
|
|
5734
6009
|
const collectors = s.table(EnumTableName.COLLECTORS, {
|
|
5735
6010
|
chainId: bigint("chain_id", { mode: "number" }).$type().notNull().references(() => chains.chainId, { onDelete: "no action" }),
|
|
5736
6011
|
name: text("name").$type().notNull(),
|
|
@@ -5756,18 +6031,352 @@ const trees = s.table(EnumTableName.TREES, {
|
|
|
5756
6031
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
5757
6032
|
});
|
|
5758
6033
|
const merklePaths = s.table(EnumTableName.MERKLE_PATHS, {
|
|
5759
|
-
offerHash: varchar("offer_hash", { length: 66 }).
|
|
6034
|
+
offerHash: varchar("offer_hash", { length: 66 }).notNull(),
|
|
6035
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5760
6036
|
treeRoot: varchar("tree_root", { length: 66 }).notNull().references(() => trees.root, { onDelete: "cascade" }),
|
|
5761
6037
|
proofNodes: text("proof_nodes").notNull(),
|
|
5762
6038
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
5763
|
-
}, (table) => [
|
|
6039
|
+
}, (table) => [
|
|
6040
|
+
primaryKey({
|
|
6041
|
+
columns: [table.offerHash, table.obligationId],
|
|
6042
|
+
name: "merkle_paths_pk"
|
|
6043
|
+
}),
|
|
6044
|
+
foreignKey({
|
|
6045
|
+
columns: [table.offerHash, table.obligationId],
|
|
6046
|
+
foreignColumns: [offers.hash, offers.obligationId],
|
|
6047
|
+
name: "merkle_paths_offer_fk"
|
|
6048
|
+
}).onDelete("cascade"),
|
|
6049
|
+
index("merkle_paths_tree_root_idx").on(table.treeRoot)
|
|
6050
|
+
]);
|
|
6051
|
+
|
|
6052
|
+
//#endregion
|
|
6053
|
+
//#region src/indexer/collectors/CollectFunctions/processors/processCollateralSeizures.ts
|
|
6054
|
+
/**
|
|
6055
|
+
* Parse raw MorphoV2 liquidate logs and compute collateral seizures.
|
|
6056
|
+
*
|
|
6057
|
+
* Seizures only expose `(obligationId, collateralIndex)` and must be resolved
|
|
6058
|
+
* to collateral token addresses before creating transfer rows.
|
|
6059
|
+
*
|
|
6060
|
+
* @param parameters - The parsed event logs and chain ID.
|
|
6061
|
+
* @param parameters.logs - Parsed event logs from MorphoV2.
|
|
6062
|
+
* @param parameters.chainId - Chain ID for event attribution.
|
|
6063
|
+
* @returns Seizure events pending collateral address resolution.
|
|
6064
|
+
*/
|
|
6065
|
+
function processCollateralSeizures(parameters) {
|
|
6066
|
+
const { logs, chainId } = parameters;
|
|
6067
|
+
const logger = getLogger();
|
|
6068
|
+
const seizureEvents = [];
|
|
6069
|
+
for (const rawLog of logs) {
|
|
6070
|
+
if (rawLog.blockNumber === null || rawLog.logIndex === null || rawLog.transactionHash === null) {
|
|
6071
|
+
logger.debug({
|
|
6072
|
+
chainId,
|
|
6073
|
+
msg: "Skipping collateral log because it is missing required fields"
|
|
6074
|
+
});
|
|
6075
|
+
continue;
|
|
6076
|
+
}
|
|
6077
|
+
if (rawLog.eventName !== liquidateEvent.name) continue;
|
|
6078
|
+
const args = rawLog.args;
|
|
6079
|
+
if (args?.id === void 0 || args?.borrower === void 0 || args?.seizures === void 0) {
|
|
6080
|
+
logger.debug({
|
|
6081
|
+
chainId,
|
|
6082
|
+
msg: "Skipping Liquidate log for collateral because it is missing required args"
|
|
6083
|
+
});
|
|
6084
|
+
continue;
|
|
6085
|
+
}
|
|
6086
|
+
const baseId = `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${chainId}-${rawLog.transactionHash}`;
|
|
6087
|
+
for (let seizureIndex = 0; seizureIndex < args.seizures.length; seizureIndex++) {
|
|
6088
|
+
const seizure = args.seizures[seizureIndex];
|
|
6089
|
+
if (seizure.seized === 0n) continue;
|
|
6090
|
+
seizureEvents.push({
|
|
6091
|
+
id: `${baseId}-collateral-liquidate-${seizureIndex}`,
|
|
6092
|
+
chainId,
|
|
6093
|
+
obligationId: args.id,
|
|
6094
|
+
collateralIndex: Number(seizure.collateralIndex),
|
|
6095
|
+
user: args.borrower,
|
|
6096
|
+
amount: seizure.seized,
|
|
6097
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6098
|
+
});
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
return seizureEvents;
|
|
6102
|
+
}
|
|
6103
|
+
|
|
6104
|
+
//#endregion
|
|
6105
|
+
//#region src/indexer/collectors/CollectFunctions/processors/processCollateralTransfers.ts
|
|
6106
|
+
/**
|
|
6107
|
+
* Parse raw MorphoV2 logs and compute collateral transfers.
|
|
6108
|
+
*
|
|
6109
|
+
* A collateral position uses `contract = obligationId` and `asset = collateral token address`.
|
|
6110
|
+
* The 5-col PK `(chainId, contract, user, positionTypeId, asset)` distinguishes per-token positions.
|
|
6111
|
+
*
|
|
6112
|
+
* **SupplyCollateral**: Transfer from zeroAddress to onBehalf (supply)
|
|
6113
|
+
* **WithdrawCollateral**: Transfer from onBehalf to zeroAddress (withdrawal)
|
|
6114
|
+
*
|
|
6115
|
+
* @param parameters - The parsed event logs and chain ID.
|
|
6116
|
+
* @param parameters.logs - Parsed event logs from MorphoV2.
|
|
6117
|
+
* @param parameters.chainId - Chain ID for event attribution.
|
|
6118
|
+
* @returns Collateral transfers from SupplyCollateral/WithdrawCollateral.
|
|
6119
|
+
*/
|
|
6120
|
+
function processCollateralTransfers(parameters) {
|
|
6121
|
+
const { logs, chainId } = parameters;
|
|
6122
|
+
const logger = getLogger();
|
|
6123
|
+
const transfers = [];
|
|
6124
|
+
for (const rawLog of logs) {
|
|
6125
|
+
if (rawLog.blockNumber === null || rawLog.logIndex === null || rawLog.transactionHash === null) {
|
|
6126
|
+
logger.debug({
|
|
6127
|
+
chainId,
|
|
6128
|
+
msg: "Skipping collateral log because it is missing required fields"
|
|
6129
|
+
});
|
|
6130
|
+
continue;
|
|
6131
|
+
}
|
|
6132
|
+
const eventName = rawLog.eventName;
|
|
6133
|
+
const baseId = `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${chainId}-${rawLog.transactionHash}`;
|
|
6134
|
+
if (eventName === supplyCollateralEvent.name) {
|
|
6135
|
+
const args = rawLog.args;
|
|
6136
|
+
if (args?.id === void 0 || args?.collateral === void 0 || args?.assets === void 0 || args?.onBehalf === void 0) {
|
|
6137
|
+
logger.debug({
|
|
6138
|
+
chainId,
|
|
6139
|
+
msg: "Skipping SupplyCollateral log because it is missing required args"
|
|
6140
|
+
});
|
|
6141
|
+
continue;
|
|
6142
|
+
}
|
|
6143
|
+
if (args.assets === 0n) continue;
|
|
6144
|
+
transfers.push(from$8({
|
|
6145
|
+
id: `${baseId}-collateral-supply`,
|
|
6146
|
+
chainId,
|
|
6147
|
+
contract: args.id,
|
|
6148
|
+
from: zeroAddress,
|
|
6149
|
+
to: args.onBehalf,
|
|
6150
|
+
value: args.assets,
|
|
6151
|
+
type: Type.COLLATERAL_OF,
|
|
6152
|
+
asset: args.collateral,
|
|
6153
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6154
|
+
}));
|
|
6155
|
+
continue;
|
|
6156
|
+
}
|
|
6157
|
+
if (eventName === withdrawCollateralEvent.name) {
|
|
6158
|
+
const args = rawLog.args;
|
|
6159
|
+
if (args?.id === void 0 || args?.collateral === void 0 || args?.assets === void 0 || args?.onBehalf === void 0) {
|
|
6160
|
+
logger.debug({
|
|
6161
|
+
chainId,
|
|
6162
|
+
msg: "Skipping WithdrawCollateral log because it is missing required args"
|
|
6163
|
+
});
|
|
6164
|
+
continue;
|
|
6165
|
+
}
|
|
6166
|
+
if (args.assets === 0n) continue;
|
|
6167
|
+
transfers.push(from$8({
|
|
6168
|
+
id: `${baseId}-collateral-withdraw`,
|
|
6169
|
+
chainId,
|
|
6170
|
+
contract: args.id,
|
|
6171
|
+
from: args.onBehalf,
|
|
6172
|
+
to: zeroAddress,
|
|
6173
|
+
value: args.assets,
|
|
6174
|
+
type: Type.COLLATERAL_OF,
|
|
6175
|
+
asset: args.collateral,
|
|
6176
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6177
|
+
}));
|
|
6178
|
+
}
|
|
6179
|
+
}
|
|
6180
|
+
return transfers;
|
|
6181
|
+
}
|
|
5764
6182
|
|
|
5765
6183
|
//#endregion
|
|
5766
|
-
//#region src/indexer/collectors/CollectFunctions/
|
|
6184
|
+
//#region src/indexer/collectors/CollectFunctions/processors/processConsumedLogs.ts
|
|
5767
6185
|
const buildGroupKey = (parameters) => {
|
|
5768
6186
|
return `${parameters.chainId}-${parameters.maker.toLowerCase()}-${parameters.group.toLowerCase()}`;
|
|
5769
6187
|
};
|
|
5770
|
-
|
|
6188
|
+
/** Parse raw MorphoV2 logs and produce normalized consumed events.
|
|
6189
|
+
* @param parameters - The parsed event logs and chain ID.
|
|
6190
|
+
* @param parameters.logs - Parsed event logs from MorphoV2 (Consume and Take events).
|
|
6191
|
+
* @param parameters.chainId - Chain ID for event attribution.
|
|
6192
|
+
* @returns Flat array of consumed events.
|
|
6193
|
+
*/
|
|
6194
|
+
function processConsumedLogs(parameters) {
|
|
6195
|
+
const { logs, chainId } = parameters;
|
|
6196
|
+
const logger = getLogger();
|
|
6197
|
+
const consumedEvents = [];
|
|
6198
|
+
for (const rawLog of logs) {
|
|
6199
|
+
if (rawLog.blockNumber === null || rawLog.logIndex === null || rawLog.transactionHash === null) {
|
|
6200
|
+
logger.debug({
|
|
6201
|
+
chainId,
|
|
6202
|
+
msg: "Skipping log because it is missing required fields"
|
|
6203
|
+
});
|
|
6204
|
+
continue;
|
|
6205
|
+
}
|
|
6206
|
+
const eventName = rawLog.eventName;
|
|
6207
|
+
if (eventName === consumedEvent.name) {
|
|
6208
|
+
const consumeArgs = rawLog.args;
|
|
6209
|
+
if (consumeArgs?.user === void 0 || consumeArgs?.group === void 0 || consumeArgs?.amount === void 0) {
|
|
6210
|
+
logger.debug({
|
|
6211
|
+
chainId,
|
|
6212
|
+
msg: "Skipping Consume log because it is missing required args"
|
|
6213
|
+
});
|
|
6214
|
+
continue;
|
|
6215
|
+
}
|
|
6216
|
+
consumedEvents.push({
|
|
6217
|
+
kind: "consume",
|
|
6218
|
+
id: `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${chainId}-${rawLog.transactionHash}`,
|
|
6219
|
+
chainId,
|
|
6220
|
+
maker: consumeArgs.user,
|
|
6221
|
+
group: consumeArgs.group,
|
|
6222
|
+
amount: consumeArgs.amount,
|
|
6223
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6224
|
+
});
|
|
6225
|
+
continue;
|
|
6226
|
+
}
|
|
6227
|
+
if (eventName === takeEvent.name) {
|
|
6228
|
+
const takeArgs = rawLog.args;
|
|
6229
|
+
if (takeArgs?.maker === void 0 || takeArgs?.group === void 0 || takeArgs?.consumed === void 0) {
|
|
6230
|
+
logger.debug({
|
|
6231
|
+
chainId,
|
|
6232
|
+
msg: "Skipping Take log because it is missing required args for consumed"
|
|
6233
|
+
});
|
|
6234
|
+
continue;
|
|
6235
|
+
}
|
|
6236
|
+
consumedEvents.push({
|
|
6237
|
+
kind: "take",
|
|
6238
|
+
id: `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${chainId}-${rawLog.transactionHash}`,
|
|
6239
|
+
chainId,
|
|
6240
|
+
maker: takeArgs.maker,
|
|
6241
|
+
group: takeArgs.group,
|
|
6242
|
+
consumed: takeArgs.consumed,
|
|
6243
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6244
|
+
});
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
return consumedEvents;
|
|
6248
|
+
}
|
|
6249
|
+
|
|
6250
|
+
//#endregion
|
|
6251
|
+
//#region src/indexer/collectors/CollectFunctions/processors/processDebtTransfers.ts
|
|
6252
|
+
/**
|
|
6253
|
+
* Parse raw MorphoV2 logs and compute debt transfers.
|
|
6254
|
+
*
|
|
6255
|
+
* A debt is modeled as a negative position: borrowing = transfer FROM user TO zeroAddress,
|
|
6256
|
+
* repayment = FROM zeroAddress TO user. The contract field is the obligationId.
|
|
6257
|
+
*
|
|
6258
|
+
* Applies the 4-case Take matrix, Repay, and Liquidate attribution rules:
|
|
6259
|
+
*
|
|
6260
|
+
* **Take event** — Buyer = maker if offerIsBuy, taker otherwise. Seller = the other.
|
|
6261
|
+
* | buyerIsLender | sellerIsBorrower | Buyer transfer | Seller transfer |
|
|
6262
|
+
* |---------------|------------------|------------------------------|------------------------------|
|
|
6263
|
+
* | true | true | none | from: seller → to: 0x0 |
|
|
6264
|
+
* | true | false | none | none |
|
|
6265
|
+
* | false | true | from: 0x0 → to: buyer | from: seller → to: 0x0 |
|
|
6266
|
+
* | false | false | from: 0x0 → to: buyer | none |
|
|
6267
|
+
*
|
|
6268
|
+
* **Repay**: from: 0x0 → to: onBehalf
|
|
6269
|
+
* **Liquidate**: from: 0x0 → to: borrower (value = totalRepaid + badDebt)
|
|
6270
|
+
*
|
|
6271
|
+
* @param parameters - The parsed event logs and chain ID.
|
|
6272
|
+
* @param parameters.logs - Parsed event logs from MorphoV2 (Take, Repay, Liquidate events).
|
|
6273
|
+
* @param parameters.chainId - Chain ID for event attribution.
|
|
6274
|
+
* @returns Transfer events ready for DB insertion.
|
|
6275
|
+
*/
|
|
6276
|
+
function processDebtTransfers(parameters) {
|
|
6277
|
+
const { logs, chainId } = parameters;
|
|
6278
|
+
const logger = getLogger();
|
|
6279
|
+
const transfers = [];
|
|
6280
|
+
for (const rawLog of logs) {
|
|
6281
|
+
if (rawLog.blockNumber === null || rawLog.logIndex === null || rawLog.transactionHash === null) {
|
|
6282
|
+
logger.debug({
|
|
6283
|
+
chainId,
|
|
6284
|
+
msg: "Skipping debt log because it is missing required fields"
|
|
6285
|
+
});
|
|
6286
|
+
continue;
|
|
6287
|
+
}
|
|
6288
|
+
const eventName = rawLog.eventName;
|
|
6289
|
+
const baseId = `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${chainId}-${rawLog.transactionHash}`;
|
|
6290
|
+
if (eventName === takeEvent.name) {
|
|
6291
|
+
const args = rawLog.args;
|
|
6292
|
+
if (args?.id === void 0 || args?.maker === void 0 || args?.taker === void 0 || args?.offerIsBuy === void 0 || args?.obligationUnits === void 0 || args?.buyerIsLender === void 0 || args?.sellerIsBorrower === void 0) {
|
|
6293
|
+
logger.debug({
|
|
6294
|
+
chainId,
|
|
6295
|
+
msg: "Skipping Take log because it is missing required args for debt"
|
|
6296
|
+
});
|
|
6297
|
+
continue;
|
|
6298
|
+
}
|
|
6299
|
+
if (args.obligationUnits === 0n) continue;
|
|
6300
|
+
const buyer = args.offerIsBuy ? args.maker : args.taker;
|
|
6301
|
+
const seller = args.offerIsBuy ? args.taker : args.maker;
|
|
6302
|
+
const blockNumber = Number(rawLog.blockNumber);
|
|
6303
|
+
if (!args.buyerIsLender) transfers.push(from$8({
|
|
6304
|
+
id: `${baseId}-debt-buyer`,
|
|
6305
|
+
chainId,
|
|
6306
|
+
contract: args.id,
|
|
6307
|
+
from: zeroAddress,
|
|
6308
|
+
to: buyer,
|
|
6309
|
+
value: args.obligationUnits,
|
|
6310
|
+
type: Type.DEBT_OF,
|
|
6311
|
+
asset: zeroAddress,
|
|
6312
|
+
blockNumber
|
|
6313
|
+
}));
|
|
6314
|
+
if (args.sellerIsBorrower) transfers.push(from$8({
|
|
6315
|
+
id: `${baseId}-debt-seller`,
|
|
6316
|
+
chainId,
|
|
6317
|
+
contract: args.id,
|
|
6318
|
+
from: seller,
|
|
6319
|
+
to: zeroAddress,
|
|
6320
|
+
value: args.obligationUnits,
|
|
6321
|
+
type: Type.DEBT_OF,
|
|
6322
|
+
asset: zeroAddress,
|
|
6323
|
+
blockNumber
|
|
6324
|
+
}));
|
|
6325
|
+
continue;
|
|
6326
|
+
}
|
|
6327
|
+
if (eventName === repayEvent.name) {
|
|
6328
|
+
const args = rawLog.args;
|
|
6329
|
+
if (args?.id === void 0 || args?.obligationUnits === void 0 || args?.onBehalf === void 0) {
|
|
6330
|
+
logger.debug({
|
|
6331
|
+
chainId,
|
|
6332
|
+
msg: "Skipping Repay log because it is missing required args"
|
|
6333
|
+
});
|
|
6334
|
+
continue;
|
|
6335
|
+
}
|
|
6336
|
+
if (args.obligationUnits === 0n) continue;
|
|
6337
|
+
transfers.push(from$8({
|
|
6338
|
+
id: `${baseId}-debt-repay`,
|
|
6339
|
+
chainId,
|
|
6340
|
+
contract: args.id,
|
|
6341
|
+
from: zeroAddress,
|
|
6342
|
+
to: args.onBehalf,
|
|
6343
|
+
value: args.obligationUnits,
|
|
6344
|
+
type: Type.DEBT_OF,
|
|
6345
|
+
asset: zeroAddress,
|
|
6346
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6347
|
+
}));
|
|
6348
|
+
continue;
|
|
6349
|
+
}
|
|
6350
|
+
if (eventName === liquidateEvent.name) {
|
|
6351
|
+
const args = rawLog.args;
|
|
6352
|
+
if (args?.id === void 0 || args?.borrower === void 0 || args?.totalRepaid === void 0 || args?.badDebt === void 0) {
|
|
6353
|
+
logger.debug({
|
|
6354
|
+
chainId,
|
|
6355
|
+
msg: "Skipping Liquidate log because it is missing required args"
|
|
6356
|
+
});
|
|
6357
|
+
continue;
|
|
6358
|
+
}
|
|
6359
|
+
const totalReduction = args.totalRepaid + args.badDebt;
|
|
6360
|
+
if (totalReduction === 0n) continue;
|
|
6361
|
+
transfers.push(from$8({
|
|
6362
|
+
id: `${baseId}-debt-liquidate`,
|
|
6363
|
+
chainId,
|
|
6364
|
+
contract: args.id,
|
|
6365
|
+
from: zeroAddress,
|
|
6366
|
+
to: args.borrower,
|
|
6367
|
+
value: totalReduction,
|
|
6368
|
+
type: Type.DEBT_OF,
|
|
6369
|
+
asset: zeroAddress,
|
|
6370
|
+
blockNumber: Number(rawLog.blockNumber)
|
|
6371
|
+
}));
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
return transfers;
|
|
6375
|
+
}
|
|
6376
|
+
|
|
6377
|
+
//#endregion
|
|
6378
|
+
//#region src/indexer/collectors/CollectFunctions/collectMorphoV2.ts
|
|
6379
|
+
async function* collectMorphoV2(parameters) {
|
|
5771
6380
|
let { db, collector, client, lastBlockNumber: blockNumber, epoch, options: { maxBatchSize = 1e3, blockWindow } = {} } = parameters;
|
|
5772
6381
|
const logger = getLogger();
|
|
5773
6382
|
let startBlock = blockNumber;
|
|
@@ -5786,179 +6395,63 @@ async function* collectConsumedEvents(parameters) {
|
|
|
5786
6395
|
});
|
|
5787
6396
|
for await (const { logs, blockNumber: lastStreamBlockNumber } of stream) {
|
|
5788
6397
|
const parsedLogs = parseEventLogs({
|
|
5789
|
-
abi: [
|
|
6398
|
+
abi: [
|
|
6399
|
+
consumedEvent,
|
|
6400
|
+
takeEvent,
|
|
6401
|
+
repayEvent,
|
|
6402
|
+
liquidateEvent,
|
|
6403
|
+
supplyCollateralEvent,
|
|
6404
|
+
withdrawCollateralEvent
|
|
6405
|
+
],
|
|
5790
6406
|
logs,
|
|
5791
6407
|
strict: false
|
|
5792
6408
|
});
|
|
5793
|
-
const
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
};
|
|
5810
|
-
for (const rawLog of parsedLogs) {
|
|
5811
|
-
if (rawLog.blockNumber === null || rawLog.logIndex === null || rawLog.transactionHash === null) {
|
|
5812
|
-
logger.debug({
|
|
5813
|
-
collector,
|
|
5814
|
-
chainId: client.chain.id,
|
|
5815
|
-
msg: "Skipping log because it is missing required fields"
|
|
5816
|
-
});
|
|
5817
|
-
continue;
|
|
5818
|
-
}
|
|
5819
|
-
if (rawLog.eventName === consumedEvent.name) {
|
|
5820
|
-
const consumeArgs = rawLog.args;
|
|
5821
|
-
if (consumeArgs.user === void 0 || consumeArgs.group === void 0 || consumeArgs.amount === void 0) {
|
|
5822
|
-
logger.debug({
|
|
5823
|
-
collector,
|
|
5824
|
-
chainId: client.chain.id,
|
|
5825
|
-
msg: "Skipping Consume log because it is missing required args"
|
|
5826
|
-
});
|
|
5827
|
-
continue;
|
|
5828
|
-
}
|
|
5829
|
-
recordLog({
|
|
5830
|
-
kind: "consume",
|
|
5831
|
-
id: `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${client.chain.id}-${rawLog.transactionHash}`,
|
|
5832
|
-
chainId: client.chain.id,
|
|
5833
|
-
maker: consumeArgs.user,
|
|
5834
|
-
group: consumeArgs.group,
|
|
5835
|
-
amount: consumeArgs.amount,
|
|
5836
|
-
blockNumber: Number(rawLog.blockNumber)
|
|
5837
|
-
});
|
|
5838
|
-
continue;
|
|
5839
|
-
}
|
|
5840
|
-
if (rawLog.eventName === takeEvent.name) {
|
|
5841
|
-
const takeArgs = rawLog.args;
|
|
5842
|
-
if (takeArgs.maker === void 0 || takeArgs.group === void 0 || takeArgs.consumed === void 0) {
|
|
5843
|
-
logger.debug({
|
|
5844
|
-
collector,
|
|
5845
|
-
chainId: client.chain.id,
|
|
5846
|
-
msg: "Skipping Take log because it is missing required args"
|
|
5847
|
-
});
|
|
5848
|
-
continue;
|
|
5849
|
-
}
|
|
5850
|
-
recordLog({
|
|
5851
|
-
kind: "take",
|
|
5852
|
-
id: `${rawLog.blockNumber.toString()}-${rawLog.logIndex.toString()}-${client.chain.id}-${rawLog.transactionHash}`,
|
|
5853
|
-
chainId: client.chain.id,
|
|
5854
|
-
maker: takeArgs.maker,
|
|
5855
|
-
group: takeArgs.group,
|
|
5856
|
-
consumed: takeArgs.consumed,
|
|
5857
|
-
blockNumber: Number(rawLog.blockNumber)
|
|
5858
|
-
});
|
|
5859
|
-
}
|
|
5860
|
-
}
|
|
6409
|
+
const consumedEvents = processConsumedLogs({
|
|
6410
|
+
logs: parsedLogs,
|
|
6411
|
+
chainId: client.chain.id
|
|
6412
|
+
});
|
|
6413
|
+
const debtTransfers = processDebtTransfers({
|
|
6414
|
+
logs: parsedLogs,
|
|
6415
|
+
chainId: client.chain.id
|
|
6416
|
+
});
|
|
6417
|
+
const collateralTransfers = processCollateralTransfers({
|
|
6418
|
+
logs: parsedLogs,
|
|
6419
|
+
chainId: client.chain.id
|
|
6420
|
+
});
|
|
6421
|
+
const seizureEvents = processCollateralSeizures({
|
|
6422
|
+
logs: parsedLogs,
|
|
6423
|
+
chainId: client.chain.id
|
|
6424
|
+
});
|
|
5861
6425
|
await db.transaction(async (dbTx) => {
|
|
5862
|
-
const
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
const consumedByGroup = /* @__PURE__ */ new Map();
|
|
5876
|
-
if (groups$3.size > 0) {
|
|
5877
|
-
const groupList = Array.from(groups$3.values());
|
|
5878
|
-
for (let index = 0; index < groupList.length; index += 500) {
|
|
5879
|
-
const slice = groupList.slice(index, index + 500);
|
|
5880
|
-
const { rows } = await dbTx.execute(sql`
|
|
5881
|
-
WITH targets(chain_id, maker, "group") AS (
|
|
5882
|
-
VALUES ${sql.join(slice.map((group) => sql`(${group.chainId}::bigint, ${group.maker.toLowerCase()}::varchar(42), ${group.group.toLowerCase()}::varchar(66))`), sql`,`)}
|
|
5883
|
-
)
|
|
5884
|
-
SELECT
|
|
5885
|
-
targets.chain_id,
|
|
5886
|
-
targets.maker,
|
|
5887
|
-
targets."group",
|
|
5888
|
-
COALESCE(g.consumed, 0)::numeric AS consumed
|
|
5889
|
-
FROM targets
|
|
5890
|
-
LEFT JOIN ${groups} g
|
|
5891
|
-
ON g.chain_id = targets.chain_id
|
|
5892
|
-
AND g.maker = targets.maker
|
|
5893
|
-
AND g."group" = targets."group";
|
|
5894
|
-
`);
|
|
5895
|
-
for (const row of rows) {
|
|
5896
|
-
const groupKey = buildGroupKey({
|
|
5897
|
-
chainId: Number(row.chain_id),
|
|
5898
|
-
maker: row.maker,
|
|
5899
|
-
group: row.group
|
|
5900
|
-
});
|
|
5901
|
-
consumedByGroup.set(groupKey, BigInt(row.consumed ?? "0"));
|
|
5902
|
-
}
|
|
5903
|
-
}
|
|
5904
|
-
}
|
|
5905
|
-
const events = [];
|
|
5906
|
-
for (const log of normalizedLogs) {
|
|
5907
|
-
if (existingEventIds.has(log.id)) continue;
|
|
5908
|
-
const groupKey = buildGroupKey({
|
|
5909
|
-
chainId: log.chainId,
|
|
5910
|
-
maker: log.maker,
|
|
5911
|
-
group: log.group
|
|
5912
|
-
});
|
|
5913
|
-
const previousConsumed = consumedByGroup.get(groupKey) ?? 0n;
|
|
5914
|
-
if (log.kind === "consume") {
|
|
5915
|
-
events.push({
|
|
5916
|
-
id: log.id,
|
|
5917
|
-
chainId: log.chainId,
|
|
5918
|
-
maker: log.maker,
|
|
5919
|
-
group: log.group,
|
|
5920
|
-
amount: log.amount,
|
|
5921
|
-
blockNumber: log.blockNumber
|
|
5922
|
-
});
|
|
5923
|
-
consumedByGroup.set(groupKey, previousConsumed + log.amount);
|
|
5924
|
-
continue;
|
|
5925
|
-
}
|
|
5926
|
-
const delta = log.consumed - previousConsumed;
|
|
5927
|
-
if (delta <= 0n) {
|
|
5928
|
-
logger.debug({
|
|
5929
|
-
collector,
|
|
5930
|
-
chainId: client.chain.id,
|
|
5931
|
-
msg: "Skipping Take log because consumed did not increase",
|
|
5932
|
-
previous_consumed: previousConsumed.toString(),
|
|
5933
|
-
consumed: log.consumed.toString()
|
|
5934
|
-
});
|
|
5935
|
-
continue;
|
|
5936
|
-
}
|
|
5937
|
-
events.push({
|
|
5938
|
-
id: log.id,
|
|
5939
|
-
chainId: log.chainId,
|
|
5940
|
-
maker: log.maker,
|
|
5941
|
-
group: log.group,
|
|
5942
|
-
amount: delta,
|
|
5943
|
-
blockNumber: log.blockNumber
|
|
5944
|
-
});
|
|
5945
|
-
consumedByGroup.set(groupKey, log.consumed);
|
|
5946
|
-
}
|
|
5947
|
-
try {
|
|
5948
|
-
await dbTx.consumed.create(events);
|
|
5949
|
-
if (events.length > 0) logger.info({
|
|
5950
|
-
msg: `Events indexed`,
|
|
5951
|
-
collector,
|
|
5952
|
-
count: events.length,
|
|
5953
|
-
chain_id: client.chain.id,
|
|
5954
|
-
block_range: [startBlock, lastStreamBlockNumber]
|
|
5955
|
-
});
|
|
5956
|
-
} catch (err) {
|
|
5957
|
-
logger.error({
|
|
5958
|
-
err,
|
|
5959
|
-
msg: "Failed to process consumed events"
|
|
5960
|
-
});
|
|
6426
|
+
const [resolvedSeizureTransfers, resolvedConsumedEvents] = await Promise.all([resolveSeizureTransfers({
|
|
6427
|
+
dbTx,
|
|
6428
|
+
seizureEvents
|
|
6429
|
+
}), resolveConsumedEvents({
|
|
6430
|
+
dbTx,
|
|
6431
|
+
consumedEvents,
|
|
6432
|
+
chainId: client.chain.id,
|
|
6433
|
+
collector,
|
|
6434
|
+
logger
|
|
6435
|
+
})]);
|
|
6436
|
+
if (debtTransfers.length > 0) {
|
|
6437
|
+
await dbTx.positions.upsert(transfersToPositions(debtTransfers));
|
|
6438
|
+
await dbTx.transfers.create(debtTransfers);
|
|
5961
6439
|
}
|
|
6440
|
+
const allCollateralTransfers = [...resolvedSeizureTransfers, ...collateralTransfers];
|
|
6441
|
+
if (allCollateralTransfers.length > 0) {
|
|
6442
|
+
await dbTx.positions.upsert(transfersToPositions(allCollateralTransfers));
|
|
6443
|
+
await dbTx.transfers.create(allCollateralTransfers);
|
|
6444
|
+
}
|
|
6445
|
+
await dbTx.consumed.create(resolvedConsumedEvents);
|
|
6446
|
+
if (resolvedConsumedEvents.length > 0) logger.info({
|
|
6447
|
+
msg: "Events indexed",
|
|
6448
|
+
collector,
|
|
6449
|
+
consumed_count: resolvedConsumedEvents.length,
|
|
6450
|
+
debt_transfer_count: debtTransfers.length,
|
|
6451
|
+
collateral_transfer_count: allCollateralTransfers.length,
|
|
6452
|
+
chain_id: client.chain.id,
|
|
6453
|
+
block_range: [startBlock, lastStreamBlockNumber]
|
|
6454
|
+
});
|
|
5962
6455
|
blockNumber = lastStreamBlockNumber;
|
|
5963
6456
|
try {
|
|
5964
6457
|
await dbTx.blocks.advanceCollector({
|
|
@@ -5974,15 +6467,27 @@ async function* collectConsumedEvents(parameters) {
|
|
|
5974
6467
|
chainId: client.chain.id
|
|
5975
6468
|
});
|
|
5976
6469
|
blockNumber = ancestor.blockNumber;
|
|
5977
|
-
const
|
|
6470
|
+
const deletedConsumed = await dbTx.consumed.delete({
|
|
5978
6471
|
chainId: client.chain.id,
|
|
5979
6472
|
blockNumberGte: blockNumber + 1
|
|
5980
6473
|
});
|
|
6474
|
+
const deletedDebtTransfers = await dbTx.transfers.delete({
|
|
6475
|
+
chainId: client.chain.id,
|
|
6476
|
+
blockNumberGte: blockNumber + 1,
|
|
6477
|
+
positionTypeId: positionTypeId(Type.DEBT_OF)
|
|
6478
|
+
});
|
|
6479
|
+
const deletedCollateralTransfers = await dbTx.transfers.delete({
|
|
6480
|
+
chainId: client.chain.id,
|
|
6481
|
+
blockNumberGte: blockNumber + 1,
|
|
6482
|
+
positionTypeId: positionTypeId(Type.COLLATERAL_OF)
|
|
6483
|
+
});
|
|
5981
6484
|
logger.info({
|
|
5982
6485
|
collector,
|
|
5983
6486
|
chain_id: client.chain.id,
|
|
5984
|
-
msg:
|
|
5985
|
-
|
|
6487
|
+
msg: "Reorg detected, events deleted",
|
|
6488
|
+
consumed_deleted: deletedConsumed,
|
|
6489
|
+
debt_transfers_deleted: deletedDebtTransfers,
|
|
6490
|
+
collateral_transfers_deleted: deletedCollateralTransfers,
|
|
5986
6491
|
block_number: blockNumber
|
|
5987
6492
|
});
|
|
5988
6493
|
await dbTx.blocks.advanceCollector({
|
|
@@ -5993,7 +6498,7 @@ async function* collectConsumedEvents(parameters) {
|
|
|
5993
6498
|
});
|
|
5994
6499
|
reorgDetected = true;
|
|
5995
6500
|
} catch (err) {
|
|
5996
|
-
const msg = "Failed to delete
|
|
6501
|
+
const msg = "Failed to delete events when handling reorg.";
|
|
5997
6502
|
logger.error({
|
|
5998
6503
|
collector,
|
|
5999
6504
|
chainId: client.chain.id,
|
|
@@ -6008,10 +6513,210 @@ async function* collectConsumedEvents(parameters) {
|
|
|
6008
6513
|
yield blockNumber;
|
|
6009
6514
|
startBlock = blockNumber;
|
|
6010
6515
|
}
|
|
6516
|
+
if (!reorgDetected) await db.transaction(async (dbTx) => {
|
|
6517
|
+
const collectorState = await dbTx.blocks.getCollector({
|
|
6518
|
+
collectorName: collector,
|
|
6519
|
+
chainId: client.chain.id
|
|
6520
|
+
});
|
|
6521
|
+
const deletedConsumed = await dbTx.consumed.delete({
|
|
6522
|
+
chainId: client.chain.id,
|
|
6523
|
+
blockNumberGte: collectorState.blockNumber + 1
|
|
6524
|
+
});
|
|
6525
|
+
const deletedDebtTransfers = await dbTx.transfers.delete({
|
|
6526
|
+
chainId: client.chain.id,
|
|
6527
|
+
blockNumberGte: collectorState.blockNumber + 1,
|
|
6528
|
+
positionTypeId: positionTypeId(Type.DEBT_OF)
|
|
6529
|
+
});
|
|
6530
|
+
const deletedCollateralTransfers = await dbTx.transfers.delete({
|
|
6531
|
+
chainId: client.chain.id,
|
|
6532
|
+
blockNumberGte: collectorState.blockNumber + 1,
|
|
6533
|
+
positionTypeId: positionTypeId(Type.COLLATERAL_OF)
|
|
6534
|
+
});
|
|
6535
|
+
if (deletedConsumed > 0 || deletedDebtTransfers > 0 || deletedCollateralTransfers > 0) logger.info({
|
|
6536
|
+
collector,
|
|
6537
|
+
chain_id: client.chain.id,
|
|
6538
|
+
msg: "Reorg detected, events deleted",
|
|
6539
|
+
consumed_deleted: deletedConsumed,
|
|
6540
|
+
debt_transfers_deleted: deletedDebtTransfers,
|
|
6541
|
+
collateral_transfers_deleted: deletedCollateralTransfers,
|
|
6542
|
+
block_number: collectorState.blockNumber
|
|
6543
|
+
});
|
|
6544
|
+
});
|
|
6545
|
+
}
|
|
6546
|
+
async function resolveSeizureTransfers(parameters) {
|
|
6547
|
+
const { dbTx, seizureEvents } = parameters;
|
|
6548
|
+
if (seizureEvents.length === 0) return [];
|
|
6549
|
+
const uniqueObligationIds = [...new Set(seizureEvents.map((event) => event.obligationId.toLowerCase()))];
|
|
6550
|
+
const rows = await dbTx.select({
|
|
6551
|
+
obligationId: obligationIdKeys.obligationId,
|
|
6552
|
+
collateralIndex: obligationCollateralsV2.collateralIndex,
|
|
6553
|
+
asset: obligationCollateralsV2.asset
|
|
6554
|
+
}).from(obligationIdKeys).innerJoin(obligationCollateralsV2, eq(obligationIdKeys.obligationKey, obligationCollateralsV2.obligationKey)).where(inArray(obligationIdKeys.obligationId, uniqueObligationIds));
|
|
6555
|
+
const resolutionMap = /* @__PURE__ */ new Map();
|
|
6556
|
+
for (const row of rows) resolutionMap.set(`${row.obligationId.toLowerCase()}-${row.collateralIndex}`, row.asset);
|
|
6557
|
+
const resolvedSeizureTransfers = [];
|
|
6558
|
+
for (const seizure of seizureEvents) {
|
|
6559
|
+
const key = `${seizure.obligationId.toLowerCase()}-${seizure.collateralIndex}`;
|
|
6560
|
+
const asset = resolutionMap.get(key);
|
|
6561
|
+
if (asset === void 0) throw new Error(`Unresolvable Liquidate seizure: obligationId=${seizure.obligationId}, collateralIndex=${seizure.collateralIndex}, chainId=${seizure.chainId}, blockNumber=${seizure.blockNumber}`);
|
|
6562
|
+
resolvedSeizureTransfers.push(from$8({
|
|
6563
|
+
id: seizure.id,
|
|
6564
|
+
chainId: seizure.chainId,
|
|
6565
|
+
contract: seizure.obligationId,
|
|
6566
|
+
from: seizure.user,
|
|
6567
|
+
to: zeroAddress,
|
|
6568
|
+
value: seizure.amount,
|
|
6569
|
+
type: Type.COLLATERAL_OF,
|
|
6570
|
+
asset,
|
|
6571
|
+
blockNumber: seizure.blockNumber
|
|
6572
|
+
}));
|
|
6573
|
+
}
|
|
6574
|
+
return resolvedSeizureTransfers;
|
|
6575
|
+
}
|
|
6576
|
+
async function resolveConsumedEvents(parameters) {
|
|
6577
|
+
const { dbTx, consumedEvents, chainId, collector, logger } = parameters;
|
|
6578
|
+
if (consumedEvents.length === 0) return [];
|
|
6579
|
+
const [existingConsumedIds, consumedByGroup] = await Promise.all([getExistingConsumedIds({
|
|
6580
|
+
dbTx,
|
|
6581
|
+
consumedEvents
|
|
6582
|
+
}), getConsumedByGroup({
|
|
6583
|
+
dbTx,
|
|
6584
|
+
consumedEvents
|
|
6585
|
+
})]);
|
|
6586
|
+
const resolvedEvents = [];
|
|
6587
|
+
for (const log of consumedEvents) {
|
|
6588
|
+
if (existingConsumedIds.has(log.id)) continue;
|
|
6589
|
+
const groupKey = buildGroupKey({
|
|
6590
|
+
chainId: log.chainId,
|
|
6591
|
+
maker: log.maker,
|
|
6592
|
+
group: log.group
|
|
6593
|
+
});
|
|
6594
|
+
const previousConsumed = consumedByGroup.get(groupKey) ?? 0n;
|
|
6595
|
+
if (log.kind === "consume") {
|
|
6596
|
+
resolvedEvents.push({
|
|
6597
|
+
id: log.id,
|
|
6598
|
+
chainId: log.chainId,
|
|
6599
|
+
maker: log.maker,
|
|
6600
|
+
group: log.group,
|
|
6601
|
+
amount: log.amount,
|
|
6602
|
+
blockNumber: log.blockNumber
|
|
6603
|
+
});
|
|
6604
|
+
consumedByGroup.set(groupKey, previousConsumed + log.amount);
|
|
6605
|
+
continue;
|
|
6606
|
+
}
|
|
6607
|
+
const delta = log.consumed - previousConsumed;
|
|
6608
|
+
if (delta <= 0n) {
|
|
6609
|
+
logger.debug({
|
|
6610
|
+
collector,
|
|
6611
|
+
chainId,
|
|
6612
|
+
msg: "Skipping Take log because consumed did not increase",
|
|
6613
|
+
previous_consumed: previousConsumed.toString(),
|
|
6614
|
+
consumed: log.consumed.toString()
|
|
6615
|
+
});
|
|
6616
|
+
continue;
|
|
6617
|
+
}
|
|
6618
|
+
resolvedEvents.push({
|
|
6619
|
+
id: log.id,
|
|
6620
|
+
chainId: log.chainId,
|
|
6621
|
+
maker: log.maker,
|
|
6622
|
+
group: log.group,
|
|
6623
|
+
amount: delta,
|
|
6624
|
+
blockNumber: log.blockNumber
|
|
6625
|
+
});
|
|
6626
|
+
consumedByGroup.set(groupKey, log.consumed);
|
|
6627
|
+
}
|
|
6628
|
+
return resolvedEvents;
|
|
6629
|
+
}
|
|
6630
|
+
async function getExistingConsumedIds(parameters) {
|
|
6631
|
+
const { dbTx, consumedEvents: consumedEvents$1 } = parameters;
|
|
6632
|
+
const existingConsumedIds = /* @__PURE__ */ new Set();
|
|
6633
|
+
const ids = Array.from(new Set(consumedEvents$1.map((event) => event.id)));
|
|
6634
|
+
for (let index = 0; index < ids.length; index += 500) {
|
|
6635
|
+
const slice = ids.slice(index, index + 500);
|
|
6636
|
+
const { rows } = await dbTx.execute(sql`
|
|
6637
|
+
SELECT event_id
|
|
6638
|
+
FROM ${consumedEvents}
|
|
6639
|
+
WHERE event_id IN (${sql.join(slice.map((id) => sql`${id}`), sql`,`)});
|
|
6640
|
+
`);
|
|
6641
|
+
for (const row of rows) existingConsumedIds.add(row.event_id);
|
|
6642
|
+
}
|
|
6643
|
+
return existingConsumedIds;
|
|
6644
|
+
}
|
|
6645
|
+
async function getConsumedByGroup(parameters) {
|
|
6646
|
+
const { dbTx, consumedEvents } = parameters;
|
|
6647
|
+
const groups$3 = /* @__PURE__ */ new Map();
|
|
6648
|
+
for (const event of consumedEvents) {
|
|
6649
|
+
const key = buildGroupKey({
|
|
6650
|
+
chainId: event.chainId,
|
|
6651
|
+
maker: event.maker,
|
|
6652
|
+
group: event.group
|
|
6653
|
+
});
|
|
6654
|
+
if (!groups$3.has(key)) groups$3.set(key, {
|
|
6655
|
+
chainId: event.chainId,
|
|
6656
|
+
maker: event.maker,
|
|
6657
|
+
group: event.group
|
|
6658
|
+
});
|
|
6659
|
+
}
|
|
6660
|
+
const consumedByGroup = /* @__PURE__ */ new Map();
|
|
6661
|
+
const groupList = Array.from(groups$3.values());
|
|
6662
|
+
for (let index = 0; index < groupList.length; index += 500) {
|
|
6663
|
+
const slice = groupList.slice(index, index + 500);
|
|
6664
|
+
const { rows } = await dbTx.execute(sql`
|
|
6665
|
+
WITH targets(chain_id, maker, "group") AS (
|
|
6666
|
+
VALUES ${sql.join(slice.map((group) => sql`(${group.chainId}::bigint, ${group.maker.toLowerCase()}::varchar(42), ${group.group.toLowerCase()}::varchar(66))`), sql`,`)}
|
|
6667
|
+
)
|
|
6668
|
+
SELECT
|
|
6669
|
+
targets.chain_id,
|
|
6670
|
+
targets.maker,
|
|
6671
|
+
targets."group",
|
|
6672
|
+
COALESCE(g.consumed, 0)::numeric AS consumed
|
|
6673
|
+
FROM targets
|
|
6674
|
+
LEFT JOIN ${groups} g
|
|
6675
|
+
ON g.chain_id = targets.chain_id
|
|
6676
|
+
AND g.maker = targets.maker
|
|
6677
|
+
AND g."group" = targets."group";
|
|
6678
|
+
`);
|
|
6679
|
+
for (const row of rows) {
|
|
6680
|
+
const groupKey = buildGroupKey({
|
|
6681
|
+
chainId: Number(row.chain_id),
|
|
6682
|
+
maker: row.maker,
|
|
6683
|
+
group: row.group
|
|
6684
|
+
});
|
|
6685
|
+
consumedByGroup.set(groupKey, BigInt(row.consumed ?? "0"));
|
|
6686
|
+
}
|
|
6687
|
+
}
|
|
6688
|
+
return consumedByGroup;
|
|
6689
|
+
}
|
|
6690
|
+
function transfersToPositions(transfers) {
|
|
6691
|
+
if (transfers.length === 0) return [];
|
|
6692
|
+
const transferType = transfers[0].type;
|
|
6693
|
+
if (transfers.some((transfer) => transfer.type !== transferType)) throw new Error("Cannot map transfers with mixed position types to positions.");
|
|
6694
|
+
const positionKeys = /* @__PURE__ */ new Map();
|
|
6695
|
+
for (const transfer of transfers) for (const user of [transfer.from, transfer.to]) {
|
|
6696
|
+
const key = `${transfer.chainId}-${transfer.contract}-${user}-${transfer.asset}`.toLowerCase();
|
|
6697
|
+
const existing = positionKeys.get(key);
|
|
6698
|
+
if (!existing || transfer.blockNumber < existing.blockNumber) positionKeys.set(key, {
|
|
6699
|
+
chainId: transfer.chainId,
|
|
6700
|
+
contract: transfer.contract,
|
|
6701
|
+
user,
|
|
6702
|
+
asset: transfer.asset,
|
|
6703
|
+
blockNumber: transfer.blockNumber
|
|
6704
|
+
});
|
|
6705
|
+
}
|
|
6706
|
+
return Array.from(positionKeys.values()).map((position) => from$10({
|
|
6707
|
+
chainId: position.chainId,
|
|
6708
|
+
contract: position.contract,
|
|
6709
|
+
user: position.user,
|
|
6710
|
+
type: transferType,
|
|
6711
|
+
balance: 0n,
|
|
6712
|
+
asset: position.asset,
|
|
6713
|
+
blockNumber: position.blockNumber
|
|
6714
|
+
}));
|
|
6011
6715
|
}
|
|
6012
6716
|
|
|
6013
6717
|
//#endregion
|
|
6014
6718
|
//#region src/indexer/collectors/CollectFunctions/collectOffers.ts
|
|
6719
|
+
const ERC20_TYPE_ID = Object.values(Type).indexOf(Type.ERC20) + 1;
|
|
6015
6720
|
async function* collectOffersV2(parameters) {
|
|
6016
6721
|
let { db, collector, client, lastBlockNumber: blockNumber, gatekeeper, options: { maxBatchSize = 1e3, blockWindow } = {} } = parameters;
|
|
6017
6722
|
const logger = getLogger();
|
|
@@ -6025,9 +6730,10 @@ async function* collectOffersV2(parameters) {
|
|
|
6025
6730
|
});
|
|
6026
6731
|
throw new Error(msg);
|
|
6027
6732
|
}
|
|
6733
|
+
const morphoV2 = client.chain.custom.morpho.address;
|
|
6028
6734
|
const signatureDomain = {
|
|
6029
6735
|
chainId: client.chain.id,
|
|
6030
|
-
verifyingContract:
|
|
6736
|
+
verifyingContract: morphoV2
|
|
6031
6737
|
};
|
|
6032
6738
|
const { blockNumber: latestBlockNumberChain } = await db.blocks.getChain(client.chain.id);
|
|
6033
6739
|
const stream = streamLogs({
|
|
@@ -6090,10 +6796,14 @@ async function* collectOffersV2(parameters) {
|
|
|
6090
6796
|
await db.transaction(async (dbTx) => {
|
|
6091
6797
|
const { epoch, blockNumber: latestBlockNumber } = await dbTx.blocks.getChain(client.chain.id);
|
|
6092
6798
|
const treesToInsert = [];
|
|
6799
|
+
const pathsToInsert = [];
|
|
6093
6800
|
let totalValidOffers = 0;
|
|
6094
6801
|
const offersWithBlock = [];
|
|
6095
6802
|
for (const { tree, signature, blockNumber: treeBlockNumber } of decodedTrees) try {
|
|
6096
|
-
const allowedResults = await gatekeeper.isAllowed(
|
|
6803
|
+
const allowedResults = await gatekeeper.isAllowed({
|
|
6804
|
+
offers: tree.offers,
|
|
6805
|
+
chainId: client.chain.id
|
|
6806
|
+
});
|
|
6097
6807
|
const hasBlockWindowViolation = treeBlockNumber > latestBlockNumber;
|
|
6098
6808
|
if (!(allowedResults.issues.length === 0 && allowedResults.valid.length === tree.offers.length) || hasBlockWindowViolation) {
|
|
6099
6809
|
if (allowedResults.issues.length > 0) {
|
|
@@ -6112,14 +6822,35 @@ async function* collectOffersV2(parameters) {
|
|
|
6112
6822
|
continue;
|
|
6113
6823
|
}
|
|
6114
6824
|
treesToInsert.push({
|
|
6115
|
-
tree,
|
|
6825
|
+
root: tree.root,
|
|
6116
6826
|
signature
|
|
6117
6827
|
});
|
|
6118
6828
|
totalValidOffers += tree.offers.length;
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6829
|
+
const obligationIdsByOfferHash = /* @__PURE__ */ new Map();
|
|
6830
|
+
for (const offer of tree.offers) {
|
|
6831
|
+
const offerHash = hash(offer).toLowerCase();
|
|
6832
|
+
const obligationId$3 = obligationId(offer, {
|
|
6833
|
+
chainId: client.chain.id,
|
|
6834
|
+
morphoV2
|
|
6835
|
+
}).toLowerCase();
|
|
6836
|
+
offersWithBlock.push({
|
|
6837
|
+
offer,
|
|
6838
|
+
blockNumber: treeBlockNumber,
|
|
6839
|
+
obligationId: obligationId$3
|
|
6840
|
+
});
|
|
6841
|
+
obligationIdsByOfferHash.set(offerHash, obligationId$3);
|
|
6842
|
+
}
|
|
6843
|
+
for (const proof of proofs(tree)) {
|
|
6844
|
+
const offerHash = hash(proof.offer).toLowerCase();
|
|
6845
|
+
const obligationId = obligationIdsByOfferHash.get(offerHash);
|
|
6846
|
+
if (obligationId === void 0) continue;
|
|
6847
|
+
pathsToInsert.push({
|
|
6848
|
+
offerHash,
|
|
6849
|
+
obligationId,
|
|
6850
|
+
treeRoot: tree.root,
|
|
6851
|
+
proof: proof.path
|
|
6852
|
+
});
|
|
6853
|
+
}
|
|
6123
6854
|
} catch (err) {
|
|
6124
6855
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
6125
6856
|
logger.error({
|
|
@@ -6129,19 +6860,23 @@ async function* collectOffersV2(parameters) {
|
|
|
6129
6860
|
});
|
|
6130
6861
|
throw new Error("Gatekeeper validation failed", { cause: error });
|
|
6131
6862
|
}
|
|
6132
|
-
const dependencies = buildOfferDependencies$1(
|
|
6863
|
+
const dependencies = buildOfferDependencies$1({
|
|
6864
|
+
offers: offersWithBlock,
|
|
6865
|
+
chainId: client.chain.id,
|
|
6866
|
+
morphoV2
|
|
6867
|
+
});
|
|
6133
6868
|
await dbTx.oracles.upsert(dependencies.oracles);
|
|
6134
6869
|
await dbTx.obligations.create(dependencies.obligations);
|
|
6135
6870
|
await dbTx.groups.create(dependencies.groups);
|
|
6136
|
-
const
|
|
6137
|
-
if (treesToInsert.length > 0) await dbTx.trees.
|
|
6138
|
-
|
|
6139
|
-
offers: offersWithBlock,
|
|
6140
|
-
hashes: insertedHashes
|
|
6141
|
-
});
|
|
6871
|
+
const insertedReferences = await dbTx.offers.create(dependencies.offerBatches);
|
|
6872
|
+
if (treesToInsert.length > 0) await dbTx.trees.upsert(treesToInsert);
|
|
6873
|
+
if (pathsToInsert.length > 0) await dbTx.trees.upsertPaths(pathsToInsert);
|
|
6142
6874
|
const { callbacks, positions, lots } = decodeCallbacks({
|
|
6143
|
-
|
|
6144
|
-
|
|
6875
|
+
offers: filterInsertedOffers({
|
|
6876
|
+
offers: offersWithBlock,
|
|
6877
|
+
references: insertedReferences
|
|
6878
|
+
}),
|
|
6879
|
+
chainId: client.chain.id
|
|
6145
6880
|
});
|
|
6146
6881
|
if (positions.length > 0) await dbTx.positions.upsert(positions);
|
|
6147
6882
|
if (callbacks.length > 0) await dbTx.callbacks.upsert(callbacks);
|
|
@@ -6204,7 +6939,7 @@ async function* collectOffersV2(parameters) {
|
|
|
6204
6939
|
}
|
|
6205
6940
|
}
|
|
6206
6941
|
function decodeCallbacks(parameters) {
|
|
6207
|
-
const { offers } = parameters;
|
|
6942
|
+
const { offers, chainId } = parameters;
|
|
6208
6943
|
if (offers.length === 0) return {
|
|
6209
6944
|
callbacks: [],
|
|
6210
6945
|
positions: [],
|
|
@@ -6213,12 +6948,12 @@ function decodeCallbacks(parameters) {
|
|
|
6213
6948
|
const callbacks = [];
|
|
6214
6949
|
const positions = [];
|
|
6215
6950
|
const lots = [];
|
|
6216
|
-
for (const { offer, blockNumber: offerBlockNumber } of offers) {
|
|
6951
|
+
for (const { offer, blockNumber: offerBlockNumber, obligationId } of offers) {
|
|
6217
6952
|
if (!offer.buy) continue;
|
|
6218
6953
|
if (!isEmptyCallback(offer)) continue;
|
|
6219
6954
|
const loanToken = offer.loanToken.toLowerCase();
|
|
6220
6955
|
positions.push(from$10({
|
|
6221
|
-
chainId
|
|
6956
|
+
chainId,
|
|
6222
6957
|
contract: loanToken,
|
|
6223
6958
|
user: offer.maker,
|
|
6224
6959
|
type: Type.ERC20,
|
|
@@ -6226,20 +6961,23 @@ function decodeCallbacks(parameters) {
|
|
|
6226
6961
|
blockNumber: offerBlockNumber
|
|
6227
6962
|
}));
|
|
6228
6963
|
lots.push({
|
|
6229
|
-
positionChainId:
|
|
6964
|
+
positionChainId: chainId,
|
|
6230
6965
|
positionContract: loanToken,
|
|
6231
6966
|
positionUser: offer.maker,
|
|
6967
|
+
positionTypeId: ERC20_TYPE_ID,
|
|
6232
6968
|
group: offer.group,
|
|
6233
|
-
obligationId
|
|
6969
|
+
obligationId,
|
|
6234
6970
|
size: offer.assets
|
|
6235
6971
|
});
|
|
6236
6972
|
callbacks.push({
|
|
6237
6973
|
offerHash: hash(offer),
|
|
6974
|
+
obligationId,
|
|
6238
6975
|
callbacks: [{
|
|
6239
|
-
chainId
|
|
6976
|
+
chainId,
|
|
6240
6977
|
contract: loanToken,
|
|
6241
6978
|
user: offer.maker,
|
|
6242
|
-
amount: offer.assets
|
|
6979
|
+
amount: offer.assets,
|
|
6980
|
+
positionTypeId: ERC20_TYPE_ID
|
|
6243
6981
|
}]
|
|
6244
6982
|
});
|
|
6245
6983
|
}
|
|
@@ -6249,34 +6987,42 @@ function decodeCallbacks(parameters) {
|
|
|
6249
6987
|
lots
|
|
6250
6988
|
};
|
|
6251
6989
|
}
|
|
6252
|
-
function buildOfferDependencies$1(
|
|
6990
|
+
function buildOfferDependencies$1(parameters) {
|
|
6991
|
+
const { offers, chainId, morphoV2 } = parameters;
|
|
6253
6992
|
const obligationsById = /* @__PURE__ */ new Map();
|
|
6254
6993
|
const oraclesByKey = /* @__PURE__ */ new Map();
|
|
6255
6994
|
const groupsByKey = /* @__PURE__ */ new Map();
|
|
6256
6995
|
const offersByBlock = /* @__PURE__ */ new Map();
|
|
6257
|
-
for (const { offer, blockNumber } of offers) {
|
|
6996
|
+
for (const { offer, blockNumber, obligationId } of offers) {
|
|
6258
6997
|
const list = offersByBlock.get(blockNumber) ?? [];
|
|
6259
|
-
list.push(
|
|
6998
|
+
list.push({
|
|
6999
|
+
offer,
|
|
7000
|
+
obligationId,
|
|
7001
|
+
chainId
|
|
7002
|
+
});
|
|
6260
7003
|
offersByBlock.set(blockNumber, list);
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
chainId
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
7004
|
+
if (!obligationsById.get(obligationId)) obligationsById.set(obligationId, {
|
|
7005
|
+
obligationId,
|
|
7006
|
+
chainId,
|
|
7007
|
+
morphoV2,
|
|
7008
|
+
obligation: from$13({
|
|
7009
|
+
loanToken: offer.loanToken,
|
|
7010
|
+
maturity: offer.maturity,
|
|
7011
|
+
collaterals: offer.collaterals
|
|
7012
|
+
})
|
|
7013
|
+
});
|
|
6268
7014
|
for (const collateral of offer.collaterals) {
|
|
6269
|
-
const oracleKey = `${
|
|
7015
|
+
const oracleKey = `${chainId}-${collateral.oracle}`.toLowerCase();
|
|
6270
7016
|
if (!oraclesByKey.has(oracleKey)) oraclesByKey.set(oracleKey, from$11({
|
|
6271
|
-
chainId
|
|
7017
|
+
chainId,
|
|
6272
7018
|
address: collateral.oracle,
|
|
6273
7019
|
price: null,
|
|
6274
7020
|
blockNumber
|
|
6275
7021
|
}));
|
|
6276
7022
|
}
|
|
6277
|
-
const groupKey = `${
|
|
7023
|
+
const groupKey = `${chainId}-${offer.maker}-${offer.group}`.toLowerCase();
|
|
6278
7024
|
if (!groupsByKey.has(groupKey)) groupsByKey.set(groupKey, {
|
|
6279
|
-
chainId
|
|
7025
|
+
chainId,
|
|
6280
7026
|
maker: offer.maker,
|
|
6281
7027
|
group: offer.group,
|
|
6282
7028
|
blockNumber
|
|
@@ -6293,15 +7039,22 @@ function buildOfferDependencies$1(offers) {
|
|
|
6293
7039
|
};
|
|
6294
7040
|
}
|
|
6295
7041
|
function filterInsertedOffers(parameters) {
|
|
6296
|
-
if (parameters.
|
|
6297
|
-
const
|
|
7042
|
+
if (parameters.references.length === 0) return [];
|
|
7043
|
+
const keyOf = (input) => `${input.hash.toLowerCase()}:${input.obligationId.toLowerCase()}`;
|
|
7044
|
+
const inserted = new Set(parameters.references.map((offer) => keyOf({
|
|
7045
|
+
hash: offer.hash,
|
|
7046
|
+
obligationId: offer.obligationId
|
|
7047
|
+
})));
|
|
6298
7048
|
const seen = /* @__PURE__ */ new Set();
|
|
6299
7049
|
const filtered = [];
|
|
6300
7050
|
for (const entry of parameters.offers) {
|
|
6301
|
-
const
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
7051
|
+
const key = keyOf({
|
|
7052
|
+
hash: hash(entry.offer),
|
|
7053
|
+
obligationId: entry.obligationId
|
|
7054
|
+
});
|
|
7055
|
+
if (!inserted.has(key)) continue;
|
|
7056
|
+
if (seen.has(key)) continue;
|
|
7057
|
+
seen.add(key);
|
|
6305
7058
|
filtered.push(entry);
|
|
6306
7059
|
}
|
|
6307
7060
|
return filtered;
|
|
@@ -6416,7 +7169,7 @@ async function snapshotVaultPositions(parameters) {
|
|
|
6416
7169
|
convertToAssets: (shares) => {
|
|
6417
7170
|
const contract = contracts.get(position.contract.toLowerCase());
|
|
6418
7171
|
if (!contract) return;
|
|
6419
|
-
if (contract.decimalsOffset === void 0 || contract.totalAssets === void 0 || contract.totalSupply === void 0) return;
|
|
7172
|
+
if (contract.decimalsOffset === void 0 || contract.totalAssets === void 0 || contract.totalSupply === void 0 || contract.asset === void 0) return;
|
|
6420
7173
|
try {
|
|
6421
7174
|
position.balance = convertToAssets({
|
|
6422
7175
|
shares,
|
|
@@ -6571,12 +7324,15 @@ async function* collectPositions(parameters) {
|
|
|
6571
7324
|
from: log.args.from,
|
|
6572
7325
|
to: log.args.to,
|
|
6573
7326
|
value: log.args.value,
|
|
7327
|
+
type: Type.ERC20,
|
|
7328
|
+
asset: log.address,
|
|
6574
7329
|
blockNumber: Number(log.blockNumber)
|
|
6575
7330
|
}));
|
|
6576
7331
|
}
|
|
6577
7332
|
const { positions } = await db.positions.get({
|
|
6578
7333
|
chainId: client.chain.id,
|
|
6579
|
-
filled: false
|
|
7334
|
+
filled: false,
|
|
7335
|
+
type: Type.ERC20
|
|
6580
7336
|
});
|
|
6581
7337
|
const newPositions = [];
|
|
6582
7338
|
try {
|
|
@@ -6699,7 +7455,8 @@ async function* collectPositions(parameters) {
|
|
|
6699
7455
|
blockNumber = ancestor.blockNumber;
|
|
6700
7456
|
const emptied = await dbTx.positions.setEmptyAfter({
|
|
6701
7457
|
chainId: client.chain.id,
|
|
6702
|
-
blockNumber: blockNumber + 1
|
|
7458
|
+
blockNumber: blockNumber + 1,
|
|
7459
|
+
type: Type.ERC20
|
|
6703
7460
|
});
|
|
6704
7461
|
logger.info({
|
|
6705
7462
|
msg: "Reorg detected, positions set to empty",
|
|
@@ -6926,10 +7683,10 @@ function createBuilder(parameters) {
|
|
|
6926
7683
|
}
|
|
6927
7684
|
}));
|
|
6928
7685
|
},
|
|
6929
|
-
|
|
6930
|
-
return createCollector("
|
|
7686
|
+
buildMorphoV2Collector: ({ options: { maxBatchSize = 1e3 } = {} } = {}) => {
|
|
7687
|
+
return createCollector("morpho_v2", (p) => collectMorphoV2({
|
|
6931
7688
|
...p,
|
|
6932
|
-
collector: "
|
|
7689
|
+
collector: "morpho_v2",
|
|
6933
7690
|
options: {
|
|
6934
7691
|
maxBatchSize,
|
|
6935
7692
|
blockWindow
|
|
@@ -6978,7 +7735,7 @@ const from$2 = (parameters) => {
|
|
|
6978
7735
|
});
|
|
6979
7736
|
return {
|
|
6980
7737
|
offersCollector: collectorBuilder.buildOffersCollector({ options: { maxBatchSize } }),
|
|
6981
|
-
|
|
7738
|
+
morphoV2Collector: collectorBuilder.buildMorphoV2Collector({ options: { maxBatchSize } }),
|
|
6982
7739
|
pricesCollector: collectorBuilder.buildPricesCollector({ options: {
|
|
6983
7740
|
maxBatchSize,
|
|
6984
7741
|
retryAttempts,
|
|
@@ -6996,7 +7753,7 @@ const from$2 = (parameters) => {
|
|
|
6996
7753
|
//#region src/indexer/Indexer.ts
|
|
6997
7754
|
function from$1(config) {
|
|
6998
7755
|
const { client, gatekeeper, db, interval = 1e4, maxBatchSize = 1e3, maxBlockNumber, blockWindow, retryAttempts, retryDelayMs } = config;
|
|
6999
|
-
const { offersCollector,
|
|
7756
|
+
const { offersCollector, morphoV2Collector, positionsCollector, pricesCollector } = from$2({
|
|
7000
7757
|
client,
|
|
7001
7758
|
db,
|
|
7002
7759
|
gatekeeper,
|
|
@@ -7011,7 +7768,7 @@ function from$1(config) {
|
|
|
7011
7768
|
client,
|
|
7012
7769
|
collectors: [
|
|
7013
7770
|
offersCollector,
|
|
7014
|
-
|
|
7771
|
+
morphoV2Collector,
|
|
7015
7772
|
positionsCollector,
|
|
7016
7773
|
pricesCollector
|
|
7017
7774
|
]
|
|
@@ -7282,7 +8039,7 @@ const reconcile = async (parameters) => {
|
|
|
7282
8039
|
//#region src/indexer/collectors/Collector.ts
|
|
7283
8040
|
const names = [
|
|
7284
8041
|
"offers",
|
|
7285
|
-
"
|
|
8042
|
+
"morpho_v2",
|
|
7286
8043
|
"positions",
|
|
7287
8044
|
"prices"
|
|
7288
8045
|
];
|
|
@@ -7681,18 +8438,17 @@ async function _getOffers(db, params) {
|
|
|
7681
8438
|
const { obligationId, side, now, priceSortDirection, cursor, limit } = params;
|
|
7682
8439
|
const raw = await db.execute(sql`
|
|
7683
8440
|
WITH collats AS MATERIALIZED (
|
|
7684
|
-
SELECT
|
|
8441
|
+
SELECT oia.obligation_id,
|
|
7685
8442
|
COALESCE(jsonb_agg(jsonb_build_object(
|
|
7686
8443
|
'asset', oc.asset,
|
|
7687
|
-
'oracle',
|
|
8444
|
+
'oracle', oc.oracle_address,
|
|
7688
8445
|
'lltv', oc.lltv
|
|
7689
8446
|
) ORDER BY oc.asset), '[]'::jsonb) AS collaterals
|
|
7690
|
-
FROM ${
|
|
7691
|
-
JOIN ${
|
|
7692
|
-
ON
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
GROUP BY oc.obligation_id
|
|
8447
|
+
FROM ${obligationIdKeys} oia
|
|
8448
|
+
JOIN ${obligationCollateralsV2} oc
|
|
8449
|
+
ON oc.obligation_key = oia.obligation_key
|
|
8450
|
+
WHERE oia.obligation_id = ${obligationId}
|
|
8451
|
+
GROUP BY oia.obligation_id
|
|
7696
8452
|
),
|
|
7697
8453
|
winners AS (
|
|
7698
8454
|
SELECT DISTINCT ON (o.group_chain_id, o.group_maker, o."group_group")
|
|
@@ -7700,6 +8456,7 @@ async function _getOffers(db, params) {
|
|
|
7700
8456
|
FROM ${offers} o
|
|
7701
8457
|
LEFT JOIN ${validations} v
|
|
7702
8458
|
ON v.offer_hash = o.hash
|
|
8459
|
+
AND v.obligation_id = o.obligation_id
|
|
7703
8460
|
LEFT JOIN ${status} s
|
|
7704
8461
|
ON s.id = v.status_id
|
|
7705
8462
|
WHERE o.obligation_id = ${obligationId}
|
|
@@ -7726,8 +8483,10 @@ async function _getOffers(db, params) {
|
|
|
7726
8483
|
ON g.chain_id = w.group_chain_id
|
|
7727
8484
|
AND g.maker = w.group_maker
|
|
7728
8485
|
AND g."group" = w."group_group"
|
|
8486
|
+
JOIN ${obligationIdKeys} oia
|
|
8487
|
+
ON oia.obligation_id = w.obligation_id
|
|
7729
8488
|
JOIN ${obligations} obl
|
|
7730
|
-
ON obl.
|
|
8489
|
+
ON obl.obligation_key = oia.obligation_key
|
|
7731
8490
|
),
|
|
7732
8491
|
paged AS (
|
|
7733
8492
|
SELECT e.*
|
|
@@ -7826,7 +8585,9 @@ async function _getOffers(db, params) {
|
|
|
7826
8585
|
END
|
|
7827
8586
|
)) AS lot_balance
|
|
7828
8587
|
FROM paged p
|
|
7829
|
-
LEFT JOIN ${offersCallbacks} oc
|
|
8588
|
+
LEFT JOIN ${offersCallbacks} oc
|
|
8589
|
+
ON oc.offer_hash = p.hash
|
|
8590
|
+
AND oc.obligation_id = p.obligation_id
|
|
7830
8591
|
LEFT JOIN ${callbacks} c ON c.id = oc.callback_id
|
|
7831
8592
|
LEFT JOIN ${lots} l
|
|
7832
8593
|
ON l.chain_id = c.position_chain_id
|
|
@@ -7911,11 +8672,13 @@ async function _getOffers(db, params) {
|
|
|
7911
8672
|
AND NOT EXISTS (
|
|
7912
8673
|
SELECT 1 FROM ${offersCallbacks} oc2
|
|
7913
8674
|
WHERE oc2.offer_hash = p.hash
|
|
8675
|
+
AND oc2.obligation_id = p.obligation_id
|
|
7914
8676
|
)
|
|
7915
8677
|
)
|
|
7916
8678
|
-- Final SELECT with inline takeable computation
|
|
7917
8679
|
SELECT
|
|
7918
8680
|
oc.hash,
|
|
8681
|
+
oc.obligation_id,
|
|
7919
8682
|
oc.group_maker,
|
|
7920
8683
|
oc.assets,
|
|
7921
8684
|
oc.obligation_units,
|
|
@@ -7964,6 +8727,7 @@ async function _getOffers(db, params) {
|
|
|
7964
8727
|
const receiverIfMakerIsSeller = (row.receiver_if_maker_is_seller ?? row.group_maker).toLowerCase();
|
|
7965
8728
|
return {
|
|
7966
8729
|
hash: row.hash,
|
|
8730
|
+
obligationId: row.obligation_id,
|
|
7967
8731
|
maker: row.group_maker,
|
|
7968
8732
|
assets: BigInt(row.assets),
|
|
7969
8733
|
obligationUnits: BigInt(row.obligation_units ?? 0),
|
|
@@ -8092,18 +8856,21 @@ function create$14(db) {
|
|
|
8092
8856
|
idCache.set(preimage, id);
|
|
8093
8857
|
return id;
|
|
8094
8858
|
};
|
|
8095
|
-
for (const { offerHash, callbacks } of inputs) {
|
|
8859
|
+
for (const { offerHash, obligationId, callbacks } of inputs) {
|
|
8096
8860
|
const normalizedOfferHash = offerHash.toLowerCase();
|
|
8861
|
+
const normalizedObligationId = obligationId.toLowerCase();
|
|
8097
8862
|
for (const callback of callbacks) {
|
|
8098
8863
|
const normalized = {
|
|
8099
8864
|
chainId: callback.chainId,
|
|
8100
8865
|
contract: callback.contract.toLowerCase(),
|
|
8101
8866
|
user: callback.user.toLowerCase(),
|
|
8102
|
-
amount: callback.amount
|
|
8867
|
+
amount: callback.amount,
|
|
8868
|
+
positionTypeId: callback.positionTypeId
|
|
8103
8869
|
};
|
|
8104
8870
|
const id = callbackId(normalized);
|
|
8105
8871
|
offersCallbacksRows.push({
|
|
8106
8872
|
offerHash: normalizedOfferHash,
|
|
8873
|
+
obligationId: normalizedObligationId,
|
|
8107
8874
|
callbackId: id
|
|
8108
8875
|
});
|
|
8109
8876
|
if (seenCallbackIds.has(id)) continue;
|
|
@@ -8113,6 +8880,7 @@ function create$14(db) {
|
|
|
8113
8880
|
positionChainId: normalized.chainId,
|
|
8114
8881
|
positionContract: normalized.contract,
|
|
8115
8882
|
positionUser: normalized.user,
|
|
8883
|
+
positionTypeId: normalized.positionTypeId,
|
|
8116
8884
|
amount: normalized.amount.toString()
|
|
8117
8885
|
});
|
|
8118
8886
|
}
|
|
@@ -8225,6 +8993,17 @@ function create$11(db) {
|
|
|
8225
8993
|
const existing = lotsByKey.get(key);
|
|
8226
8994
|
if (!existing || offer.size > existing.size) lotsByKey.set(key, offer);
|
|
8227
8995
|
}
|
|
8996
|
+
const positionsByKey = /* @__PURE__ */ new Map();
|
|
8997
|
+
for (const offer of lotsByKey.values()) {
|
|
8998
|
+
const posKey = `${offer.positionChainId}-${offer.positionContract}-${offer.positionUser}`.toLowerCase();
|
|
8999
|
+
if (!positionsByKey.has(posKey)) positionsByKey.set(posKey, {
|
|
9000
|
+
chainId: offer.positionChainId,
|
|
9001
|
+
contract: offer.positionContract.toLowerCase(),
|
|
9002
|
+
user: offer.positionUser.toLowerCase(),
|
|
9003
|
+
positionTypeId: offer.positionTypeId
|
|
9004
|
+
});
|
|
9005
|
+
}
|
|
9006
|
+
for (const row of positionsByKey.values()) await db.insert(lotsPositions).values(row).onConflictDoNothing();
|
|
8228
9007
|
for (const offer of lotsByKey.values()) if ((await db.select().from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase()), eq(lots.group, offer.group.toLowerCase()), eq(lots.obligationId, offer.obligationId.toLowerCase()))).limit(1)).length === 0) {
|
|
8229
9008
|
const maxUpperResult = await db.select({ maxUpper: sql`COALESCE(MAX(${lots.upper}::numeric), 0)` }).from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase()), eq(lots.obligationId, offer.obligationId.toLowerCase())));
|
|
8230
9009
|
const newLower = BigInt(maxUpperResult[0]?.maxUpper ?? "0");
|
|
@@ -8256,52 +9035,66 @@ function create$10(db) {
|
|
|
8256
9035
|
const chainIds = parameters?.chainId;
|
|
8257
9036
|
const now$3 = now();
|
|
8258
9037
|
return (await db.select({
|
|
8259
|
-
|
|
9038
|
+
obligationId: obligationIdKeys.obligationId,
|
|
9039
|
+
chainId: obligationIdKeys.chainId,
|
|
9040
|
+
morphoV2: obligationIdKeys.morphoV2,
|
|
8260
9041
|
loanToken: obligations.loanToken,
|
|
8261
|
-
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${
|
|
9042
|
+
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${obligationCollateralsV2.oracleAddress}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
8262
9043
|
maturity: obligations.maturity
|
|
8263
|
-
}).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.
|
|
8264
|
-
|
|
9044
|
+
}).from(obligationIdKeys).innerJoin(obligations, eq(obligationIdKeys.obligationKey, obligations.obligationKey)).innerJoin(obligationCollateralsV2, eq(obligations.obligationKey, obligationCollateralsV2.obligationKey)).groupBy(obligationIdKeys.obligationId, obligationIdKeys.chainId, obligationIdKeys.morphoV2, obligations.loanToken, obligations.maturity).where(and(chainIds !== void 0 && chainIds.length > 0 ? inArray(obligationIdKeys.chainId, chainIds) : void 0, gte(obligations.maturity, now$3))).orderBy(asc(obligationIdKeys.obligationId))).map((row) => ({
|
|
9045
|
+
obligationId: row.obligationId,
|
|
8265
9046
|
chainId: row.chainId,
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
9047
|
+
morphoV2: row.morphoV2,
|
|
9048
|
+
obligation: from$13({
|
|
9049
|
+
loanToken: row.loanToken,
|
|
9050
|
+
collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$14({
|
|
9051
|
+
asset: collateral.asset,
|
|
9052
|
+
oracle: collateral.oracle,
|
|
9053
|
+
lltv: from$15(BigInt(collateral.lltv))
|
|
9054
|
+
})),
|
|
9055
|
+
maturity: row.maturity
|
|
9056
|
+
})
|
|
8273
9057
|
}));
|
|
8274
9058
|
},
|
|
8275
9059
|
create: async (obligations$1) => {
|
|
8276
9060
|
if (obligations$1.length === 0) return;
|
|
8277
|
-
const
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
9061
|
+
const obligationsByKey = /* @__PURE__ */ new Map();
|
|
9062
|
+
const obligationIdKeysById = /* @__PURE__ */ new Map();
|
|
9063
|
+
for (const input of obligations$1) {
|
|
9064
|
+
const obligationKey = key(input.obligation).toLowerCase();
|
|
9065
|
+
if (!obligationsByKey.has(obligationKey)) obligationsByKey.set(obligationKey, input.obligation);
|
|
9066
|
+
const obligationId = input.obligationId.toLowerCase();
|
|
9067
|
+
if (!obligationIdKeysById.has(obligationId)) obligationIdKeysById.set(obligationId, input);
|
|
8281
9068
|
}
|
|
8282
9069
|
try {
|
|
8283
9070
|
await db.transaction(async (dbTx) => {
|
|
8284
|
-
const obligationRows =
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
maturity: obligation.maturity
|
|
9071
|
+
const obligationRows = Array.from(obligationsByKey.entries()).map(([obligationKey, item]) => ({
|
|
9072
|
+
obligationKey,
|
|
9073
|
+
loanToken: item.loanToken.toLowerCase(),
|
|
9074
|
+
maturity: item.maturity
|
|
8289
9075
|
}));
|
|
8290
9076
|
for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
|
|
8291
|
-
const
|
|
8292
|
-
|
|
8293
|
-
|
|
9077
|
+
const obligationIdKeyRows = Array.from(obligationIdKeysById.entries()).map(([obligationId, input]) => ({
|
|
9078
|
+
obligationId,
|
|
9079
|
+
obligationKey: key(input.obligation).toLowerCase(),
|
|
9080
|
+
chainId: input.chainId,
|
|
9081
|
+
morphoV2: input.morphoV2.toLowerCase()
|
|
9082
|
+
}));
|
|
9083
|
+
for (const batch of batch$1(obligationIdKeyRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligationIdKeys).values(batch).onConflictDoNothing();
|
|
9084
|
+
const collateralRows = Array.from(obligationsByKey.entries()).flatMap(([obligationKey, item]) => {
|
|
9085
|
+
return [...item.collaterals].sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())).map((collateral, collateralIndex) => ({
|
|
9086
|
+
obligationKey,
|
|
8294
9087
|
asset: collateral.asset.toLowerCase(),
|
|
8295
|
-
oracleChainId: obligation.chainId,
|
|
8296
9088
|
oracleAddress: collateral.oracle.toLowerCase(),
|
|
8297
|
-
lltv: collateral.lltv
|
|
9089
|
+
lltv: collateral.lltv,
|
|
9090
|
+
collateralIndex
|
|
8298
9091
|
}));
|
|
8299
9092
|
});
|
|
8300
9093
|
for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
|
|
8301
9094
|
});
|
|
8302
9095
|
} catch (err) {
|
|
8303
9096
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
8304
|
-
throw new Error("Obligations.create failed. Ensure
|
|
9097
|
+
throw new Error("Obligations.create failed. Ensure obligation id keys are valid before inserting offers.", { cause: error });
|
|
8305
9098
|
}
|
|
8306
9099
|
}
|
|
8307
9100
|
};
|
|
@@ -8315,10 +9108,10 @@ function create$9(config) {
|
|
|
8315
9108
|
return {
|
|
8316
9109
|
create: async (batches) => {
|
|
8317
9110
|
if (batches.length === 0) return [];
|
|
8318
|
-
const offersRows = batches.flatMap(({ blockNumber, offers }) => offers.map((offer) => ({
|
|
9111
|
+
const offersRows = batches.flatMap(({ blockNumber, offers }) => offers.map(({ offer, obligationId, chainId }) => ({
|
|
8319
9112
|
...serialize(offer),
|
|
8320
|
-
obligationId: obligationId(
|
|
8321
|
-
groupChainId:
|
|
9113
|
+
obligationId: obligationId.toLowerCase(),
|
|
9114
|
+
groupChainId: chainId,
|
|
8322
9115
|
groupMaker: offer.maker.toLowerCase(),
|
|
8323
9116
|
callbackAddress: offer.callback.address.toLowerCase(),
|
|
8324
9117
|
callbackData: offer.callback.data,
|
|
@@ -8328,48 +9121,67 @@ function create$9(config) {
|
|
|
8328
9121
|
if (offersRows.length === 0) return [];
|
|
8329
9122
|
try {
|
|
8330
9123
|
return await db.transaction(async (dbTx) => {
|
|
8331
|
-
const
|
|
8332
|
-
|
|
9124
|
+
const keyOf = (input) => `${input.hash.toLowerCase()}:${input.obligationId.toLowerCase()}`;
|
|
9125
|
+
const selectExisting = async (offers$1) => {
|
|
9126
|
+
if (offers$1.length === 0) return /* @__PURE__ */ new Set();
|
|
9127
|
+
const expected = new Set(offers$1.map((offer) => keyOf({
|
|
9128
|
+
hash: offer.hash,
|
|
9129
|
+
obligationId: offer.obligationId
|
|
9130
|
+
})));
|
|
8333
9131
|
const existing = /* @__PURE__ */ new Set();
|
|
9132
|
+
const hashes = [...new Set(offers$1.map((offer) => offer.hash.toLowerCase()))];
|
|
8334
9133
|
for (const batch of batch$1(hashes, DEFAULT_BATCH_SIZE)) {
|
|
8335
|
-
const rows = await dbTx.select({
|
|
8336
|
-
|
|
9134
|
+
const rows = await dbTx.select({
|
|
9135
|
+
hash: offers.hash,
|
|
9136
|
+
obligationId: offers.obligationId
|
|
9137
|
+
}).from(offers).where(inArray(offers.hash, batch));
|
|
9138
|
+
for (const row of rows) {
|
|
9139
|
+
const key = keyOf({
|
|
9140
|
+
hash: String(row.hash),
|
|
9141
|
+
obligationId: String(row.obligationId)
|
|
9142
|
+
});
|
|
9143
|
+
if (expected.has(key)) existing.add(key);
|
|
9144
|
+
}
|
|
8337
9145
|
}
|
|
8338
9146
|
return existing;
|
|
8339
9147
|
};
|
|
8340
9148
|
const inserted = [];
|
|
8341
9149
|
for (const batch of batch$1(offersRows, DEFAULT_BATCH_SIZE)) {
|
|
8342
9150
|
const rows = await dbTx.insert(offers).values(batch).onConflictDoNothing().returning();
|
|
8343
|
-
inserted.push(...rows.map((row) =>
|
|
9151
|
+
inserted.push(...rows.map((row) => ({
|
|
9152
|
+
hash: row.hash,
|
|
9153
|
+
obligationId: row.obligationId
|
|
9154
|
+
})));
|
|
8344
9155
|
}
|
|
8345
9156
|
const existing = await selectExisting(inserted);
|
|
8346
|
-
return inserted.filter((
|
|
9157
|
+
return inserted.filter((offer) => existing.has(keyOf({
|
|
9158
|
+
hash: offer.hash,
|
|
9159
|
+
obligationId: offer.obligationId
|
|
9160
|
+
})));
|
|
8347
9161
|
});
|
|
8348
9162
|
} catch (err) {
|
|
8349
9163
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
8350
|
-
throw new Error("Offers.create failed. Ensure
|
|
9164
|
+
throw new Error("Offers.create failed. Ensure obligation id keys and groups exist before inserting offers.", { cause: error });
|
|
8351
9165
|
}
|
|
8352
9166
|
},
|
|
8353
9167
|
get: async (parameters) => {
|
|
8354
9168
|
const limit = parameters?.limit ?? DEFAULT_LIMIT$3;
|
|
8355
|
-
const
|
|
9169
|
+
const rawCursor = parameters?.cursor;
|
|
8356
9170
|
const maker = parameters?.maker;
|
|
8357
|
-
|
|
8358
|
-
if (!cursor.startsWith("0x") || cursor.length !== 66) throw new Error("Invalid cursor format");
|
|
8359
|
-
}
|
|
9171
|
+
const cursor = rawCursor !== null && rawCursor !== void 0 ? parseCursor$2(rawCursor) : void 0;
|
|
8360
9172
|
const collateralsLateral = db.select({ collaterals: sql`COALESCE(
|
|
8361
9173
|
jsonb_agg(
|
|
8362
9174
|
jsonb_build_object(
|
|
8363
9175
|
'asset', ${obligationCollateralsV2.asset},
|
|
8364
|
-
'oracle', ${
|
|
9176
|
+
'oracle', ${obligationCollateralsV2.oracleAddress},
|
|
8365
9177
|
'lltv', ${obligationCollateralsV2.lltv}
|
|
8366
9178
|
)
|
|
8367
9179
|
),
|
|
8368
9180
|
'[]'::jsonb
|
|
8369
|
-
)`.as("collaterals") }).from(obligationCollateralsV2).
|
|
8370
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).where(eq(obligationCollateralsV2.obligationId, offers.obligationId)).as("collaterals_lateral");
|
|
9181
|
+
)`.as("collaterals") }).from(obligationCollateralsV2).where(eq(obligationCollateralsV2.obligationKey, obligationIdKeys.obligationKey)).as("collaterals_lateral");
|
|
8371
9182
|
const rows = (await db.select({
|
|
8372
9183
|
hash: offers.hash,
|
|
9184
|
+
obligationId: offers.obligationId,
|
|
8373
9185
|
maker: offers.groupMaker,
|
|
8374
9186
|
assets: offers.assets,
|
|
8375
9187
|
obligationUnits: offers.obligationUnits,
|
|
@@ -8381,17 +9193,18 @@ function create$9(config) {
|
|
|
8381
9193
|
group: offers.group,
|
|
8382
9194
|
session: offers.session,
|
|
8383
9195
|
buy: offers.buy,
|
|
8384
|
-
chainId:
|
|
9196
|
+
chainId: obligationIdKeys.chainId,
|
|
8385
9197
|
loanToken: obligations.loanToken,
|
|
8386
9198
|
callbackAddress: offers.callbackAddress,
|
|
8387
9199
|
callbackData: offers.callbackData,
|
|
8388
9200
|
receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
|
|
8389
9201
|
collaterals: collateralsLateral.collaterals,
|
|
8390
9202
|
blockNumber: offers.blockNumber
|
|
8391
|
-
}).from(offers).innerJoin(
|
|
9203
|
+
}).from(offers).innerJoin(obligationIdKeys, eq(offers.obligationId, obligationIdKeys.obligationId)).innerJoin(obligations, eq(obligationIdKeys.obligationKey, obligations.obligationKey)).innerJoinLateral(collateralsLateral, sql`true`).where(and(cursor !== void 0 ? or(gt(offers.hash, cursor.hash), and(eq(offers.hash, cursor.hash), gt(offers.obligationId, cursor.obligationId))) : void 0, maker !== void 0 ? eq(offers.groupMaker, maker.toLowerCase()) : void 0)).orderBy(asc(offers.hash), asc(offers.obligationId)).limit(limit)).map((row) => {
|
|
8392
9204
|
const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
|
|
8393
9205
|
return {
|
|
8394
9206
|
hash: row.hash,
|
|
9207
|
+
obligationId: row.obligationId,
|
|
8395
9208
|
maker: row.maker,
|
|
8396
9209
|
assets: BigInt(row.assets),
|
|
8397
9210
|
obligationUnits: BigInt(row.obligationUnits),
|
|
@@ -8423,7 +9236,10 @@ function create$9(config) {
|
|
|
8423
9236
|
});
|
|
8424
9237
|
return {
|
|
8425
9238
|
rows,
|
|
8426
|
-
nextCursor: rows.length === limit ?
|
|
9239
|
+
nextCursor: rows.length === limit ? formatCursor$2({
|
|
9240
|
+
hash: rows[rows.length - 1].hash,
|
|
9241
|
+
obligationId: rows[rows.length - 1].obligationId
|
|
9242
|
+
}) : null
|
|
8427
9243
|
};
|
|
8428
9244
|
},
|
|
8429
9245
|
delete: async (parameters) => {
|
|
@@ -8446,7 +9262,7 @@ function create$9(config) {
|
|
|
8446
9262
|
const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
|
|
8447
9263
|
obligationId: offers.obligationId,
|
|
8448
9264
|
tick: offers.tick
|
|
8449
|
-
}).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(inArray(offers.obligationId, obligationIds), eq(offers.buy, side === "buy"), gte(offers.expiry, now$2), gte(offers.maturity, now$2), lte(offers.start, now$2), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(offers.obligationId, side === "buy" ? sql`${offers.tick} ASC` : sql`${offers.tick} DESC`);
|
|
9265
|
+
}).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, and(eq(offers.hash, validations.offerHash), eq(offers.obligationId, validations.obligationId))).leftJoin(status, eq(validations.statusId, status.id)).where(and(inArray(offers.obligationId, obligationIds), eq(offers.buy, side === "buy"), gte(offers.expiry, now$2), gte(offers.maturity, now$2), lte(offers.start, now$2), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(offers.obligationId, side === "buy" ? sql`${offers.tick} ASC` : sql`${offers.tick} DESC`);
|
|
8450
9266
|
const [bestBuys, bestSells] = await Promise.all([query({ side: "buy" }), query({ side: "sell" })]);
|
|
8451
9267
|
const quotes = /* @__PURE__ */ new Map();
|
|
8452
9268
|
for (const row of bestSells) quotes.set(row.obligationId, {
|
|
@@ -8476,6 +9292,18 @@ function create$9(config) {
|
|
|
8476
9292
|
}
|
|
8477
9293
|
};
|
|
8478
9294
|
}
|
|
9295
|
+
const HEX_32$2 = /^0x[a-fA-F0-9]{64}$/;
|
|
9296
|
+
function parseCursor$2(cursor) {
|
|
9297
|
+
const [hash, obligationId] = cursor.split(":");
|
|
9298
|
+
if (!hash || !obligationId || !HEX_32$2.test(hash) || !HEX_32$2.test(obligationId)) throw new Error("Invalid cursor format");
|
|
9299
|
+
return {
|
|
9300
|
+
hash: hash.toLowerCase(),
|
|
9301
|
+
obligationId: obligationId.toLowerCase()
|
|
9302
|
+
};
|
|
9303
|
+
}
|
|
9304
|
+
function formatCursor$2(input) {
|
|
9305
|
+
return `${input.hash.toLowerCase()}:${input.obligationId.toLowerCase()}`;
|
|
9306
|
+
}
|
|
8479
9307
|
|
|
8480
9308
|
//#endregion
|
|
8481
9309
|
//#region src/database/domains/Offsets.ts
|
|
@@ -8549,14 +9377,15 @@ const create$6 = (db) => {
|
|
|
8549
9377
|
upsert: async (positions$1) => {
|
|
8550
9378
|
const positionsMap = /* @__PURE__ */ new Map();
|
|
8551
9379
|
for (const p of positions$1) {
|
|
8552
|
-
const key = `${p.chainId}-${p.contract}-${p.user}`.toLowerCase();
|
|
8553
|
-
const zeroKey = `${p.chainId}-${p.contract}-${zeroAddress}`.toLowerCase();
|
|
9380
|
+
const key = `${p.chainId}-${p.contract}-${p.user}-${p.asset}`.toLowerCase();
|
|
9381
|
+
const zeroKey = `${p.chainId}-${p.contract}-${zeroAddress}-${p.asset}`.toLowerCase();
|
|
8554
9382
|
if (!positionsMap.has(zeroKey)) positionsMap.set(zeroKey, {
|
|
8555
9383
|
chainId: p.chainId,
|
|
8556
9384
|
contract: p.contract,
|
|
8557
9385
|
user: zeroAddress,
|
|
8558
9386
|
balance: 0n,
|
|
8559
9387
|
type: p.type,
|
|
9388
|
+
asset: p.asset,
|
|
8560
9389
|
blockNumber: p.blockNumber
|
|
8561
9390
|
});
|
|
8562
9391
|
if (!positionsMap.has(key)) {
|
|
@@ -8567,14 +9396,14 @@ const create$6 = (db) => {
|
|
|
8567
9396
|
}
|
|
8568
9397
|
if (positionsMap.size === 0) return 0;
|
|
8569
9398
|
const rows = Array.from(positionsMap.values()).map((p) => {
|
|
8570
|
-
const positionTypeId =
|
|
9399
|
+
const positionTypeId$1 = positionTypeId(p.type);
|
|
8571
9400
|
return {
|
|
8572
9401
|
chainId: p.chainId,
|
|
8573
9402
|
contract: p.contract.toLowerCase(),
|
|
8574
9403
|
user: p.user.toLowerCase(),
|
|
8575
|
-
positionTypeId,
|
|
9404
|
+
positionTypeId: positionTypeId$1,
|
|
8576
9405
|
...p.balance !== void 0 ? { balance: p.balance.toString() } : {},
|
|
8577
|
-
|
|
9406
|
+
asset: p.asset.toLowerCase(),
|
|
8578
9407
|
blockNumber: p.blockNumber
|
|
8579
9408
|
};
|
|
8580
9409
|
});
|
|
@@ -8584,11 +9413,12 @@ const create$6 = (db) => {
|
|
|
8584
9413
|
target: [
|
|
8585
9414
|
positions.chainId,
|
|
8586
9415
|
positions.contract,
|
|
8587
|
-
positions.user
|
|
9416
|
+
positions.user,
|
|
9417
|
+
positions.positionTypeId,
|
|
9418
|
+
positions.asset
|
|
8588
9419
|
],
|
|
8589
9420
|
set: {
|
|
8590
9421
|
balance: sql`EXCLUDED.balance`,
|
|
8591
|
-
asset: sql`COALESCE(EXCLUDED.asset, ${positions.asset})`,
|
|
8592
9422
|
blockNumber: sql`EXCLUDED.block_number`,
|
|
8593
9423
|
updatedAt: sql`NOW()`
|
|
8594
9424
|
},
|
|
@@ -8607,20 +9437,23 @@ const create$6 = (db) => {
|
|
|
8607
9437
|
cursor = {
|
|
8608
9438
|
chainId: parsed.chainId,
|
|
8609
9439
|
contract: parsed.contract,
|
|
8610
|
-
user: parsed.user
|
|
9440
|
+
user: parsed.user,
|
|
9441
|
+
positionTypeId: parsed.positionTypeId ?? 0,
|
|
9442
|
+
asset: parsed.asset ?? ""
|
|
8611
9443
|
};
|
|
8612
9444
|
}
|
|
8613
|
-
const positions$2 = await db.select().from(positions).where(and(ne(positions.user, zeroAddress), filled === void 0 ? sql`true` : sql`${positions.balance} IS ${filled === true ? sql`NOT` : sql``} NULL`, type !== void 0 ? eq(positions.positionTypeId,
|
|
9445
|
+
const positions$2 = await db.select().from(positions).where(and(ne(positions.user, zeroAddress), filled === void 0 ? sql`true` : sql`${positions.balance} IS ${filled === true ? sql`NOT` : sql``} NULL`, type !== void 0 ? eq(positions.positionTypeId, positionTypeId(type)) : sql`true`, chainId !== void 0 ? eq(positions.chainId, chainId) : sql`true`, (() => {
|
|
8614
9446
|
if (cursor === null || cursor === void 0) return sql`true`;
|
|
8615
9447
|
return sql`
|
|
8616
|
-
(${chainId === void 0 ? sql`"chain_id", ` : sql``}"contract", "user") > (${chainId === void 0 ? sql`${cursor.chainId}, ` : sql``}${cursor.contract}, ${cursor.user})
|
|
9448
|
+
(${chainId === void 0 ? sql`"chain_id", ` : sql``}"contract", "user", "position_type_id", "asset") > (${chainId === void 0 ? sql`${cursor.chainId}, ` : sql``}${cursor.contract}, ${cursor.user}, ${cursor.positionTypeId}, ${cursor.asset})
|
|
8617
9449
|
`;
|
|
8618
|
-
})())).orderBy(asc(positions.chainId), asc(positions.contract), asc(positions.user), asc(positions.
|
|
9450
|
+
})())).orderBy(asc(positions.chainId), asc(positions.contract), asc(positions.user), asc(positions.positionTypeId), asc(positions.asset)).limit(limit);
|
|
8619
9451
|
const nextCursor = positions$2.length === limit ? Buffer.from(JSON.stringify({
|
|
8620
9452
|
chainId: positions$2[positions$2.length - 1].chainId.toString(),
|
|
8621
9453
|
contract: positions$2[positions$2.length - 1].contract,
|
|
8622
9454
|
user: positions$2[positions$2.length - 1].user,
|
|
8623
|
-
|
|
9455
|
+
positionTypeId: positions$2[positions$2.length - 1].positionTypeId,
|
|
9456
|
+
asset: positions$2[positions$2.length - 1].asset
|
|
8624
9457
|
})).toString("base64url") : null;
|
|
8625
9458
|
return {
|
|
8626
9459
|
positions: positions$2.map((p) => ({
|
|
@@ -8629,14 +9462,14 @@ const create$6 = (db) => {
|
|
|
8629
9462
|
user: p.user,
|
|
8630
9463
|
type: Object.values(Type)[p.positionTypeId - 1],
|
|
8631
9464
|
balance: p.balance !== null ? BigInt(p.balance) : void 0,
|
|
8632
|
-
|
|
9465
|
+
asset: p.asset,
|
|
8633
9466
|
blockNumber: p.blockNumber
|
|
8634
9467
|
})),
|
|
8635
9468
|
nextCursor
|
|
8636
9469
|
};
|
|
8637
9470
|
},
|
|
8638
9471
|
getByUser: async (parameters) => {
|
|
8639
|
-
const { user, limit = DEFAULT_LIMIT$2, cursor: encodedCursor } = parameters;
|
|
9472
|
+
const { user, type, limit = DEFAULT_LIMIT$2, cursor: encodedCursor } = parameters;
|
|
8640
9473
|
let cursor = null;
|
|
8641
9474
|
if (encodedCursor !== null && encodedCursor !== void 0) {
|
|
8642
9475
|
const parsed = JSON.parse(Buffer.from(encodedCursor, "base64url").toString("utf8"));
|
|
@@ -8644,6 +9477,8 @@ const create$6 = (db) => {
|
|
|
8644
9477
|
cursor = {
|
|
8645
9478
|
chainId: parsed.chainId,
|
|
8646
9479
|
contract: parsed.contract,
|
|
9480
|
+
positionTypeId: parsed.positionTypeId ?? 0,
|
|
9481
|
+
asset: parsed.asset ?? "",
|
|
8647
9482
|
obligationId: parsed.obligationId ?? null
|
|
8648
9483
|
};
|
|
8649
9484
|
}
|
|
@@ -8731,6 +9566,8 @@ const create$6 = (db) => {
|
|
|
8731
9566
|
p.contract,
|
|
8732
9567
|
p."user",
|
|
8733
9568
|
p.block_number,
|
|
9569
|
+
p.position_type_id,
|
|
9570
|
+
p.asset,
|
|
8734
9571
|
po.obligation_id,
|
|
8735
9572
|
COALESCE(po.reserved_balance, '0') AS reserved_balance
|
|
8736
9573
|
FROM ${positions} p
|
|
@@ -8740,14 +9577,18 @@ const create$6 = (db) => {
|
|
|
8740
9577
|
AND LOWER(po."user") = LOWER(p."user")
|
|
8741
9578
|
WHERE LOWER(p."user") = LOWER(${user})
|
|
8742
9579
|
AND p."user" != ${zeroAddress}
|
|
8743
|
-
${
|
|
8744
|
-
|
|
9580
|
+
${type !== void 0 ? sql`AND p.position_type_id = ${positionTypeId(type)}` : sql``}
|
|
9581
|
+
${cursor !== null ? sql`AND (p.chain_id, p.contract, p.position_type_id, p.asset, COALESCE(po.obligation_id, '')) > (${cursor.chainId}, ${cursor.contract}, ${cursor.positionTypeId}, ${cursor.asset}, ${cursor.obligationId ?? ""})` : sql``}
|
|
9582
|
+
ORDER BY p.chain_id ASC, p.contract ASC, p.position_type_id ASC, p.asset ASC, po.obligation_id ASC NULLS FIRST
|
|
8745
9583
|
LIMIT ${limit}
|
|
8746
9584
|
`);
|
|
8747
|
-
const
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
9585
|
+
const lastRow = raw.rows[raw.rows.length - 1];
|
|
9586
|
+
const nextCursor = raw.rows.length === limit && lastRow !== void 0 ? Buffer.from(JSON.stringify({
|
|
9587
|
+
chainId: lastRow.chain_id.toString(),
|
|
9588
|
+
contract: lastRow.contract,
|
|
9589
|
+
positionTypeId: lastRow.position_type_id,
|
|
9590
|
+
asset: lastRow.asset,
|
|
9591
|
+
obligationId: lastRow.obligation_id
|
|
8751
9592
|
})).toString("base64url") : null;
|
|
8752
9593
|
return {
|
|
8753
9594
|
positions: raw.rows.map((row) => ({
|
|
@@ -8762,21 +9603,23 @@ const create$6 = (db) => {
|
|
|
8762
9603
|
};
|
|
8763
9604
|
},
|
|
8764
9605
|
setEmptyAfter: async (parameters) => {
|
|
8765
|
-
const { chainId, blockNumber } = parameters;
|
|
9606
|
+
const { chainId, blockNumber, type } = parameters;
|
|
9607
|
+
const typeId = positionTypeId(type);
|
|
8766
9608
|
return await db.transaction(async (tx) => {
|
|
8767
9609
|
const updatedPositions = await tx.update(positions).set({
|
|
8768
9610
|
balance: null,
|
|
8769
9611
|
blockNumber,
|
|
8770
9612
|
updatedAt: sql`NOW()`
|
|
8771
|
-
}).where(and(eq(positions.chainId, chainId), sql`${positions.blockNumber} >= ${blockNumber}
|
|
9613
|
+
}).where(and(eq(positions.chainId, chainId), sql`${positions.blockNumber} >= ${blockNumber}`, eq(positions.positionTypeId, typeId))).returning();
|
|
8772
9614
|
if (updatedPositions.length === 0) return 0;
|
|
8773
9615
|
await tx.execute(sql`
|
|
8774
9616
|
DELETE FROM ${transfers} t
|
|
8775
|
-
USING (VALUES ${sql.join(updatedPositions.map((u) => sql`(${u.chainId}::bigint, ${u.contract}::varchar(
|
|
9617
|
+
USING (VALUES ${sql.join(updatedPositions.map((u) => sql`(${u.chainId}::bigint, ${u.contract}::varchar(66), ${u.user}::varchar(42))`), sql`,`)}) AS s(chain_id, "contract", "user")
|
|
8776
9618
|
WHERE
|
|
8777
9619
|
t.chain_id = s.chain_id
|
|
8778
9620
|
AND t."contract" = s."contract"
|
|
8779
9621
|
AND (t."from" = s."user" OR t."to" = s."user")
|
|
9622
|
+
AND t.position_type_id = ${typeId}
|
|
8780
9623
|
`);
|
|
8781
9624
|
return updatedPositions.length;
|
|
8782
9625
|
});
|
|
@@ -8786,33 +9629,35 @@ const create$6 = (db) => {
|
|
|
8786
9629
|
|
|
8787
9630
|
//#endregion
|
|
8788
9631
|
//#region src/database/domains/Transfers.ts
|
|
8789
|
-
const create$5 = (db) => ({
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
9632
|
+
const create$5 = (db) => ({
|
|
9633
|
+
create: async (transfers$1) => {
|
|
9634
|
+
if (transfers$1.length === 0) return 0;
|
|
9635
|
+
const typeId = positionTypeId(transfers$1[0].type);
|
|
9636
|
+
return await db.transaction(async (dbTx) => {
|
|
9637
|
+
let totalInserted = 0;
|
|
9638
|
+
for (const transfersBatch of batch$1(transfers$1, DEFAULT_BATCH_SIZE)) {
|
|
9639
|
+
const { rows: inserted } = await dbTx.execute(sql`
|
|
9640
|
+
INSERT INTO ${transfers} (event_id, chain_id, "contract", "from", "to", "value", position_type_id, asset, block_number)
|
|
9641
|
+
SELECT * FROM (VALUES ${sql.join(transfersBatch.map((transfer) => sql`(${transfer.id}::varchar(128), ${transfer.chainId}::bigint, ${transfer.contract.toLowerCase()}::varchar(66), ${transfer.from.toLowerCase()}::varchar(42), ${transfer.to.toLowerCase()}::varchar(42), ${transfer.value.toString()}::numeric(78, 0), ${typeId}::integer, ${transfer.asset.toLowerCase()}::varchar(42), ${transfer.blockNumber}::bigint)`), sql`,`)}) AS v(event_id, chain_id, "contract", "from", "to", "value", position_type_id, asset, block_number)
|
|
8797
9642
|
WHERE
|
|
8798
9643
|
EXISTS (
|
|
8799
9644
|
SELECT 1 FROM ${positions} p
|
|
8800
|
-
WHERE p.chain_id = v.chain_id AND p."contract" = v."contract" AND p."user" = v."from" AND p.balance IS NOT NULL
|
|
9645
|
+
WHERE p.chain_id = v.chain_id AND p."contract" = v."contract" AND p."user" = v."from" AND p.position_type_id = v.position_type_id AND p.asset = v.asset AND p.balance IS NOT NULL
|
|
8801
9646
|
)
|
|
8802
9647
|
AND
|
|
8803
9648
|
EXISTS (
|
|
8804
9649
|
SELECT 1 FROM ${positions} p
|
|
8805
|
-
WHERE p.chain_id = v.chain_id AND p."contract" = v."contract" AND p."user" = v."to" AND p.balance IS NOT NULL
|
|
9650
|
+
WHERE p.chain_id = v.chain_id AND p."contract" = v."contract" AND p."user" = v."to" AND p.position_type_id = v.position_type_id AND p.asset = v.asset AND p.balance IS NOT NULL
|
|
8806
9651
|
)
|
|
8807
9652
|
ON CONFLICT DO NOTHING
|
|
8808
9653
|
RETURNING *;
|
|
8809
9654
|
`);
|
|
8810
|
-
|
|
8811
|
-
|
|
9655
|
+
if (inserted.length === 0) continue;
|
|
9656
|
+
await dbTx.execute(sql`
|
|
8812
9657
|
WITH inserted AS (
|
|
8813
9658
|
VALUES ${sql.join(inserted.map((t) => {
|
|
8814
|
-
|
|
8815
|
-
|
|
9659
|
+
return sql`(${t.chain_id}::bigint, ${t.contract}::varchar(66), ${t.from}::varchar(42), ${t.to}::varchar(42), ${t.value}::numeric(78, 0), ${t.asset}::varchar(42), ${t.block_number}::bigint)`;
|
|
9660
|
+
}), sql`,`)}
|
|
8816
9661
|
),
|
|
8817
9662
|
new_transfers AS (
|
|
8818
9663
|
SELECT
|
|
@@ -8821,7 +9666,8 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8821
9666
|
column3 AS "from",
|
|
8822
9667
|
column4 AS "to",
|
|
8823
9668
|
column5 AS "value",
|
|
8824
|
-
column6 AS
|
|
9669
|
+
column6 AS asset,
|
|
9670
|
+
column7 AS block_number
|
|
8825
9671
|
FROM inserted
|
|
8826
9672
|
),
|
|
8827
9673
|
diffs AS (
|
|
@@ -8830,6 +9676,7 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8830
9676
|
nt."contract",
|
|
8831
9677
|
nt."from" AS "user",
|
|
8832
9678
|
-nt."value" AS delta,
|
|
9679
|
+
nt.asset,
|
|
8833
9680
|
nt.block_number
|
|
8834
9681
|
FROM new_transfers nt
|
|
8835
9682
|
UNION ALL
|
|
@@ -8838,6 +9685,7 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8838
9685
|
nt."contract",
|
|
8839
9686
|
nt."to" AS "user",
|
|
8840
9687
|
nt."value" AS delta,
|
|
9688
|
+
nt.asset,
|
|
8841
9689
|
nt.block_number
|
|
8842
9690
|
FROM new_transfers nt
|
|
8843
9691
|
),
|
|
@@ -8847,12 +9695,15 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8847
9695
|
d."contract",
|
|
8848
9696
|
d."user",
|
|
8849
9697
|
d.delta,
|
|
9698
|
+
d.asset,
|
|
8850
9699
|
d.block_number
|
|
8851
9700
|
FROM diffs d
|
|
8852
9701
|
JOIN ${positions} p
|
|
8853
9702
|
ON p.chain_id = d.chain_id
|
|
8854
9703
|
AND p."contract" = d."contract"
|
|
8855
9704
|
AND p."user" = d."user"
|
|
9705
|
+
AND p.position_type_id = ${typeId}
|
|
9706
|
+
AND p.asset = d.asset
|
|
8856
9707
|
-- Only keep per-event diffs that are at or after the current position block
|
|
8857
9708
|
WHERE d.block_number >= p.block_number
|
|
8858
9709
|
),
|
|
@@ -8861,10 +9712,11 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8861
9712
|
chain_id,
|
|
8862
9713
|
"contract",
|
|
8863
9714
|
"user",
|
|
9715
|
+
asset,
|
|
8864
9716
|
SUM(delta) AS sum_delta,
|
|
8865
9717
|
MAX(block_number) AS max_block
|
|
8866
9718
|
FROM valid_diffs
|
|
8867
|
-
GROUP BY 1,2,3
|
|
9719
|
+
GROUP BY 1,2,3,4
|
|
8868
9720
|
)
|
|
8869
9721
|
UPDATE ${positions} AS p
|
|
8870
9722
|
SET
|
|
@@ -8876,12 +9728,109 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8876
9728
|
p.chain_id = a.chain_id
|
|
8877
9729
|
AND p."contract" = a."contract"
|
|
8878
9730
|
AND p."user" = a."user"
|
|
9731
|
+
AND p.asset = a.asset
|
|
9732
|
+
AND p.position_type_id = ${typeId}
|
|
8879
9733
|
`);
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
}
|
|
9734
|
+
totalInserted += inserted.length;
|
|
9735
|
+
}
|
|
9736
|
+
return totalInserted;
|
|
9737
|
+
});
|
|
9738
|
+
},
|
|
9739
|
+
delete: async (parameters) => {
|
|
9740
|
+
const { chainId, blockNumberGte, positionTypeId } = parameters;
|
|
9741
|
+
return await db.transaction(async (dbTx) => {
|
|
9742
|
+
const { rows: toDelete } = await dbTx.execute(sql`
|
|
9743
|
+
SELECT event_id, chain_id, "contract", "from", "to", "value"::text, asset, block_number
|
|
9744
|
+
FROM ${transfers}
|
|
9745
|
+
WHERE chain_id = ${chainId}
|
|
9746
|
+
AND position_type_id = ${positionTypeId}
|
|
9747
|
+
AND block_number >= ${blockNumberGte}
|
|
9748
|
+
`);
|
|
9749
|
+
if (toDelete.length === 0) return 0;
|
|
9750
|
+
await dbTx.execute(sql`
|
|
9751
|
+
WITH to_delete AS (
|
|
9752
|
+
VALUES ${sql.join(toDelete.map((t) => sql`(${t.chain_id}::bigint, ${t.contract}::varchar(66), ${t.from}::varchar(42), ${t.to}::varchar(42), ${t.value}::numeric(78, 0), ${t.asset}::varchar(42))`), sql`,`)}
|
|
9753
|
+
),
|
|
9754
|
+
reverse_diffs AS (
|
|
9755
|
+
SELECT column1 AS chain_id, column2 AS "contract", column3 AS "user", column5 AS delta, column6 AS asset
|
|
9756
|
+
FROM to_delete
|
|
9757
|
+
UNION ALL
|
|
9758
|
+
SELECT column1 AS chain_id, column2 AS "contract", column4 AS "user", -column5 AS delta, column6 AS asset
|
|
9759
|
+
FROM to_delete
|
|
9760
|
+
),
|
|
9761
|
+
aggregated AS (
|
|
9762
|
+
SELECT chain_id, "contract", "user", asset, SUM(delta) AS sum_delta
|
|
9763
|
+
FROM reverse_diffs
|
|
9764
|
+
GROUP BY 1,2,3,4
|
|
9765
|
+
),
|
|
9766
|
+
remaining_max AS (
|
|
9767
|
+
SELECT t.chain_id, t."contract",
|
|
9768
|
+
CASE WHEN t."from" = a."user" THEN t."from" ELSE t."to" END AS "user",
|
|
9769
|
+
t.asset,
|
|
9770
|
+
MAX(t.block_number) AS max_block
|
|
9771
|
+
FROM ${transfers} t
|
|
9772
|
+
JOIN aggregated a
|
|
9773
|
+
ON t.chain_id = a.chain_id AND t."contract" = a."contract"
|
|
9774
|
+
AND t.asset = a.asset
|
|
9775
|
+
AND (t."from" = a."user" OR t."to" = a."user")
|
|
9776
|
+
WHERE t.position_type_id = ${positionTypeId}
|
|
9777
|
+
AND t.block_number < ${blockNumberGte}
|
|
9778
|
+
GROUP BY t.chain_id, t."contract",
|
|
9779
|
+
CASE WHEN t."from" = a."user" THEN t."from" ELSE t."to" END,
|
|
9780
|
+
t.asset
|
|
9781
|
+
)
|
|
9782
|
+
UPDATE ${positions} AS p
|
|
9783
|
+
SET
|
|
9784
|
+
balance = COALESCE(p.balance, 0) + a.sum_delta,
|
|
9785
|
+
block_number = COALESCE(rm.max_block, p.block_number),
|
|
9786
|
+
updated_at = NOW()
|
|
9787
|
+
FROM aggregated a
|
|
9788
|
+
LEFT JOIN remaining_max rm
|
|
9789
|
+
ON rm.chain_id = a.chain_id AND rm."contract" = a."contract" AND rm."user" = a."user" AND rm.asset = a.asset
|
|
9790
|
+
WHERE p.chain_id = a.chain_id
|
|
9791
|
+
AND p."contract" = a."contract"
|
|
9792
|
+
AND p."user" = a."user"
|
|
9793
|
+
AND p.asset = a.asset
|
|
9794
|
+
AND p.position_type_id = ${positionTypeId}
|
|
9795
|
+
`);
|
|
9796
|
+
await dbTx.execute(sql`
|
|
9797
|
+
DELETE FROM ${transfers}
|
|
9798
|
+
WHERE chain_id = ${chainId}
|
|
9799
|
+
AND position_type_id = ${positionTypeId}
|
|
9800
|
+
AND block_number >= ${blockNumberGte}
|
|
9801
|
+
`);
|
|
9802
|
+
await dbTx.execute(sql`
|
|
9803
|
+
DELETE FROM ${positions} p
|
|
9804
|
+
USING (
|
|
9805
|
+
SELECT DISTINCT chain_id, "contract", "user", asset
|
|
9806
|
+
FROM (
|
|
9807
|
+
SELECT chain_id, "contract", "from" AS "user", asset FROM (
|
|
9808
|
+
VALUES ${sql.join(toDelete.map((t) => sql`(${t.chain_id}::bigint, ${t.contract}::varchar(66), ${t.from}::varchar(42), ${t.asset}::varchar(42))`), sql`,`)}
|
|
9809
|
+
) AS d(chain_id, "contract", "from", asset)
|
|
9810
|
+
UNION
|
|
9811
|
+
SELECT chain_id, "contract", "to" AS "user", asset FROM (
|
|
9812
|
+
VALUES ${sql.join(toDelete.map((t) => sql`(${t.chain_id}::bigint, ${t.contract}::varchar(66), ${t.to}::varchar(42), ${t.asset}::varchar(42))`), sql`,`)}
|
|
9813
|
+
) AS d(chain_id, "contract", "to", asset)
|
|
9814
|
+
) AS combined
|
|
9815
|
+
) AS affected
|
|
9816
|
+
WHERE p.chain_id = affected.chain_id
|
|
9817
|
+
AND p."contract" = affected."contract"
|
|
9818
|
+
AND p."user" = affected."user"
|
|
9819
|
+
AND p.asset = affected.asset
|
|
9820
|
+
AND p.position_type_id = ${positionTypeId}
|
|
9821
|
+
AND NOT EXISTS (
|
|
9822
|
+
SELECT 1 FROM ${transfers} t
|
|
9823
|
+
WHERE t.chain_id = p.chain_id
|
|
9824
|
+
AND t."contract" = p."contract"
|
|
9825
|
+
AND t.asset = p.asset
|
|
9826
|
+
AND t.position_type_id = ${positionTypeId}
|
|
9827
|
+
AND (t."from" = p."user" OR t."to" = p."user")
|
|
9828
|
+
)
|
|
9829
|
+
`);
|
|
9830
|
+
return toDelete.length;
|
|
9831
|
+
});
|
|
9832
|
+
}
|
|
9833
|
+
});
|
|
8885
9834
|
|
|
8886
9835
|
//#endregion
|
|
8887
9836
|
//#region src/database/domains/Trees.ts
|
|
@@ -8894,34 +9843,22 @@ const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
|
8894
9843
|
function create$4(config) {
|
|
8895
9844
|
const db = config.db;
|
|
8896
9845
|
return {
|
|
8897
|
-
|
|
9846
|
+
upsert: async (trees$1) => {
|
|
8898
9847
|
if (trees$1.length === 0) return [];
|
|
8899
9848
|
try {
|
|
8900
9849
|
return await db.transaction(async (dbTx) => {
|
|
8901
9850
|
const roots = [];
|
|
8902
|
-
for (const {
|
|
8903
|
-
const
|
|
8904
|
-
|
|
9851
|
+
for (const { root, signature } of trees$1) {
|
|
9852
|
+
const normalizedRoot = root.toLowerCase();
|
|
9853
|
+
const normalizedSignature = signature.toLowerCase();
|
|
9854
|
+
roots.push(normalizedRoot);
|
|
8905
9855
|
await dbTx.insert(trees).values({
|
|
8906
|
-
root,
|
|
8907
|
-
rootSignature:
|
|
9856
|
+
root: normalizedRoot,
|
|
9857
|
+
rootSignature: normalizedSignature
|
|
8908
9858
|
}).onConflictDoUpdate({
|
|
8909
9859
|
target: [trees.root],
|
|
8910
9860
|
set: {
|
|
8911
|
-
rootSignature:
|
|
8912
|
-
createdAt: sql`NOW()`
|
|
8913
|
-
}
|
|
8914
|
-
});
|
|
8915
|
-
const pathRows = proofs(tree).map((proof) => ({
|
|
8916
|
-
offerHash: hash(proof.offer).toLowerCase(),
|
|
8917
|
-
treeRoot: root,
|
|
8918
|
-
proofNodes: concatenateProofs(proof.path)
|
|
8919
|
-
}));
|
|
8920
|
-
for (const batch of batch$1(pathRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(merklePaths).values(batch).onConflictDoUpdate({
|
|
8921
|
-
target: [merklePaths.offerHash],
|
|
8922
|
-
set: {
|
|
8923
|
-
treeRoot: sql`excluded.tree_root`,
|
|
8924
|
-
proofNodes: sql`excluded.proof_nodes`,
|
|
9861
|
+
rootSignature: normalizedSignature,
|
|
8925
9862
|
createdAt: sql`NOW()`
|
|
8926
9863
|
}
|
|
8927
9864
|
});
|
|
@@ -8930,24 +9867,60 @@ function create$4(config) {
|
|
|
8930
9867
|
});
|
|
8931
9868
|
} catch (err) {
|
|
8932
9869
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
8933
|
-
throw new Error("Trees.
|
|
9870
|
+
throw new Error("Trees.upsert failed. Ensure obligations and offers exist before upserting roots.", { cause: error });
|
|
9871
|
+
}
|
|
9872
|
+
},
|
|
9873
|
+
upsertPaths: async (paths) => {
|
|
9874
|
+
if (paths.length === 0) return;
|
|
9875
|
+
const rows = paths.map((path) => ({
|
|
9876
|
+
offerHash: path.offerHash.toLowerCase(),
|
|
9877
|
+
obligationId: path.obligationId.toLowerCase(),
|
|
9878
|
+
treeRoot: path.treeRoot.toLowerCase(),
|
|
9879
|
+
proofNodes: concatenateProofs(path.proof)
|
|
9880
|
+
}));
|
|
9881
|
+
try {
|
|
9882
|
+
for (const batch of batch$1(rows, DEFAULT_BATCH_SIZE)) await db.insert(merklePaths).values(batch).onConflictDoUpdate({
|
|
9883
|
+
target: [merklePaths.offerHash, merklePaths.obligationId],
|
|
9884
|
+
set: {
|
|
9885
|
+
treeRoot: sql`excluded.tree_root`,
|
|
9886
|
+
proofNodes: sql`excluded.proof_nodes`,
|
|
9887
|
+
createdAt: sql`NOW()`
|
|
9888
|
+
}
|
|
9889
|
+
});
|
|
9890
|
+
} catch (err) {
|
|
9891
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
9892
|
+
throw new Error("Trees.upsertPaths failed. Ensure offers and roots exist before inserting merkle paths.", { cause: error });
|
|
8934
9893
|
}
|
|
8935
9894
|
},
|
|
8936
|
-
getAttestations: async (
|
|
8937
|
-
if (
|
|
8938
|
-
const
|
|
9895
|
+
getAttestations: async (references) => {
|
|
9896
|
+
if (references.length === 0) return /* @__PURE__ */ new Map();
|
|
9897
|
+
const normalizedReferences = references.map((reference) => ({
|
|
9898
|
+
offerHash: reference.offerHash.toLowerCase(),
|
|
9899
|
+
obligationId: reference.obligationId.toLowerCase()
|
|
9900
|
+
}));
|
|
9901
|
+
const hashes = [...new Set(normalizedReferences.map((reference) => reference.offerHash))];
|
|
9902
|
+
const obligationIds = [...new Set(normalizedReferences.map((reference) => reference.obligationId))];
|
|
8939
9903
|
const results = await db.select({
|
|
8940
9904
|
offerHash: merklePaths.offerHash,
|
|
9905
|
+
obligationId: merklePaths.obligationId,
|
|
8941
9906
|
treeRoot: merklePaths.treeRoot,
|
|
8942
9907
|
proofNodes: merklePaths.proofNodes,
|
|
8943
9908
|
rootSignature: trees.rootSignature
|
|
8944
|
-
}).from(merklePaths).innerJoin(trees, eq(merklePaths.treeRoot, trees.root)).where(inArray(merklePaths.offerHash,
|
|
9909
|
+
}).from(merklePaths).innerJoin(trees, eq(merklePaths.treeRoot, trees.root)).where(sql`${inArray(merklePaths.offerHash, hashes)} AND ${inArray(merklePaths.obligationId, obligationIds)}`);
|
|
9910
|
+
const expectedKeys = new Set(normalizedReferences.map(toAttestationKey));
|
|
8945
9911
|
const attestationMap = /* @__PURE__ */ new Map();
|
|
8946
|
-
for (const row of results)
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
9912
|
+
for (const row of results) {
|
|
9913
|
+
const key = toAttestationKey({
|
|
9914
|
+
offerHash: row.offerHash,
|
|
9915
|
+
obligationId: row.obligationId
|
|
9916
|
+
});
|
|
9917
|
+
if (!expectedKeys.has(key)) continue;
|
|
9918
|
+
attestationMap.set(key, {
|
|
9919
|
+
root: row.treeRoot.toLowerCase(),
|
|
9920
|
+
signature: row.rootSignature.toLowerCase(),
|
|
9921
|
+
proof: splitProofs(row.proofNodes)
|
|
9922
|
+
});
|
|
9923
|
+
}
|
|
8951
9924
|
return attestationMap;
|
|
8952
9925
|
}
|
|
8953
9926
|
};
|
|
@@ -8958,7 +9931,7 @@ function create$4(config) {
|
|
|
8958
9931
|
*/
|
|
8959
9932
|
function concatenateProofs(proofs) {
|
|
8960
9933
|
if (proofs.length === 0) return "0x";
|
|
8961
|
-
return `0x${proofs.map((
|
|
9934
|
+
return `0x${proofs.map((proof) => proof.toLowerCase().slice(2)).join("")}`;
|
|
8962
9935
|
}
|
|
8963
9936
|
/**
|
|
8964
9937
|
* Splits a concatenated hex string back into an array of 32-byte hex hashes.
|
|
@@ -8968,9 +9941,12 @@ function splitProofs(concatenated) {
|
|
|
8968
9941
|
if (!concatenated || concatenated === "0x" || concatenated.length <= 2) return [];
|
|
8969
9942
|
const hex = concatenated.slice(2);
|
|
8970
9943
|
const proofs = [];
|
|
8971
|
-
for (let i = 0; i < hex.length; i += 64) proofs.push(`0x${hex.slice(i, i + 64)}`);
|
|
9944
|
+
for (let i = 0; i < hex.length; i += 64) proofs.push(`0x${hex.slice(i, i + 64).toLowerCase()}`);
|
|
8972
9945
|
return proofs;
|
|
8973
9946
|
}
|
|
9947
|
+
function toAttestationKey(offer) {
|
|
9948
|
+
return `${offer.offerHash.toLowerCase()}:${offer.obligationId.toLowerCase()}`;
|
|
9949
|
+
}
|
|
8974
9950
|
|
|
8975
9951
|
//#endregion
|
|
8976
9952
|
//#region src/database/domains/Validations.ts
|
|
@@ -8979,26 +9955,26 @@ function create$3(db) {
|
|
|
8979
9955
|
return {
|
|
8980
9956
|
get: async (params) => {
|
|
8981
9957
|
const { status: status$2, cursor, limit = DEFAULT_LIMIT$1 } = params ?? {};
|
|
8982
|
-
|
|
8983
|
-
if (!cursor.startsWith("0x") || cursor.length !== 66) throw new Error("Invalid cursor format");
|
|
8984
|
-
}
|
|
9958
|
+
const parsedCursor = cursor !== null && cursor !== void 0 ? parseCursor$1(cursor) : void 0;
|
|
8985
9959
|
let query = db.select({
|
|
8986
9960
|
offerHash: validations.offerHash,
|
|
9961
|
+
obligationId: validations.obligationId,
|
|
8987
9962
|
code: status.code
|
|
8988
9963
|
}).from(validations).innerJoin(status, eq(validations.statusId, status.id));
|
|
8989
9964
|
if (status$2 !== void 0) query = query.where(eq(status.code, status$2));
|
|
8990
|
-
if (
|
|
8991
|
-
const cursorCondition = gt(validations.offerHash,
|
|
9965
|
+
if (parsedCursor !== void 0) {
|
|
9966
|
+
const cursorCondition = or(gt(validations.offerHash, parsedCursor.offerHash), and(eq(validations.offerHash, parsedCursor.offerHash), gt(validations.obligationId, parsedCursor.obligationId)));
|
|
8992
9967
|
if (status$2 !== void 0) query = query.where(and(eq(status.code, status$2), cursorCondition));
|
|
8993
9968
|
else query = query.where(cursorCondition);
|
|
8994
9969
|
}
|
|
8995
|
-
const mapped = (await query.orderBy(asc(validations.offerHash)).limit(limit)).map((row) => ({
|
|
9970
|
+
const mapped = (await query.orderBy(asc(validations.offerHash), asc(validations.obligationId)).limit(limit)).map((row) => ({
|
|
8996
9971
|
offerHash: row.offerHash,
|
|
9972
|
+
obligationId: row.obligationId,
|
|
8997
9973
|
status: row.code
|
|
8998
9974
|
}));
|
|
8999
9975
|
return {
|
|
9000
9976
|
validations: mapped,
|
|
9001
|
-
nextCursor: mapped.length === limit ? mapped[mapped.length - 1]
|
|
9977
|
+
nextCursor: mapped.length === limit ? formatCursor$1(mapped[mapped.length - 1]) : null
|
|
9002
9978
|
};
|
|
9003
9979
|
},
|
|
9004
9980
|
upsert: async (results) => {
|
|
@@ -9008,6 +9984,7 @@ function create$3(db) {
|
|
|
9008
9984
|
if (invalidStatuses.length > 0) throw new Error(`Unknown validation status: ${invalidStatuses.join(", ")}`);
|
|
9009
9985
|
const normalized = results.map((r) => ({
|
|
9010
9986
|
offerHash: r.offerHash.toLowerCase(),
|
|
9987
|
+
obligationId: r.obligationId.toLowerCase(),
|
|
9011
9988
|
status: r.status
|
|
9012
9989
|
}));
|
|
9013
9990
|
const uniqueStatuses = Array.from(new Set(normalized.map((r) => r.status)));
|
|
@@ -9019,10 +9996,11 @@ function create$3(db) {
|
|
|
9019
9996
|
for (const status of uniqueStatuses) if (!statusMap.has(status)) throw new Error(`Unknown validation status: ${status}`);
|
|
9020
9997
|
const values = normalized.map((row) => ({
|
|
9021
9998
|
offerHash: row.offerHash,
|
|
9999
|
+
obligationId: row.obligationId,
|
|
9022
10000
|
statusId: statusMap.get(row.status)
|
|
9023
10001
|
}));
|
|
9024
10002
|
for (const batch of batch$1(values, DEFAULT_BATCH_SIZE)) await db.insert(validations).values(batch).onConflictDoUpdate({
|
|
9025
|
-
target: [validations.offerHash],
|
|
10003
|
+
target: [validations.offerHash, validations.obligationId],
|
|
9026
10004
|
set: {
|
|
9027
10005
|
statusId: sql`excluded.status_id`,
|
|
9028
10006
|
updatedAt: sql`NOW()`
|
|
@@ -9031,6 +10009,18 @@ function create$3(db) {
|
|
|
9031
10009
|
}
|
|
9032
10010
|
};
|
|
9033
10011
|
}
|
|
10012
|
+
const HEX_32$1 = /^0x[a-fA-F0-9]{64}$/;
|
|
10013
|
+
function parseCursor$1(cursor) {
|
|
10014
|
+
const [offerHash, obligationId] = cursor.split(":");
|
|
10015
|
+
if (!offerHash || !obligationId || !HEX_32$1.test(offerHash) || !HEX_32$1.test(obligationId)) throw new Error("Invalid cursor format");
|
|
10016
|
+
return {
|
|
10017
|
+
offerHash: offerHash.toLowerCase(),
|
|
10018
|
+
obligationId: obligationId.toLowerCase()
|
|
10019
|
+
};
|
|
10020
|
+
}
|
|
10021
|
+
function formatCursor$1(input) {
|
|
10022
|
+
return `${input.offerHash.toLowerCase()}:${input.obligationId.toLowerCase()}`;
|
|
10023
|
+
}
|
|
9034
10024
|
|
|
9035
10025
|
//#endregion
|
|
9036
10026
|
//#region src/database/readers/ObligationsListing.ts
|
|
@@ -9073,23 +10063,22 @@ function create$2(parameters) {
|
|
|
9073
10063
|
const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? sql`(${sql.join(loanTokens.map((token) => sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), sql` OR `)})` : void 0;
|
|
9074
10064
|
const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? sql`EXISTS (
|
|
9075
10065
|
SELECT 1 FROM ${obligationCollateralsV2} oc
|
|
9076
|
-
WHERE oc.
|
|
10066
|
+
WHERE oc.obligation_key = ${obligations.obligationKey}
|
|
9077
10067
|
AND (${sql.join(collateralTokens.map((token) => sql`LOWER(oc.asset) = ${token.toLowerCase()}`), sql` OR `)})
|
|
9078
10068
|
)` : void 0;
|
|
9079
|
-
const bestAskTick = db.select({ askTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId,
|
|
9080
|
-
const bestBidTick = db.select({ bidTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId,
|
|
10069
|
+
const bestAskTick = db.select({ askTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, and(eq(offers.hash, validations.offerHash), eq(offers.obligationId, validations.obligationId))).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId, obligationIdKeys.obligationId), eq(offers.buy, false), gte(offers.expiry, now$1), gte(offers.maturity, now$1), lte(offers.start, now$1), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(desc(offers.tick)).limit(1).as("best_ask_tick");
|
|
10070
|
+
const bestBidTick = db.select({ bidTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, and(eq(offers.hash, validations.offerHash), eq(offers.obligationId, validations.obligationId))).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId, obligationIdKeys.obligationId), eq(offers.buy, true), gte(offers.expiry, now$1), gte(offers.maturity, now$1), lte(offers.start, now$1), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(asc(offers.tick)).limit(1).as("best_bid_tick");
|
|
9081
10071
|
const obligationsWithQuotes = db.select({
|
|
9082
|
-
obligationId:
|
|
9083
|
-
chainId:
|
|
10072
|
+
obligationId: obligationIdKeys.obligationId,
|
|
10073
|
+
chainId: obligationIdKeys.chainId,
|
|
9084
10074
|
loanToken: obligations.loanToken,
|
|
9085
|
-
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${
|
|
10075
|
+
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${obligationCollateralsV2.oracleAddress}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
9086
10076
|
maturity: obligations.maturity,
|
|
9087
10077
|
askTick: sql`MAX(${bestAskTick.askTick})`.as("ask_tick"),
|
|
9088
10078
|
bidTick: sql`MAX(${bestBidTick.bidTick})`.as("bid_tick"),
|
|
9089
10079
|
ask: sql`COALESCE(MAX(${bestAskTick.askTick}) + 1, 0)`.as("ask"),
|
|
9090
10080
|
bid: sql`COALESCE(MAX(${bestBidTick.bidTick}) + 1, 0)`.as("bid")
|
|
9091
|
-
}).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.
|
|
9092
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).leftJoinLateral(bestAskTick, sql`true`).leftJoinLateral(bestBidTick, sql`true`).groupBy(obligations.obligationId).where(and(ids !== void 0 && ids.length > 0 ? inArray(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? inArray(obligations.maturity, maturities) : gte(obligations.maturity, now$1), collateralFilter)).as("obligations_with_quotes");
|
|
10081
|
+
}).from(obligationIdKeys).innerJoin(obligations, eq(obligationIdKeys.obligationKey, obligations.obligationKey)).innerJoin(obligationCollateralsV2, eq(obligations.obligationKey, obligationCollateralsV2.obligationKey)).leftJoinLateral(bestAskTick, sql`true`).leftJoinLateral(bestBidTick, sql`true`).groupBy(obligationIdKeys.obligationId, obligationIdKeys.chainId, obligations.loanToken, obligations.maturity).where(and(ids !== void 0 && ids.length > 0 ? inArray(obligationIdKeys.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? inArray(obligationIdKeys.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? inArray(obligations.maturity, maturities) : gte(obligations.maturity, now$1), collateralFilter)).as("obligations_with_quotes");
|
|
9093
10082
|
const sortColumns = {
|
|
9094
10083
|
id: obligationsWithQuotes.obligationId,
|
|
9095
10084
|
ask: obligationsWithQuotes.ask,
|
|
@@ -9109,22 +10098,25 @@ function create$2(parameters) {
|
|
|
9109
10098
|
}).from(obligationsWithQuotes).where(buildCursorFilter(sortColumns, sort, cursorValues)).orderBy(...buildOrderBy(sortColumns, sort)).limit(limit + 1);
|
|
9110
10099
|
const hasMore = rows.length > limit;
|
|
9111
10100
|
const listedRows = (hasMore ? rows.slice(0, limit) : rows).map((row) => {
|
|
10101
|
+
const obligation = from$13({
|
|
10102
|
+
loanToken: row.loanToken,
|
|
10103
|
+
collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$14({
|
|
10104
|
+
asset: collateral.asset,
|
|
10105
|
+
oracle: collateral.oracle,
|
|
10106
|
+
lltv: from$15(BigInt(collateral.lltv))
|
|
10107
|
+
})),
|
|
10108
|
+
maturity: row.maturity
|
|
10109
|
+
});
|
|
10110
|
+
const quote = from$9({
|
|
10111
|
+
obligationId: row.obligationId,
|
|
10112
|
+
ask: { tick: row.askTick },
|
|
10113
|
+
bid: { tick: row.bidTick }
|
|
10114
|
+
});
|
|
9112
10115
|
return {
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
asset: collateral.asset,
|
|
9118
|
-
oracle: collateral.oracle,
|
|
9119
|
-
lltv: from$15(BigInt(collateral.lltv))
|
|
9120
|
-
})),
|
|
9121
|
-
maturity: row.maturity
|
|
9122
|
-
}),
|
|
9123
|
-
quote: from$9({
|
|
9124
|
-
obligationId: row.obligationId,
|
|
9125
|
-
ask: { tick: row.askTick },
|
|
9126
|
-
bid: { tick: row.bidTick }
|
|
9127
|
-
}),
|
|
10116
|
+
obligationId: row.obligationId,
|
|
10117
|
+
chainId: row.chainId,
|
|
10118
|
+
obligation,
|
|
10119
|
+
quote,
|
|
9128
10120
|
cursorValues: {
|
|
9129
10121
|
id: row.obligationId,
|
|
9130
10122
|
ask: toBigInt(row.ask),
|
|
@@ -9142,6 +10134,8 @@ function create$2(parameters) {
|
|
|
9142
10134
|
}) : null;
|
|
9143
10135
|
return {
|
|
9144
10136
|
obligations: listedRows.map((row) => ({
|
|
10137
|
+
obligationId: row.obligationId,
|
|
10138
|
+
chainId: row.chainId,
|
|
9145
10139
|
obligation: row.obligation,
|
|
9146
10140
|
quote: row.quote
|
|
9147
10141
|
})),
|
|
@@ -9445,7 +10439,7 @@ async function postMigrate(driver) {
|
|
|
9445
10439
|
const tracer = getTracer("db.postMigrate");
|
|
9446
10440
|
await startActiveSpan(tracer, "db.postMigrate", async () => {
|
|
9447
10441
|
await driver.execute(`INSERT INTO "${VERSION}"."status" ("code") VALUES ('${Status.VALID}'), ('${Status.SIMULATION_ERROR}') ON CONFLICT DO NOTHING;`);
|
|
9448
|
-
await driver.execute(`INSERT INTO "${VERSION}"."position_types" ("type") VALUES ('${Type.ERC20}'), ('${Type.VAULT_V1}') ON CONFLICT DO NOTHING;`);
|
|
10442
|
+
await driver.execute(`INSERT INTO "${VERSION}"."position_types" ("type") VALUES ('${Type.ERC20}'), ('${Type.VAULT_V1}'), ('${Type.DEBT_OF}'), ('${Type.COLLATERAL_OF}') ON CONFLICT DO NOTHING;`);
|
|
9449
10443
|
await driver.execute(`
|
|
9450
10444
|
CREATE OR REPLACE FUNCTION apply_group_consumed_stmt_ins()
|
|
9451
10445
|
RETURNS trigger
|
|
@@ -9599,19 +10593,23 @@ async function postMigrate(driver) {
|
|
|
9599
10593
|
BEGIN
|
|
9600
10594
|
DELETE FROM "${VERSION}"."positions" p
|
|
9601
10595
|
USING (
|
|
9602
|
-
SELECT DISTINCT c.position_chain_id, c.position_contract, c.position_user
|
|
10596
|
+
SELECT DISTINCT c.position_chain_id, c.position_contract, c.position_user, c.position_type_id
|
|
9603
10597
|
FROM deleted_rows d
|
|
9604
10598
|
JOIN "${VERSION}"."callbacks" c ON c.id = d.callback_id
|
|
9605
10599
|
) AS affected
|
|
10600
|
+
JOIN "${VERSION}"."position_types" pt ON pt.id = affected.position_type_id
|
|
9606
10601
|
WHERE p.chain_id = affected.position_chain_id
|
|
9607
10602
|
AND p.contract = affected.position_contract
|
|
9608
10603
|
AND p."user" = affected.position_user
|
|
10604
|
+
AND p.position_type_id = affected.position_type_id
|
|
10605
|
+
AND pt.type = 'erc20'
|
|
9609
10606
|
AND NOT EXISTS (
|
|
9610
10607
|
SELECT 1 FROM "${VERSION}"."callbacks" c2
|
|
9611
10608
|
JOIN "${VERSION}"."offers_callbacks" oc ON oc.callback_id = c2.id
|
|
9612
10609
|
WHERE c2.position_chain_id = p.chain_id
|
|
9613
10610
|
AND c2.position_contract = p.contract
|
|
9614
10611
|
AND c2.position_user = p."user"
|
|
10612
|
+
AND c2.position_type_id = p.position_type_id
|
|
9615
10613
|
);
|
|
9616
10614
|
RETURN NULL;
|
|
9617
10615
|
END;
|
|
@@ -9658,7 +10656,7 @@ async function postMigrate(driver) {
|
|
|
9658
10656
|
RETURNS TRIGGER AS $$
|
|
9659
10657
|
DECLARE
|
|
9660
10658
|
orphan_obligation_ids TEXT[];
|
|
9661
|
-
|
|
10659
|
+
orphan_obligation_keys TEXT[];
|
|
9662
10660
|
BEGIN
|
|
9663
10661
|
-- 1. Find orphan obligation IDs
|
|
9664
10662
|
SELECT ARRAY_AGG(DISTINCT obligation_id) INTO orphan_obligation_ids
|
|
@@ -9666,33 +10664,39 @@ async function postMigrate(driver) {
|
|
|
9666
10664
|
WHERE NOT EXISTS (
|
|
9667
10665
|
SELECT 1 FROM "${VERSION}"."offers" ov
|
|
9668
10666
|
WHERE ov.obligation_id = d.obligation_id
|
|
10667
|
+
)
|
|
10668
|
+
AND NOT EXISTS (
|
|
10669
|
+
SELECT 1 FROM "${VERSION}"."positions" p
|
|
10670
|
+
JOIN "${VERSION}"."position_types" pt ON pt.id = p.position_type_id
|
|
10671
|
+
WHERE p."contract" = d.obligation_id
|
|
10672
|
+
AND pt.type IN ('debtOf', 'collateralOf')
|
|
9669
10673
|
);
|
|
9670
10674
|
|
|
9671
|
-
-- 2. If no orphan
|
|
10675
|
+
-- 2. If no orphan obligation IDs, exit early
|
|
9672
10676
|
IF orphan_obligation_ids IS NULL OR array_length(orphan_obligation_ids, 1) IS NULL THEN
|
|
9673
10677
|
RETURN NULL;
|
|
9674
10678
|
END IF;
|
|
9675
10679
|
|
|
9676
|
-
-- 3.
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
FROM "${VERSION}"."obligation_collaterals_v2" oc
|
|
9680
|
-
WHERE oc.obligation_id = ANY(orphan_obligation_ids);
|
|
10680
|
+
-- 3. Delete orphan obligation id keys
|
|
10681
|
+
DELETE FROM "${VERSION}"."obligation_id_keys" oia
|
|
10682
|
+
WHERE oia.obligation_id = ANY(orphan_obligation_ids);
|
|
9681
10683
|
|
|
9682
|
-
-- 4.
|
|
9683
|
-
|
|
9684
|
-
|
|
10684
|
+
-- 4. Find canonical obligation keys with no remaining obligation id keys
|
|
10685
|
+
SELECT ARRAY_AGG(ob.obligation_key) INTO orphan_obligation_keys
|
|
10686
|
+
FROM "${VERSION}"."obligations" ob
|
|
10687
|
+
WHERE NOT EXISTS (
|
|
10688
|
+
SELECT 1 FROM "${VERSION}"."obligation_id_keys" oia
|
|
10689
|
+
WHERE oia.obligation_key = ob.obligation_key
|
|
10690
|
+
);
|
|
9685
10691
|
|
|
9686
|
-
-- 5.
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
AND oc.oracle_address = o.address
|
|
9695
|
-
);
|
|
10692
|
+
-- 5. If no orphan canonical obligations, exit early
|
|
10693
|
+
IF orphan_obligation_keys IS NULL OR array_length(orphan_obligation_keys, 1) IS NULL THEN
|
|
10694
|
+
RETURN NULL;
|
|
10695
|
+
END IF;
|
|
10696
|
+
|
|
10697
|
+
-- 6. Delete orphan canonical obligations (cascades to collaterals)
|
|
10698
|
+
DELETE FROM "${VERSION}"."obligations" ob
|
|
10699
|
+
WHERE ob.obligation_key = ANY(orphan_obligation_keys);
|
|
9696
10700
|
|
|
9697
10701
|
RETURN NULL;
|
|
9698
10702
|
END;
|
|
@@ -9710,16 +10714,21 @@ async function postMigrate(driver) {
|
|
|
9710
10714
|
RETURNS trigger
|
|
9711
10715
|
LANGUAGE plpgsql AS $$
|
|
9712
10716
|
BEGIN
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
10717
|
+
BEGIN
|
|
10718
|
+
INSERT INTO "${VERSION}"."offsets" (chain_id, "user", contract, "group", obligation_id, value)
|
|
10719
|
+
VALUES (
|
|
10720
|
+
OLD.chain_id,
|
|
10721
|
+
OLD."user",
|
|
10722
|
+
OLD.contract,
|
|
10723
|
+
OLD."group",
|
|
10724
|
+
OLD.obligation_id,
|
|
10725
|
+
OLD.upper::numeric - OLD.lower::numeric
|
|
10726
|
+
)
|
|
10727
|
+
ON CONFLICT (chain_id, "user", contract, "group", obligation_id) DO NOTHING;
|
|
10728
|
+
EXCEPTION WHEN foreign_key_violation THEN
|
|
10729
|
+
-- lots_positions may be gone during cascaded deletes
|
|
10730
|
+
NULL;
|
|
10731
|
+
END;
|
|
9723
10732
|
RETURN OLD;
|
|
9724
10733
|
END;
|
|
9725
10734
|
$$;
|
|
@@ -9742,11 +10751,26 @@ async function postMigrate(driver) {
|
|
|
9742
10751
|
AND l.contract = OLD.contract
|
|
9743
10752
|
AND l."user" = OLD."user"
|
|
9744
10753
|
) THEN
|
|
9745
|
-
-- No lots remain, delete
|
|
10754
|
+
-- No lots remain, delete erc20 positions and their lots_positions bridge rows.
|
|
10755
|
+
-- debtOf/collateralOf positions live independently of offers and must not be deleted here.
|
|
10756
|
+
-- First delete the position (for ERC20, asset = contract).
|
|
9746
10757
|
DELETE FROM "${VERSION}"."positions" p
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
10758
|
+
USING "${VERSION}"."lots_positions" lp
|
|
10759
|
+
JOIN "${VERSION}"."position_types" pt ON pt.id = lp.position_type_id
|
|
10760
|
+
WHERE lp.chain_id = OLD.chain_id
|
|
10761
|
+
AND lp.contract = OLD.contract
|
|
10762
|
+
AND lp."user" = OLD."user"
|
|
10763
|
+
AND p.chain_id = lp.chain_id
|
|
10764
|
+
AND p.contract = lp.contract
|
|
10765
|
+
AND p."user" = lp."user"
|
|
10766
|
+
AND p.position_type_id = lp.position_type_id
|
|
10767
|
+
AND pt.type = 'erc20';
|
|
10768
|
+
|
|
10769
|
+
-- Delete the lots_positions bridge row (cascades to offsets via offsets_lots_positions_fk).
|
|
10770
|
+
DELETE FROM "${VERSION}"."lots_positions" lp
|
|
10771
|
+
WHERE lp.chain_id = OLD.chain_id
|
|
10772
|
+
AND lp.contract = OLD.contract
|
|
10773
|
+
AND lp."user" = OLD."user";
|
|
9750
10774
|
END IF;
|
|
9751
10775
|
RETURN NULL;
|
|
9752
10776
|
END;
|
|
@@ -9846,7 +10870,7 @@ function resolveRouterConfig(config, env) {
|
|
|
9846
10870
|
const override = config.chains[name];
|
|
9847
10871
|
if (!override) throw new Error(`Chain ${name} is missing configuration.`);
|
|
9848
10872
|
const rpcUrl = resolveRpcUrl(name, override, env, indexerChains.includes(name));
|
|
9849
|
-
const chainWithContracts = applyChainOverride(chains$
|
|
10873
|
+
const chainWithContracts = applyChainOverride(chains$1[name], override);
|
|
9850
10874
|
acc[name] = {
|
|
9851
10875
|
chain: rpcUrl ? applyRpcUrlOverride(chainWithContracts, rpcUrl) : chainWithContracts,
|
|
9852
10876
|
rpcUrl
|
|
@@ -9955,7 +10979,7 @@ function resolveRpcUrls(baseChain, rpcUrl) {
|
|
|
9955
10979
|
function createDefaultConfig() {
|
|
9956
10980
|
const chainName = "ethereum-virtual-testnet";
|
|
9957
10981
|
const rpcUrl = "https://virtual.mainnet.eu.rpc.tenderly.co/49383bba-e2c5-423e-bc94-8e78c91f1130";
|
|
9958
|
-
const baseChain = chains$
|
|
10982
|
+
const baseChain = chains$1[chainName];
|
|
9959
10983
|
const chainWithRpc = applyRpcUrlOverride(baseChain, rpcUrl);
|
|
9960
10984
|
const chain = {
|
|
9961
10985
|
...chainWithRpc,
|
|
@@ -10067,7 +11091,10 @@ const gatekeeperCmd = new RouterCmd("gatekeeper");
|
|
|
10067
11091
|
gatekeeperCmd.description("Start Gatekeeper validation service.").action(async (opts) => {
|
|
10068
11092
|
const { gatekeeper: gatekeeperConfig, chainRegistry, logger } = opts;
|
|
10069
11093
|
await runWithLogger(logger, async () => {
|
|
10070
|
-
const gatekeeperCore = create$21({ rules:
|
|
11094
|
+
const gatekeeperCore = create$21({ rules: (chainId) => morphoRules({
|
|
11095
|
+
chains: chainRegistry.list(),
|
|
11096
|
+
chainId
|
|
11097
|
+
}) });
|
|
10071
11098
|
const handle = await start$1({
|
|
10072
11099
|
gatekeeper: gatekeeperCore,
|
|
10073
11100
|
chainRegistry,
|
|
@@ -10551,7 +11578,7 @@ async function getObligation(params, db) {
|
|
|
10551
11578
|
if (listing.obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
|
|
10552
11579
|
const obligation = listing.obligations[0];
|
|
10553
11580
|
return success({
|
|
10554
|
-
data: from$5(obligation.obligation, obligation.quote),
|
|
11581
|
+
data: from$5(obligation.obligation, obligation.quote, obligation.chainId),
|
|
10555
11582
|
cursor: null
|
|
10556
11583
|
});
|
|
10557
11584
|
} catch (err) {
|
|
@@ -10592,7 +11619,7 @@ async function getObligations(queryParameters, db) {
|
|
|
10592
11619
|
limit: query.limit
|
|
10593
11620
|
});
|
|
10594
11621
|
return success({
|
|
10595
|
-
data: listing.obligations.map((item) => from$5(item.obligation, item.quote)),
|
|
11622
|
+
data: listing.obligations.map((item) => from$5(item.obligation, item.quote, item.chainId)),
|
|
10596
11623
|
cursor: listing.nextCursor
|
|
10597
11624
|
});
|
|
10598
11625
|
} catch (err) {
|
|
@@ -10617,23 +11644,20 @@ async function getObligations(queryParameters, db) {
|
|
|
10617
11644
|
*/
|
|
10618
11645
|
async function getOffersQuery(db, parameters) {
|
|
10619
11646
|
const limit = parameters?.limit ?? DEFAULT_LIMIT$3;
|
|
10620
|
-
const
|
|
11647
|
+
const rawCursor = parameters?.cursor;
|
|
10621
11648
|
const maker = parameters?.maker;
|
|
10622
|
-
|
|
10623
|
-
if (!cursor.startsWith("0x") || cursor.length !== 66) throw new Error("Invalid cursor format");
|
|
10624
|
-
}
|
|
11649
|
+
const cursor = maker && rawCursor !== void 0 && rawCursor !== null ? parseMakerCursor(rawCursor) : void 0;
|
|
10625
11650
|
const now = Math.floor((Date.now() - 1) / 1e3);
|
|
10626
11651
|
const collateralsLateral = db.select({ collaterals: sql`COALESCE(
|
|
10627
11652
|
jsonb_agg(
|
|
10628
11653
|
jsonb_build_object(
|
|
10629
11654
|
'asset', ${obligationCollateralsV2.asset},
|
|
10630
|
-
'oracle', ${
|
|
11655
|
+
'oracle', ${obligationCollateralsV2.oracleAddress},
|
|
10631
11656
|
'lltv', ${obligationCollateralsV2.lltv}
|
|
10632
11657
|
)
|
|
10633
11658
|
),
|
|
10634
11659
|
'[]'::jsonb
|
|
10635
|
-
)`.as("collaterals") }).from(obligationCollateralsV2).
|
|
10636
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).where(eq(obligationCollateralsV2.obligationId, offers.obligationId)).as("collaterals_lateral");
|
|
11660
|
+
)`.as("collaterals") }).from(obligationCollateralsV2).where(eq(obligationCollateralsV2.obligationKey, obligationIdKeys.obligationKey)).as("collaterals_lateral");
|
|
10637
11661
|
const lotBalanceExpr = sql`GREATEST(0, LEAST(
|
|
10638
11662
|
COALESCE(${positions.balance}, 0)::numeric
|
|
10639
11663
|
+ COALESCE((
|
|
@@ -10678,6 +11702,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10678
11702
|
AND LOWER(${lots.user}) = LOWER(${callbacks.positionUser})
|
|
10679
11703
|
AND LOWER(${lots.group}) = LOWER(${offers.group})
|
|
10680
11704
|
WHERE ${offersCallbacks.offerHash} = ${offers.hash}
|
|
11705
|
+
AND ${offersCallbacks.obligationId} = ${offers.obligationId}
|
|
10681
11706
|
ORDER BY
|
|
10682
11707
|
${callbacks.positionChainId},
|
|
10683
11708
|
LOWER(${callbacks.positionContract}),
|
|
@@ -10687,6 +11712,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10687
11712
|
), 0)`;
|
|
10688
11713
|
const rows = (await db.select({
|
|
10689
11714
|
hash: offers.hash,
|
|
11715
|
+
obligationId: offers.obligationId,
|
|
10690
11716
|
maker: offers.groupMaker,
|
|
10691
11717
|
assets: offers.assets,
|
|
10692
11718
|
obligationUnits: offers.obligationUnits,
|
|
@@ -10699,7 +11725,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10699
11725
|
group: offers.group,
|
|
10700
11726
|
session: offers.session,
|
|
10701
11727
|
buy: offers.buy,
|
|
10702
|
-
chainId:
|
|
11728
|
+
chainId: obligationIdKeys.chainId,
|
|
10703
11729
|
loanToken: obligations.loanToken,
|
|
10704
11730
|
callbackAddress: offers.callbackAddress,
|
|
10705
11731
|
callbackData: offers.callbackData,
|
|
@@ -10716,7 +11742,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10716
11742
|
)
|
|
10717
11743
|
END
|
|
10718
11744
|
))`.as("takeable")
|
|
10719
|
-
}).from(offers).innerJoin(
|
|
11745
|
+
}).from(offers).innerJoin(obligationIdKeys, eq(offers.obligationId, obligationIdKeys.obligationId)).innerJoin(obligations, eq(obligationIdKeys.obligationKey, obligations.obligationKey)).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).innerJoinLateral(collateralsLateral, sql`true`).where(and(cursor !== void 0 ? cursor.obligationId === void 0 ? gt(offers.hash, cursor.hash) : or(gt(offers.hash, cursor.hash), and(eq(offers.hash, cursor.hash), gt(offers.obligationId, cursor.obligationId))) : void 0, maker !== void 0 ? eq(offers.groupMaker, maker.toLowerCase()) : void 0, gte(offers.expiry, now), gte(offers.maturity, now), maker === void 0 ? sql`GREATEST(0,
|
|
10720
11746
|
CASE WHEN ${offers.buy} = false
|
|
10721
11747
|
THEN ${offers.assets}::numeric - ${groups.consumed}::numeric
|
|
10722
11748
|
ELSE LEAST(
|
|
@@ -10724,10 +11750,11 @@ async function getOffersQuery(db, parameters) {
|
|
|
10724
11750
|
${availableExpr}::numeric
|
|
10725
11751
|
)
|
|
10726
11752
|
END
|
|
10727
|
-
) > 0` : void 0)).orderBy(asc(offers.hash)).limit(limit)).map((row) => {
|
|
11753
|
+
) > 0` : void 0)).orderBy(asc(offers.hash), asc(offers.obligationId)).limit(limit)).map((row) => {
|
|
10728
11754
|
const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
|
|
10729
11755
|
return {
|
|
10730
11756
|
hash: row.hash,
|
|
11757
|
+
obligationId: row.obligationId,
|
|
10731
11758
|
maker: row.maker,
|
|
10732
11759
|
assets: BigInt(row.assets),
|
|
10733
11760
|
obligationUnits: BigInt(row.obligationUnits),
|
|
@@ -10759,7 +11786,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10759
11786
|
});
|
|
10760
11787
|
return {
|
|
10761
11788
|
rows,
|
|
10762
|
-
nextCursor: rows.length === limit ? rows[rows.length - 1].hash : null
|
|
11789
|
+
nextCursor: rows.length === limit ? maker === void 0 ? rows[rows.length - 1].hash : formatOfferCursor(rows[rows.length - 1]) : null
|
|
10763
11790
|
};
|
|
10764
11791
|
}
|
|
10765
11792
|
async function getOffers(queryParameters, db) {
|
|
@@ -10778,12 +11805,18 @@ async function getOffers(queryParameters, db) {
|
|
|
10778
11805
|
cursor: query.cursor,
|
|
10779
11806
|
limit: query.limit
|
|
10780
11807
|
});
|
|
10781
|
-
const
|
|
10782
|
-
|
|
11808
|
+
const offers = rows.map((row) => ({
|
|
11809
|
+
offerHash: row.hash,
|
|
11810
|
+
obligationId: row.obligationId
|
|
11811
|
+
}));
|
|
11812
|
+
const attestationMap = await db.trees.getAttestations(offers);
|
|
10783
11813
|
return success({
|
|
10784
11814
|
data: rows.map((row) => {
|
|
10785
|
-
const
|
|
10786
|
-
|
|
11815
|
+
const key = toAttestationKey({
|
|
11816
|
+
offerHash: row.hash,
|
|
11817
|
+
obligationId: row.obligationId
|
|
11818
|
+
});
|
|
11819
|
+
const attestation = attestationMap.get(key);
|
|
10787
11820
|
return from$4({
|
|
10788
11821
|
...row,
|
|
10789
11822
|
...attestation
|
|
@@ -10801,6 +11834,19 @@ async function getOffers(queryParameters, db) {
|
|
|
10801
11834
|
return failure(err);
|
|
10802
11835
|
}
|
|
10803
11836
|
}
|
|
11837
|
+
function parseMakerCursor(cursor) {
|
|
11838
|
+
const [rawHash, rawObligationId, ...tail] = cursor.split(":");
|
|
11839
|
+
if (tail.length > 0) throw new Error("Invalid cursor format");
|
|
11840
|
+
if (!rawHash || !rawObligationId || !HEX_32.test(rawHash) || !HEX_32.test(rawObligationId)) throw new Error("Invalid cursor format");
|
|
11841
|
+
return {
|
|
11842
|
+
hash: rawHash.toLowerCase(),
|
|
11843
|
+
obligationId: rawObligationId.toLowerCase()
|
|
11844
|
+
};
|
|
11845
|
+
}
|
|
11846
|
+
function formatOfferCursor(row) {
|
|
11847
|
+
return `${row.hash.toLowerCase()}:${row.obligationId.toLowerCase()}`;
|
|
11848
|
+
}
|
|
11849
|
+
const HEX_32 = /^0x[a-fA-F0-9]{64}$/;
|
|
10804
11850
|
|
|
10805
11851
|
//#endregion
|
|
10806
11852
|
//#region src/api/Controllers/getUserPositions.ts
|
|
@@ -11005,8 +12051,12 @@ function createHttpClient(config) {
|
|
|
11005
12051
|
body: json
|
|
11006
12052
|
};
|
|
11007
12053
|
};
|
|
11008
|
-
const isAllowed = async (
|
|
11009
|
-
const {
|
|
12054
|
+
const isAllowed = async (parameters) => {
|
|
12055
|
+
const { offers, chainId } = parameters;
|
|
12056
|
+
const { statusCode, body } = await validate({
|
|
12057
|
+
chain_id: chainId,
|
|
12058
|
+
offers: offers.map((offer) => toSnakeCase(offer))
|
|
12059
|
+
});
|
|
11010
12060
|
if (statusCode !== 200) {
|
|
11011
12061
|
const errorMessage = extractErrorMessage(body);
|
|
11012
12062
|
throw new Error(`Gatekeeper validation failed: ${errorMessage ?? `status ${statusCode}`}`);
|
|
@@ -11064,7 +12114,10 @@ startCmd.description("Start Router services.").addOption(new Option("--mock <n>"
|
|
|
11064
12114
|
let gatekeeperUrl = gatekeeperConfig?.url;
|
|
11065
12115
|
let gatekeeperHandle = null;
|
|
11066
12116
|
if (!gatekeeperUrl) {
|
|
11067
|
-
const gatekeeperCore = create$21({ rules:
|
|
12117
|
+
const gatekeeperCore = create$21({ rules: (chainId) => morphoRules({
|
|
12118
|
+
chains: chainRegistry.list(),
|
|
12119
|
+
chainId
|
|
12120
|
+
}) });
|
|
11068
12121
|
gatekeeperHandle = await start$1({
|
|
11069
12122
|
gatekeeper: gatekeeperCore,
|
|
11070
12123
|
chainRegistry,
|
|
@@ -11096,17 +12149,23 @@ startCmd.description("Start Router services.").addOption(new Option("--mock <n>"
|
|
|
11096
12149
|
}
|
|
11097
12150
|
if (file) {
|
|
11098
12151
|
const offers = await getOffersFromFile(file);
|
|
12152
|
+
const offersOnChain = withSingleChainContext({
|
|
12153
|
+
offers,
|
|
12154
|
+
chainRegistry
|
|
12155
|
+
});
|
|
11099
12156
|
const offerBlockNumber = 1;
|
|
11100
12157
|
const positionBlockNumber = offerBlockNumber + 1;
|
|
11101
12158
|
await seedOffers({
|
|
11102
12159
|
db,
|
|
11103
|
-
|
|
12160
|
+
chainRegistry,
|
|
12161
|
+
offers: offersOnChain,
|
|
11104
12162
|
blockNumber: offerBlockNumber
|
|
11105
12163
|
});
|
|
11106
12164
|
await seedOfferAssociations({
|
|
11107
12165
|
db,
|
|
12166
|
+
chainRegistry,
|
|
11108
12167
|
gatekeeper,
|
|
11109
|
-
offers,
|
|
12168
|
+
offers: offersOnChain,
|
|
11110
12169
|
blockNumber: positionBlockNumber
|
|
11111
12170
|
});
|
|
11112
12171
|
logger.info({
|
|
@@ -11203,7 +12262,7 @@ async function seedMockOffers(parameters) {
|
|
|
11203
12262
|
const expiry = now + 3600;
|
|
11204
12263
|
const maturity = from$16("end_of_next_month");
|
|
11205
12264
|
const oraclePrice = 10n ** 36n;
|
|
11206
|
-
const
|
|
12265
|
+
const offersOnChain = createMockOffers({
|
|
11207
12266
|
count,
|
|
11208
12267
|
chains: configuredChains,
|
|
11209
12268
|
assetsByChainId,
|
|
@@ -11216,23 +12275,35 @@ async function seedMockOffers(parameters) {
|
|
|
11216
12275
|
});
|
|
11217
12276
|
await seedOffers({
|
|
11218
12277
|
db,
|
|
11219
|
-
|
|
12278
|
+
chainRegistry,
|
|
12279
|
+
offers: offersOnChain,
|
|
11220
12280
|
blockNumber: offerBlockNumber
|
|
11221
12281
|
});
|
|
11222
|
-
await db.oracles.upsert(seedMockOracles(
|
|
12282
|
+
await db.oracles.upsert(seedMockOracles(offersOnChain, oraclePrice, positionBlockNumber));
|
|
11223
12283
|
await seedOfferAssociations({
|
|
11224
12284
|
db,
|
|
12285
|
+
chainRegistry,
|
|
11225
12286
|
gatekeeper,
|
|
11226
|
-
offers,
|
|
12287
|
+
offers: offersOnChain,
|
|
11227
12288
|
blockNumber: positionBlockNumber
|
|
11228
12289
|
});
|
|
11229
|
-
return
|
|
12290
|
+
return offersOnChain.map((entry) => entry.offer);
|
|
11230
12291
|
}
|
|
11231
12292
|
async function getOffersFromFile(filePath) {
|
|
11232
12293
|
const content = await readFile(filePath, "utf-8");
|
|
11233
12294
|
const data = JSON.parse(content);
|
|
11234
12295
|
return Array.isArray(data) ? data.map(from$12) : [from$12(data)];
|
|
11235
12296
|
}
|
|
12297
|
+
function withSingleChainContext(parameters) {
|
|
12298
|
+
const { offers, chainRegistry } = parameters;
|
|
12299
|
+
const chains = chainRegistry.list();
|
|
12300
|
+
if (chains.length !== 1) throw new Error(`Offer files require exactly one configured chain because offers are chain-agnostic. Found ${chains.length}.`);
|
|
12301
|
+
const chainId = chains[0].id;
|
|
12302
|
+
return offers.map((offer) => ({
|
|
12303
|
+
offer,
|
|
12304
|
+
chainId
|
|
12305
|
+
}));
|
|
12306
|
+
}
|
|
11236
12307
|
function createMockOffers(parameters) {
|
|
11237
12308
|
const { count, chains, assetsByChainId, oraclesByChainId, assetsDecimals, start, expiry, maturity, chainRegistry } = parameters;
|
|
11238
12309
|
if (chains.length === 0) throw new Error("No chains configured for mock offers");
|
|
@@ -11243,7 +12314,6 @@ function createMockOffers(parameters) {
|
|
|
11243
12314
|
if (allowedAssets.length === 0) throw new Error(`No allowed assets configured for chain ${chain.id}`);
|
|
11244
12315
|
const loanToken = allowedAssets[Math.floor(Math.random() * allowedAssets.length)];
|
|
11245
12316
|
const offer = random({
|
|
11246
|
-
chains: [chain],
|
|
11247
12317
|
loanTokens: [loanToken],
|
|
11248
12318
|
assetsDecimals,
|
|
11249
12319
|
start,
|
|
@@ -11270,25 +12340,28 @@ function createMockOffers(parameters) {
|
|
|
11270
12340
|
oracle: allowedOracles[Math.floor(Math.random() * allowedOracles.length)],
|
|
11271
12341
|
lltv
|
|
11272
12342
|
}));
|
|
11273
|
-
if (!chainRegistry.getById(
|
|
11274
|
-
return
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
12343
|
+
if (!chainRegistry.getById(chain.id)) throw new Error(`Missing chain config for id ${chain.id}`);
|
|
12344
|
+
return {
|
|
12345
|
+
chainId: chain.id,
|
|
12346
|
+
offer: from$12({
|
|
12347
|
+
...offer,
|
|
12348
|
+
loanToken,
|
|
12349
|
+
collaterals,
|
|
12350
|
+
callback: {
|
|
12351
|
+
address: zeroAddress,
|
|
12352
|
+
data: "0x"
|
|
12353
|
+
}
|
|
12354
|
+
})
|
|
12355
|
+
};
|
|
11283
12356
|
});
|
|
11284
12357
|
}
|
|
11285
12358
|
function seedMockOracles(offers, price, blockNumber) {
|
|
11286
12359
|
const oracleMap = /* @__PURE__ */ new Map();
|
|
11287
|
-
for (const offer of offers) for (const collateral of offer.collaterals) {
|
|
11288
|
-
const key = `${
|
|
12360
|
+
for (const { offer, chainId } of offers) for (const collateral of offer.collaterals) {
|
|
12361
|
+
const key = `${chainId}-${collateral.oracle}`.toLowerCase();
|
|
11289
12362
|
if (oracleMap.has(key)) continue;
|
|
11290
12363
|
oracleMap.set(key, from$11({
|
|
11291
|
-
chainId
|
|
12364
|
+
chainId,
|
|
11292
12365
|
address: collateral.oracle,
|
|
11293
12366
|
price: price.toString(),
|
|
11294
12367
|
blockNumber
|
|
@@ -11297,56 +12370,75 @@ function seedMockOracles(offers, price, blockNumber) {
|
|
|
11297
12370
|
return Array.from(oracleMap.values());
|
|
11298
12371
|
}
|
|
11299
12372
|
async function seedOffers(parameters) {
|
|
11300
|
-
const { db, offers, blockNumber } = parameters;
|
|
12373
|
+
const { db, chainRegistry, offers, blockNumber } = parameters;
|
|
11301
12374
|
if (offers.length === 0) return;
|
|
11302
12375
|
const dependencies = buildOfferDependencies({
|
|
11303
12376
|
offers,
|
|
11304
|
-
blockNumber
|
|
12377
|
+
blockNumber,
|
|
12378
|
+
chainRegistry
|
|
11305
12379
|
});
|
|
11306
12380
|
await db.oracles.upsert(dependencies.oracles);
|
|
11307
12381
|
await db.obligations.create(dependencies.obligations);
|
|
11308
12382
|
await db.groups.create(dependencies.groups);
|
|
11309
12383
|
await db.offers.create([{
|
|
11310
12384
|
blockNumber,
|
|
11311
|
-
offers
|
|
12385
|
+
offers: dependencies.offers
|
|
11312
12386
|
}]);
|
|
11313
12387
|
}
|
|
11314
12388
|
async function seedOfferAssociations(parameters) {
|
|
11315
|
-
const { db, offers, blockNumber } = parameters;
|
|
12389
|
+
const { db, chainRegistry, offers, blockNumber } = parameters;
|
|
11316
12390
|
if (offers.length === 0) return;
|
|
11317
12391
|
const { callbacks, positions, lots } = buildOfferAssociationsFromOffers({
|
|
11318
12392
|
offers,
|
|
11319
|
-
blockNumber
|
|
12393
|
+
blockNumber,
|
|
12394
|
+
chainRegistry
|
|
11320
12395
|
});
|
|
11321
12396
|
if (positions.length > 0) await db.positions.upsert(positions);
|
|
11322
12397
|
if (callbacks.length > 0) await db.callbacks.upsert(callbacks);
|
|
11323
12398
|
if (lots.length > 0) await db.lots.create(lots);
|
|
11324
12399
|
}
|
|
11325
12400
|
function buildOfferDependencies(parameters) {
|
|
11326
|
-
const { offers, blockNumber } = parameters;
|
|
12401
|
+
const { offers, blockNumber, chainRegistry } = parameters;
|
|
11327
12402
|
const obligationsById = /* @__PURE__ */ new Map();
|
|
12403
|
+
const offersWithIds = [];
|
|
11328
12404
|
const oraclesByKey = /* @__PURE__ */ new Map();
|
|
11329
12405
|
const groupsByKey = /* @__PURE__ */ new Map();
|
|
11330
|
-
for (const offer of offers) {
|
|
11331
|
-
const
|
|
11332
|
-
|
|
11333
|
-
chainId
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
12406
|
+
for (const { offer, chainId } of offers) {
|
|
12407
|
+
const morphoV2 = resolveMorphoV2Address({
|
|
12408
|
+
chainRegistry,
|
|
12409
|
+
chainId
|
|
12410
|
+
});
|
|
12411
|
+
const obligationId$2 = obligationId(offer, {
|
|
12412
|
+
chainId,
|
|
12413
|
+
morphoV2
|
|
12414
|
+
});
|
|
12415
|
+
offersWithIds.push({
|
|
12416
|
+
offer,
|
|
12417
|
+
obligationId: obligationId$2,
|
|
12418
|
+
chainId
|
|
12419
|
+
});
|
|
12420
|
+
if (!obligationsById.get(obligationId$2)) obligationsById.set(obligationId$2, {
|
|
12421
|
+
obligationId: obligationId$2,
|
|
12422
|
+
chainId,
|
|
12423
|
+
morphoV2,
|
|
12424
|
+
obligation: from$13({
|
|
12425
|
+
loanToken: offer.loanToken,
|
|
12426
|
+
maturity: offer.maturity,
|
|
12427
|
+
collaterals: offer.collaterals
|
|
12428
|
+
})
|
|
12429
|
+
});
|
|
11338
12430
|
for (const collateral of offer.collaterals) {
|
|
11339
|
-
const oracleKey = `${
|
|
12431
|
+
const oracleKey = `${chainId}-${collateral.oracle}`.toLowerCase();
|
|
11340
12432
|
if (!oraclesByKey.has(oracleKey)) oraclesByKey.set(oracleKey, from$11({
|
|
11341
|
-
chainId
|
|
12433
|
+
chainId,
|
|
11342
12434
|
address: collateral.oracle,
|
|
11343
12435
|
price: null,
|
|
11344
12436
|
blockNumber
|
|
11345
12437
|
}));
|
|
11346
12438
|
}
|
|
11347
|
-
const groupKey = `${
|
|
12439
|
+
const groupKey = `${chainId}-${offer.maker}-${offer.group}`.toLowerCase();
|
|
11348
12440
|
if (!groupsByKey.has(groupKey)) groupsByKey.set(groupKey, {
|
|
11349
|
-
chainId
|
|
12441
|
+
chainId,
|
|
11350
12442
|
maker: offer.maker,
|
|
11351
12443
|
group: offer.group,
|
|
11352
12444
|
blockNumber
|
|
@@ -11354,21 +12446,31 @@ function buildOfferDependencies(parameters) {
|
|
|
11354
12446
|
}
|
|
11355
12447
|
return {
|
|
11356
12448
|
obligations: Array.from(obligationsById.values()),
|
|
12449
|
+
offers: offersWithIds,
|
|
11357
12450
|
oracles: Array.from(oraclesByKey.values()),
|
|
11358
12451
|
groups: Array.from(groupsByKey.values())
|
|
11359
12452
|
};
|
|
11360
12453
|
}
|
|
11361
12454
|
function buildOfferAssociationsFromOffers(parameters) {
|
|
11362
|
-
const { offers, blockNumber } = parameters;
|
|
12455
|
+
const { offers, blockNumber, chainRegistry } = parameters;
|
|
12456
|
+
const erc20TypeId = Object.values(Type).indexOf(Type.ERC20) + 1;
|
|
11363
12457
|
const callbacks = [];
|
|
11364
12458
|
const positions = [];
|
|
11365
12459
|
const lots = [];
|
|
11366
|
-
for (const offer of offers) {
|
|
12460
|
+
for (const { offer, chainId } of offers) {
|
|
11367
12461
|
if (!offer.buy) continue;
|
|
12462
|
+
const morphoV2 = resolveMorphoV2Address({
|
|
12463
|
+
chainRegistry,
|
|
12464
|
+
chainId
|
|
12465
|
+
});
|
|
11368
12466
|
const loanToken = offer.loanToken.toLowerCase();
|
|
11369
12467
|
const offerHash = hash(offer);
|
|
12468
|
+
const obligationId$1 = obligationId(offer, {
|
|
12469
|
+
chainId,
|
|
12470
|
+
morphoV2
|
|
12471
|
+
});
|
|
11370
12472
|
positions.push(from$10({
|
|
11371
|
-
chainId
|
|
12473
|
+
chainId,
|
|
11372
12474
|
contract: loanToken,
|
|
11373
12475
|
user: offer.maker,
|
|
11374
12476
|
type: Type.ERC20,
|
|
@@ -11378,19 +12480,22 @@ function buildOfferAssociationsFromOffers(parameters) {
|
|
|
11378
12480
|
}));
|
|
11379
12481
|
callbacks.push({
|
|
11380
12482
|
offerHash,
|
|
12483
|
+
obligationId: obligationId$1,
|
|
11381
12484
|
callbacks: [{
|
|
11382
|
-
chainId
|
|
12485
|
+
chainId,
|
|
11383
12486
|
contract: loanToken,
|
|
11384
12487
|
user: offer.maker,
|
|
11385
|
-
amount: offer.assets
|
|
12488
|
+
amount: offer.assets,
|
|
12489
|
+
positionTypeId: erc20TypeId
|
|
11386
12490
|
}]
|
|
11387
12491
|
});
|
|
11388
12492
|
lots.push({
|
|
11389
|
-
positionChainId:
|
|
12493
|
+
positionChainId: chainId,
|
|
11390
12494
|
positionContract: loanToken,
|
|
11391
12495
|
positionUser: offer.maker,
|
|
12496
|
+
positionTypeId: erc20TypeId,
|
|
11392
12497
|
group: offer.group,
|
|
11393
|
-
obligationId: obligationId
|
|
12498
|
+
obligationId: obligationId$1,
|
|
11394
12499
|
size: offer.assets
|
|
11395
12500
|
});
|
|
11396
12501
|
}
|
|
@@ -11400,6 +12505,13 @@ function buildOfferAssociationsFromOffers(parameters) {
|
|
|
11400
12505
|
lots
|
|
11401
12506
|
};
|
|
11402
12507
|
}
|
|
12508
|
+
function resolveMorphoV2Address(parameters) {
|
|
12509
|
+
const chain = parameters.chainRegistry.getById(parameters.chainId);
|
|
12510
|
+
if (!chain) throw new Error(`Missing chain configuration for chain ${parameters.chainId}.`);
|
|
12511
|
+
const morphoV2 = chain.custom.morpho.address.toLowerCase();
|
|
12512
|
+
if (morphoV2 === zeroAddress) throw new Error(`Morpho V2 address is zero for chain ${parameters.chainId}.`);
|
|
12513
|
+
return morphoV2;
|
|
12514
|
+
}
|
|
11403
12515
|
|
|
11404
12516
|
//#endregion
|
|
11405
12517
|
//#region src/cli/cli.ts
|