@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.
Files changed (68) hide show
  1. package/dist/database/api/.generated/drizzle/schema.d.ts +2 -2
  2. package/dist/database/api/.generated/drizzle/schema.js +2 -2
  3. package/dist/database/api/.generated/drizzle/schema.ts +2 -2
  4. package/dist/database/api/.generated/edge.js +3 -3
  5. package/dist/database/api/.generated/index.d.ts +12 -12
  6. package/dist/database/api/.generated/index.js +3 -3
  7. package/dist/database/api/.generated/package.json +1 -1
  8. package/dist/database/api/.generated/schema.prisma +2 -2
  9. package/dist/src/backgroundJobs/index.js +0 -4
  10. package/dist/src/eden/index.d.ts +35 -30
  11. package/dist/src/factories/opportunityMetadata/implementations/EventBased.d.ts +1 -1
  12. package/dist/src/index.d.ts +7 -6
  13. package/dist/src/jobs/{etl/dynamic-data.js → dynamic-data.js} +1 -1
  14. package/dist/src/jobs/set-dungeon-keeper.js +82 -0
  15. package/dist/src/jobs/update-dynamic-data.d.ts +1 -0
  16. package/dist/src/jobs/update-uniswap-v4-pools.d.ts +1 -0
  17. package/dist/src/jobs/update-uniswap-v4-pools.js +14 -0
  18. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/helpers/hardcoded.js +2 -4
  19. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/implementations/HourglassProcessor.d.ts +1 -1
  20. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/implementations/HourglassProcessor.js +3 -0
  21. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/implementations/SpliceProcessor.js +1 -0
  22. package/dist/src/libs/campaigns/campaignTypes/SILODynamicData.js +2 -2
  23. package/dist/src/libs/campaigns/utils/fetchAmbientInfo.d.ts +2 -2
  24. package/dist/src/libs/campaigns/utils/getDolomiteMarkets.js +1 -1
  25. package/dist/src/libs/positions/dolomite/index.js +2 -0
  26. package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +5 -5
  27. package/dist/src/modules/v4/programPayload/programPayload.repository.d.ts +52 -1
  28. package/dist/src/modules/v4/programPayload/programPayload.repository.js +48 -13
  29. package/dist/src/modules/v4/programPayload/programPayload.service.d.ts +5 -4
  30. package/dist/src/modules/v4/programPayload/programPayload.service.js +24 -10
  31. package/dist/src/modules/v4/router.d.ts +7 -6
  32. package/dist/src/modules/v4/status/status.repository.js +1 -0
  33. package/dist/src/modules/v4/token/token.controller.d.ts +2 -1
  34. package/dist/src/modules/v4/token/token.repository.d.ts +15 -1
  35. package/dist/src/modules/v4/token/token.service.d.ts +19 -5
  36. package/dist/src/modules/v4/token/token.service.js +7 -10
  37. package/dist/src/modules/v4/uniswapV4/uniswapV4.controller.d.ts +2 -2
  38. package/dist/src/modules/v4/uniswapV4/uniswapV4.model.d.ts +9 -0
  39. package/dist/src/modules/v4/uniswapV4/uniswapV4.repository.d.ts +11 -0
  40. package/dist/src/modules/v4/uniswapV4/uniswapV4.repository.js +10 -0
  41. package/dist/src/modules/v4/uniswapV4/uniswapV4.service.d.ts +4 -2
  42. package/dist/src/modules/v4/uniswapV4/uniswapV4.service.js +175 -1
  43. package/dist/src/utils/prices/services/coinGeckoService.js +3 -3
  44. package/dist/src/utils/prices/services/defillamaService.js +3 -3
  45. package/dist/src/utils/prices/services/erc4626Service.js +6 -4
  46. package/dist/tsconfig.package.tsbuildinfo +1 -1
  47. package/package.json +11 -8
  48. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.controller.d.ts +0 -34
  49. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.controller.js +0 -8
  50. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.model.d.ts +0 -0
  51. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.model.js +0 -1
  52. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.repository.d.ts +0 -5
  53. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.repository.js +0 -71
  54. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.service.d.ts +0 -3
  55. package/dist/src/modules/v4/dungeonKeeper/dungeonKeeper.service.js +0 -15
  56. /package/dist/src/jobs/{etl/dynamic-data.d.ts → dynamic-data.d.ts} +0 -0
  57. /package/dist/src/jobs/{etl/pendings.d.ts → pendings.d.ts} +0 -0
  58. /package/dist/src/jobs/{etl/pendings.js → pendings.js} +0 -0
  59. /package/dist/src/jobs/{etl/prices.d.ts → prices.d.ts} +0 -0
  60. /package/dist/src/jobs/{etl/prices.js → prices.js} +0 -0
  61. /package/dist/src/jobs/{etl/reward-breakdowns.d.ts → reward-breakdowns.d.ts} +0 -0
  62. /package/dist/src/jobs/{etl/reward-breakdowns.js → reward-breakdowns.js} +0 -0
  63. /package/dist/src/jobs/{etl/rewards.d.ts → rewards.d.ts} +0 -0
  64. /package/dist/src/jobs/{etl/rewards.js → rewards.js} +0 -0
  65. /package/dist/src/jobs/{etl/update-dynamic-data.d.ts → set-dungeon-keeper.d.ts} +0 -0
  66. /package/dist/src/jobs/{etl/update-dynamic-data.js → update-dynamic-data.js} +0 -0
  67. /package/dist/src/jobs/{etl/update-euler-vaults.d.ts → update-euler-vaults.d.ts} +0 -0
  68. /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 { ERC20Interface, UniswapV4PoolManager__factory } from "@sdk";
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 !== undefined);
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 !== undefined);
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
- return log.warn(`❌ ERC4626Service ticker not found for ${token.symbol}`);
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().filter(price => price !== undefined && price !== null);
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 {