@merkl/api 0.18.14 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/database/api/.generated/drizzle/schema.d.ts +2 -2
- package/dist/database/api/.generated/drizzle/schema.js +2 -2
- package/dist/database/api/.generated/drizzle/schema.ts +2 -2
- package/dist/database/api/.generated/edge.js +3 -3
- package/dist/database/api/.generated/index.d.ts +12 -12
- package/dist/database/api/.generated/index.js +3 -3
- package/dist/database/api/.generated/package.json +1 -1
- package/dist/database/api/.generated/schema.prisma +2 -2
- package/dist/src/backgroundJobs/index.js +0 -4
- package/dist/src/eden/index.d.ts +35 -30
- package/dist/src/factories/opportunityMetadata/implementations/EventBased.d.ts +1 -1
- package/dist/src/index.d.ts +7 -6
- package/dist/src/jobs/{etl/dynamic-data.js → dynamic-data.js} +1 -1
- package/dist/src/jobs/set-dungeon-keeper.js +82 -0
- package/dist/src/jobs/update-dynamic-data.d.ts +1 -0
- package/dist/src/jobs/update-uniswap-v4-pools.d.ts +1 -0
- package/dist/src/jobs/update-uniswap-v4-pools.js +14 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/helpers/hardcoded.js +2 -4
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/implementations/HourglassProcessor.d.ts +1 -1
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/implementations/HourglassProcessor.js +3 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/implementations/SpliceProcessor.js +1 -0
- package/dist/src/libs/campaigns/campaignTypes/SILODynamicData.js +2 -2
- package/dist/src/libs/campaigns/utils/fetchAmbientInfo.d.ts +2 -2
- package/dist/src/libs/campaigns/utils/getDolomiteMarkets.js +1 -1
- package/dist/src/libs/positions/dolomite/index.js +2 -0
- package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +5 -5
- package/dist/src/modules/v4/programPayload/programPayload.repository.d.ts +52 -1
- package/dist/src/modules/v4/programPayload/programPayload.repository.js +48 -13
- package/dist/src/modules/v4/programPayload/programPayload.service.d.ts +5 -4
- package/dist/src/modules/v4/programPayload/programPayload.service.js +24 -10
- package/dist/src/modules/v4/router.d.ts +7 -6
- package/dist/src/modules/v4/status/status.repository.js +1 -0
- package/dist/src/modules/v4/token/token.controller.d.ts +2 -1
- package/dist/src/modules/v4/token/token.repository.d.ts +15 -1
- package/dist/src/modules/v4/token/token.service.d.ts +19 -5
- package/dist/src/modules/v4/token/token.service.js +7 -10
- package/dist/src/modules/v4/uniswapV4/uniswapV4.controller.d.ts +2 -2
- package/dist/src/modules/v4/uniswapV4/uniswapV4.model.d.ts +9 -0
- package/dist/src/modules/v4/uniswapV4/uniswapV4.repository.d.ts +11 -0
- package/dist/src/modules/v4/uniswapV4/uniswapV4.repository.js +10 -0
- package/dist/src/modules/v4/uniswapV4/uniswapV4.service.d.ts +4 -2
- package/dist/src/modules/v4/uniswapV4/uniswapV4.service.js +175 -1
- package/dist/src/utils/prices/services/coinGeckoService.js +3 -3
- package/dist/src/utils/prices/services/defillamaService.js +3 -3
- package/dist/src/utils/prices/services/erc4626Service.js +6 -4
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +11 -8
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.controller.d.ts +0 -34
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.controller.js +0 -8
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.model.d.ts +0 -0
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.model.js +0 -1
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.repository.d.ts +0 -5
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.repository.js +0 -71
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.service.d.ts +0 -3
- package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.service.js +0 -15
- /package/dist/src/jobs/{etl/dynamic-data.d.ts → dynamic-data.d.ts} +0 -0
- /package/dist/src/jobs/{etl/pendings.d.ts → pendings.d.ts} +0 -0
- /package/dist/src/jobs/{etl/pendings.js → pendings.js} +0 -0
- /package/dist/src/jobs/{etl/prices.d.ts → prices.d.ts} +0 -0
- /package/dist/src/jobs/{etl/prices.js → prices.js} +0 -0
- /package/dist/src/jobs/{etl/reward-breakdowns.d.ts → reward-breakdowns.d.ts} +0 -0
- /package/dist/src/jobs/{etl/reward-breakdowns.js → reward-breakdowns.js} +0 -0
- /package/dist/src/jobs/{etl/rewards.d.ts → rewards.d.ts} +0 -0
- /package/dist/src/jobs/{etl/rewards.js → rewards.js} +0 -0
- /package/dist/src/jobs/{etl/update-dynamic-data.d.ts → set-dungeon-keeper.d.ts} +0 -0
- /package/dist/src/jobs/{etl/update-dynamic-data.js → update-dynamic-data.js} +0 -0
- /package/dist/src/jobs/{etl/update-euler-vaults.d.ts → update-euler-vaults.d.ts} +0 -0
- /package/dist/src/jobs/{etl/update-euler-vaults.js → update-euler-vaults.js} +0 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
import { type MerklChainId } from "@sdk";
|
2
|
+
import type { UniswapV4PoolsReturnType } from "./uniswapV4.model";
|
2
3
|
export declare const PoolManagerInterface: any;
|
3
4
|
export declare abstract class UniswapV4Service {
|
4
5
|
static getPoolsByChain(chainId: MerklChainId): Promise<{
|
@@ -6,10 +7,10 @@ export declare abstract class UniswapV4Service {
|
|
6
7
|
poolId: string;
|
7
8
|
poolKey: {
|
8
9
|
hooks: string;
|
9
|
-
fee: number;
|
10
|
-
tickSpacing: number;
|
11
10
|
currency0: string;
|
12
11
|
currency1: string;
|
12
|
+
fee: number;
|
13
|
+
tickSpacing: number;
|
13
14
|
};
|
14
15
|
decimalsCurrency0: number;
|
15
16
|
decimalsCurrency1: number;
|
@@ -17,4 +18,5 @@ export declare abstract class UniswapV4Service {
|
|
17
18
|
symbolCurrency1: string;
|
18
19
|
};
|
19
20
|
}>;
|
21
|
+
static getPools(): Promise<UniswapV4PoolsReturnType>;
|
20
22
|
}
|
@@ -1,8 +1,11 @@
|
|
1
1
|
import { safeFetchLogs } from "@/libs/campaigns/utils/fetchLogs";
|
2
2
|
import { batchMulticallCallWithRetry } from "@/utils/generic";
|
3
|
+
import { log } from "@/utils/logger";
|
3
4
|
import { providers } from "@/utils/providers";
|
4
|
-
import {
|
5
|
+
import { LoggedEntityType } from "@db/api";
|
6
|
+
import { ChainInteractionService, ERC20Interface, NETWORK_LABELS, NULL_ADDRESS, UniswapV4Addresses, UniswapV4PoolManagerInterface, UniswapV4PoolManager__factory, getContractCreationBlock, } from "@sdk";
|
5
7
|
import { utils } from "ethers";
|
8
|
+
import { UniswapV4Repository } from "./uniswapV4.repository";
|
6
9
|
export const PoolManagerInterface = UniswapV4PoolManager__factory.createInterface();
|
7
10
|
export class UniswapV4Service {
|
8
11
|
static async getPoolsByChain(chainId) {
|
@@ -70,4 +73,175 @@ export class UniswapV4Service {
|
|
70
73
|
}
|
71
74
|
return res;
|
72
75
|
}
|
76
|
+
static async getPools() {
|
77
|
+
const UNIV4_CHAINIDS = Object.keys(UniswapV4Addresses).map((x) => Number(x));
|
78
|
+
const pools = {};
|
79
|
+
// 0_ Fetch all euler vaults from database
|
80
|
+
const storedPools = await UniswapV4Repository.getStoredPools();
|
81
|
+
const res = await Promise.all(UNIV4_CHAINIDS.map(async (chainId) => {
|
82
|
+
chainId = chainId;
|
83
|
+
const perChainIdRes = {};
|
84
|
+
const poolManagerAddress = UniswapV4Addresses[chainId]?.PoolManager ?? NULL_ADDRESS;
|
85
|
+
const jsonRPCprovider = providers[chainId];
|
86
|
+
try {
|
87
|
+
// 1_ Get latest euler vaults from chain
|
88
|
+
const storedPoolsPerChain = storedPools.filter(pool => pool.chainId === chainId);
|
89
|
+
log.info(`found ${storedPoolsPerChain.length} already stored pools on ${NETWORK_LABELS[chainId]}`);
|
90
|
+
let fromBlock;
|
91
|
+
if (storedPoolsPerChain.length > 0) {
|
92
|
+
fromBlock = Math.max(...storedPoolsPerChain.map(x => x.fetchAtBlock)) + 1;
|
93
|
+
}
|
94
|
+
else {
|
95
|
+
fromBlock = await getContractCreationBlock(poolManagerAddress, jsonRPCprovider);
|
96
|
+
}
|
97
|
+
const toBlock = await jsonRPCprovider.getBlockNumber();
|
98
|
+
const logs = await safeFetchLogs(chainId, // TODO: rm type enforcing
|
99
|
+
[UniswapV4PoolManagerInterface.getEventTopic("Initialize")], [poolManagerAddress], fromBlock, toBlock
|
100
|
+
// fromBlock + 10_000
|
101
|
+
);
|
102
|
+
const decodedPools = await Promise.all(logs.map(async (log) => {
|
103
|
+
const [id, currency0, currency1, fee, tickSpacing, hooks] = UniswapV4PoolManagerInterface.decodeEventLog("Initialize", log.data, log.topics);
|
104
|
+
// Respect typing
|
105
|
+
return {
|
106
|
+
poolId: id,
|
107
|
+
chainId: chainId,
|
108
|
+
currency0: currency0,
|
109
|
+
currency1: currency1,
|
110
|
+
tickSpacing: tickSpacing,
|
111
|
+
lpFee: fee,
|
112
|
+
hooks: hooks,
|
113
|
+
fetchedAtBlock: Number(log.blockNumber),
|
114
|
+
};
|
115
|
+
}));
|
116
|
+
log.local(`fetched ${decodedPools.length} pool(s) on ${NETWORK_LABELS[chainId] ?? "Sepolia"} between blocks ${fromBlock} and ${toBlock}`);
|
117
|
+
/** Extra calls batch to get the collateral addresses */
|
118
|
+
const resCurrencies = await ChainInteractionService(chainId).fetchState(decodedPools.flatMap(pool => {
|
119
|
+
return [
|
120
|
+
{
|
121
|
+
allowFailure: true,
|
122
|
+
callData: ERC20Interface.encodeFunctionData("symbol"),
|
123
|
+
target: pool.currency0,
|
124
|
+
},
|
125
|
+
{
|
126
|
+
allowFailure: true,
|
127
|
+
callData: ERC20Interface.encodeFunctionData("decimals"),
|
128
|
+
target: pool.currency0,
|
129
|
+
},
|
130
|
+
{
|
131
|
+
allowFailure: true,
|
132
|
+
callData: ERC20Interface.encodeFunctionData("symbol"),
|
133
|
+
target: pool.currency1,
|
134
|
+
},
|
135
|
+
{
|
136
|
+
allowFailure: true,
|
137
|
+
callData: ERC20Interface.encodeFunctionData("decimals"),
|
138
|
+
target: pool.currency1,
|
139
|
+
},
|
140
|
+
].filter(x => x.target !== NULL_ADDRESS);
|
141
|
+
}));
|
142
|
+
let index = 0;
|
143
|
+
for (const pool of decodedPools) {
|
144
|
+
let symbolCurrency0 = "UNKNOWN";
|
145
|
+
let decimalsCurrency0 = 18;
|
146
|
+
if (pool.currency0 !== NULL_ADDRESS) {
|
147
|
+
try {
|
148
|
+
symbolCurrency0 = ERC20Interface.decodeFunctionResult("symbol", resCurrencies[index].returnData)[0];
|
149
|
+
if (symbolCurrency0.includes("/") || symbolCurrency0.includes("\u0000")) {
|
150
|
+
symbolCurrency0 = "INVALID";
|
151
|
+
}
|
152
|
+
decimalsCurrency0 = ERC20Interface.decodeFunctionResult("decimals", resCurrencies[index + 1].returnData)[0];
|
153
|
+
}
|
154
|
+
catch {
|
155
|
+
log.error("getUniswapV4Pools", `issue when fetching symbol / decimals for currency0 ${pool.currency0} of pool ${pool.poolId} on ${NETWORK_LABELS[chainId]}`);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
else {
|
159
|
+
symbolCurrency0 = "ETH";
|
160
|
+
decimalsCurrency0 = 18;
|
161
|
+
index -= 2;
|
162
|
+
}
|
163
|
+
let symbolCurrency1 = "UNKNOWN";
|
164
|
+
let decimalsCurrency1 = 18;
|
165
|
+
if (pool.currency1 !== NULL_ADDRESS) {
|
166
|
+
try {
|
167
|
+
symbolCurrency1 = ERC20Interface.decodeFunctionResult("symbol", resCurrencies[index + 2].returnData)[0];
|
168
|
+
if (symbolCurrency1.includes("/") || symbolCurrency1.includes("\u0000")) {
|
169
|
+
symbolCurrency1 = "INVALID";
|
170
|
+
}
|
171
|
+
decimalsCurrency1 = ERC20Interface.decodeFunctionResult("decimals", resCurrencies[index + 3].returnData)[0];
|
172
|
+
}
|
173
|
+
catch {
|
174
|
+
log.error("getUniswapV4Pools", `issue when fetching symbol / decimals for currency1 ${pool.currency1} of pool ${pool.poolId} on ${NETWORK_LABELS[chainId]}`);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
else {
|
178
|
+
symbolCurrency1 = "ETH";
|
179
|
+
decimalsCurrency1 = 18;
|
180
|
+
index -= 2;
|
181
|
+
}
|
182
|
+
const id = pool.poolId;
|
183
|
+
perChainIdRes[id ?? "unknownKey"] = {
|
184
|
+
chainId: pool.chainId,
|
185
|
+
currency0: pool.currency0,
|
186
|
+
currency1: pool.currency1,
|
187
|
+
decimalsCurrency0,
|
188
|
+
decimalsCurrency1,
|
189
|
+
hooks: pool.hooks,
|
190
|
+
fetchedAtBlock: pool.fetchedAtBlock,
|
191
|
+
lpFee: pool.lpFee,
|
192
|
+
poolId: id,
|
193
|
+
poolKey: pool.poolKey,
|
194
|
+
symbolCurrency0,
|
195
|
+
symbolCurrency1,
|
196
|
+
tickSpacing: pool.tickSpacing,
|
197
|
+
};
|
198
|
+
index += 4;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
catch (e) {
|
202
|
+
log.error(`issue when fetching UniswapV4 pools on ${NETWORK_LABELS[chainId]}`, e);
|
203
|
+
}
|
204
|
+
return perChainIdRes;
|
205
|
+
}));
|
206
|
+
UNIV4_CHAINIDS.forEach((chainId, i) => {
|
207
|
+
if (!!res[i])
|
208
|
+
pools[chainId] = res[i];
|
209
|
+
});
|
210
|
+
// Update the API database
|
211
|
+
const tableData = Object.values(pools).flatMap(pools => Object.values(pools));
|
212
|
+
for (const chainId of UNIV4_CHAINIDS) {
|
213
|
+
if (tableData.filter(p => p.chainId === chainId).length > 0) {
|
214
|
+
try {
|
215
|
+
await UniswapV4Repository.createMany(tableData
|
216
|
+
.filter(point => point.chainId === chainId)
|
217
|
+
.map(pool => ({
|
218
|
+
fetchAtBlock: pool.fetchedAtBlock,
|
219
|
+
caughtFromAddress: UniswapV4Addresses[pool.chainId]?.PoolManager ?? NULL_ADDRESS,
|
220
|
+
chainId: pool.chainId,
|
221
|
+
entityData: pool,
|
222
|
+
id: Bun.hash(`${pool.poolId}-${pool.chainId}`).toString(),
|
223
|
+
type: LoggedEntityType.UNISWAP_V4,
|
224
|
+
})));
|
225
|
+
log.info(`✅ successfully saved vaults to API database ('Logged' table) on ${NETWORK_LABELS[chainId]}`);
|
226
|
+
}
|
227
|
+
catch (e) {
|
228
|
+
log.error("getUniswapV4Pools/LoggedTableUpdate", e);
|
229
|
+
throw new Error(`Error while saving UniV4 pools to API database ('Logged' table) on ${NETWORK_LABELS[chainId]}`);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
log.info(`✅ successfully fetched ${tableData.length} new pool(s) on UniswapV4`);
|
234
|
+
// _ Merge previoulsy stored pools with newly fetched ones
|
235
|
+
// TODO optimize this part
|
236
|
+
if (storedPools.length > 0) {
|
237
|
+
for (const pool of storedPools) {
|
238
|
+
const chainId = pool.chainId;
|
239
|
+
if (!pools[chainId])
|
240
|
+
pools[chainId] = {};
|
241
|
+
pools[chainId][pool.entityData.poolId] = pool.entityData;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
log.info("👋 exiting getUniswapV4Pools");
|
245
|
+
return pools;
|
246
|
+
}
|
73
247
|
}
|
@@ -28,7 +28,7 @@ export default class CoingeckoService {
|
|
28
28
|
for (let i = 0; i < tickers.length; i += chunkSize) {
|
29
29
|
chunks.push(tickers.slice(i, Math.min(tickers.length, i + chunkSize)));
|
30
30
|
}
|
31
|
-
const promises = await Promise.all(chunks.map(async (chunk) => {
|
31
|
+
const promises = (await Promise.all(chunks.map(async (chunk) => {
|
32
32
|
const url = this.buildUrl(chunk);
|
33
33
|
return axios
|
34
34
|
.get(url, { timeout: 10000 })
|
@@ -37,7 +37,7 @@ export default class CoingeckoService {
|
|
37
37
|
log.error("❌ CoingeckoService not responding", err);
|
38
38
|
throw "❌ CoingeckoService not responding";
|
39
39
|
});
|
40
|
-
}));
|
40
|
+
}))).filter(result => !!result);
|
41
41
|
return promises.reduce((acc, val) => acc.concat(val), []);
|
42
42
|
}
|
43
43
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation> Need to be typed according to coinguekko answer type
|
@@ -56,6 +56,6 @@ export default class CoingeckoService {
|
|
56
56
|
rate: data.data.coins[`coingecko:${args.ticker}`].price ?? -1,
|
57
57
|
};
|
58
58
|
})
|
59
|
-
.filter(item => item
|
59
|
+
.filter(item => !!item);
|
60
60
|
}
|
61
61
|
}
|
@@ -38,16 +38,16 @@ export default class DefillamaService {
|
|
38
38
|
.map(token => {
|
39
39
|
const args = token.args;
|
40
40
|
if (!args?.address || !args?.chain)
|
41
|
-
return;
|
41
|
+
return undefined;
|
42
42
|
if (!data.data.coins[`${args?.chain}:${args.address}`]?.price) {
|
43
43
|
log.warn(`❌ DefillamaService data failed for ${token.symbol} with address ${args.address}`);
|
44
|
-
return;
|
44
|
+
return undefined;
|
45
45
|
}
|
46
46
|
return {
|
47
47
|
token: token.symbol,
|
48
48
|
rate: data.data.coins[`${args?.chain}:${args.address}`].price ?? -1,
|
49
49
|
};
|
50
50
|
})
|
51
|
-
.filter(item => item
|
51
|
+
.filter(item => !!item);
|
52
52
|
}
|
53
53
|
}
|
@@ -10,8 +10,10 @@ export default class ERC4626Service {
|
|
10
10
|
.filter(tokenPriceSource => tokenPriceSource.method === PriceSourceMethod.ERC4626)
|
11
11
|
.map(async (token) => {
|
12
12
|
const args = token.args;
|
13
|
-
if (!args?.chainId || !args.address || !args.vaultToken)
|
14
|
-
|
13
|
+
if (!args?.chainId || !args.address || !args.vaultToken) {
|
14
|
+
log.warn(`❌ ERC4626Service ticker not found for ${token.symbol}`);
|
15
|
+
return undefined;
|
16
|
+
}
|
15
17
|
const rate = await getERC4626Price(args.chainId, args.address, args.decimals, args.vaultDecimals);
|
16
18
|
// 2 returned tokens as stUSD and STUSD (business requirement)
|
17
19
|
const price = await this.priceCalculation(rate, args.vaultToken);
|
@@ -21,8 +23,8 @@ export default class ERC4626Service {
|
|
21
23
|
];
|
22
24
|
return res;
|
23
25
|
});
|
24
|
-
const resolvedPrices = await Promise.all(pricePromises);
|
25
|
-
return resolvedPrices.flat()
|
26
|
+
const resolvedPrices = (await Promise.all(pricePromises)).filter(result => !!result);
|
27
|
+
return resolvedPrices.flat();
|
26
28
|
}
|
27
29
|
async priceCalculation(rate, vaultToken) {
|
28
30
|
try {
|