@merkl/api 0.10.167 → 0.10.169

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. package/dist/src/cache/declaration.d.ts +0 -30
  2. package/dist/src/cache/declaration.js +0 -30
  3. package/dist/src/eden/index.d.ts +59 -12
  4. package/dist/src/index.d.ts +23 -4
  5. package/dist/src/libs/campaigns/campaignTypes/CLAMMDynamicData.js +2 -2
  6. package/dist/src/libs/positions/clamm/index.d.ts +1 -2
  7. package/dist/src/libs/positions/clamm/index.js +322 -330
  8. package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.d.ts +1 -1
  9. package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.js +1 -1
  10. package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.d.ts +1 -1
  11. package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.js +2 -2
  12. package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.d.ts +1 -2
  13. package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.js +1 -4
  14. package/dist/src/libs/positions/euler/index.js +2 -3
  15. package/dist/src/libs/positions/index.js +1 -1
  16. package/dist/src/modules/v4/campaign/campaign.controller.d.ts +2 -2
  17. package/dist/src/modules/v4/campaign/campaign.service.d.ts +6 -5
  18. package/dist/src/modules/v4/campaign/campaign.service.js +10 -2
  19. package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +2 -2
  20. package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +159 -0
  21. package/dist/src/modules/v4/opportunity/opportunity.repository.js +29 -0
  22. package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +131 -3
  23. package/dist/src/modules/v4/opportunity/opportunity.service.js +11 -0
  24. package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.d.ts +6 -0
  25. package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.js +90 -0
  26. package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.d.ts +6 -0
  27. package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.js +69 -0
  28. package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.d.ts +6 -0
  29. package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.js +71 -0
  30. package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.d.ts +6 -0
  31. package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.js +45 -0
  32. package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.d.ts +6 -0
  33. package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.js +47 -0
  34. package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.d.ts +6 -0
  35. package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.js +40 -0
  36. package/dist/src/modules/v4/position/index.d.ts +2 -0
  37. package/dist/src/modules/v4/position/index.js +2 -0
  38. package/dist/src/modules/v4/position/position.controller.d.ts +39 -0
  39. package/dist/src/modules/v4/position/position.controller.js +16 -0
  40. package/dist/src/modules/v4/position/position.model.d.ts +25 -0
  41. package/dist/src/modules/v4/position/position.model.js +5 -0
  42. package/dist/src/modules/v4/position/position.repository.d.ts +14 -0
  43. package/dist/src/modules/v4/position/position.repository.js +6 -0
  44. package/dist/src/modules/v4/position/position.service.d.ts +5 -0
  45. package/dist/src/modules/v4/position/position.service.js +34 -0
  46. package/dist/src/modules/v4/reward/reward.service.d.ts +5 -0
  47. package/dist/src/modules/v4/reward/reward.service.js +5 -2
  48. package/dist/src/modules/v4/router.d.ts +23 -4
  49. package/dist/src/modules/v4/router.js +3 -1
  50. package/dist/src/routes/v1/prices.js +2 -4
  51. package/dist/src/routes/v3/blacklist.d.ts +23 -4
  52. package/dist/src/routes/v3/campaigns.d.ts +24 -5
  53. package/dist/src/routes/v3/campaignsInfo.d.ts +23 -4
  54. package/dist/src/routes/v3/multiChainPositions.d.ts +23 -4
  55. package/dist/src/routes/v3/opportunity.d.ts +23 -4
  56. package/dist/src/routes/v3/positions.d.ts +23 -4
  57. package/dist/src/routes/v3/recipients.d.ts +6 -2
  58. package/dist/src/routes/v3/recipients.js +14 -8
  59. package/dist/src/routes/v3/rewards.d.ts +23 -4
  60. package/dist/src/routes/v3/updates.d.ts +23 -4
  61. package/dist/src/routes/v3/userRewards.d.ts +23 -4
  62. package/dist/src/utils/prices/services/erc4626Service.js +4 -4
  63. package/dist/tsconfig.package.tsbuildinfo +1 -1
  64. package/package.json +1 -1
  65. package/dist/src/libs/reports/campaignReport.d.ts +0 -9
  66. package/dist/src/libs/reports/campaignReport.js +0 -37
  67. package/dist/src/libs/reports/mainParameterRewards.d.ts +0 -3
  68. package/dist/src/libs/reports/mainParameterRewards.js +0 -48
@@ -1,20 +1,23 @@
1
+ import { CacheService } from "../../../modules/v4/cache";
2
+ import { TTLPresets } from "../../../modules/v4/cache/cache.model";
3
+ import { ChainInteractionService } from "../../../modules/v4/chainInteraction";
1
4
  import { ALM, AMMAlgorithmMapping, BN2Number, NETWORK_LABELS, NFTManagerAddress, NonFungiblePositionManagerInterface, PoolInterface, PoolState, SqrtPrice, YEAR, getAmountsForLiquidity, getSupportedNFPWrapperMapping, getTickAtSqrtRatio, spNFTInterface, } from "@sdk";
2
5
  import { BigNumber, Contract, utils } from "ethers";
3
6
  import JSBI from "jsbi";
4
7
  import { log } from "../../../utils/logger";
5
8
  import { Pricer } from "../../../utils/pricer";
6
9
  import { providers } from "../../../utils/providers";
7
- import { fetchAlmPositionsV2 } from "./thegraph/fetchAlmPositions";
8
- import { fetchAmmPositionsV2 } from "./thegraph/fetchAmmPositions";
9
- import { fetchFarmedPositionsV2WithCache } from "./thegraph/fetchFarmedPositions";
10
- export async function getClammUserPositions(user, chainId, poolsByAmm) {
10
+ import { fetchAlmPositions } from "./thegraph/fetchAlmPositions";
11
+ import { fetchAmmPositions } from "./thegraph/fetchAmmPositions";
12
+ import { fetchFarmedPositions } from "./thegraph/fetchFarmedPositions";
13
+ export async function getClammUserPositions(user, chainId, poolsByAmm, withIndividualAPRs = true) {
11
14
  const pricer = await Pricer.load();
12
15
  /**
13
16
  * Fetch user positions
14
17
  */
15
18
  const promisePositions = [
16
- fetchAmmPositionsV2(chainId, user, (!!poolsByAmm ? Object.keys(poolsByAmm) : [])),
17
- fetchAlmPositionsV2(chainId, user),
19
+ fetchAmmPositions(chainId, user, (!!poolsByAmm ? Object.keys(poolsByAmm) : []).map(x => Number(x))),
20
+ fetchAlmPositions(chainId, user),
18
21
  ];
19
22
  /**
20
23
  * Per pool mapping to store all ids of positions that were wrapped into a farm
@@ -44,7 +47,7 @@ export async function getClammUserPositions(user, chainId, poolsByAmm) {
44
47
  startingIndex++;
45
48
  }
46
49
  if (getSupportedNFPWrapperMapping(chainId, amm).length > 0) {
47
- farmedTokenIds[amm] = (await fetchFarmedPositionsV2WithCache(chainId, [user], Number(amm)));
50
+ farmedTokenIds[amm] = await CacheService.wrap(TTLPresets.MIN_5, fetchFarmedPositions, chainId, [user], Number(amm));
48
51
  }
49
52
  for (const wrapper of getSupportedNFPWrapperMapping(chainId, amm)) {
50
53
  if (!!farmedTokenIds &&
@@ -69,351 +72,340 @@ export async function getClammUserPositions(user, chainId, poolsByAmm) {
69
72
  }
70
73
  }
71
74
  }
72
- // Calls
73
- // all pool calls
74
- // all NonFungiblePositionManagerInterface calls by wrapper
75
- return {
76
- cached: false,
77
- call: {
78
- callData: calls,
79
- handler: () => { },
80
- reducer: async (result) => {
81
- const [promisePosition, promiseALMPosition] = await Promise.all(promisePositions);
82
- const positions = Object.values((await Promise.resolve(promisePosition))).reduce((acc, val) => {
83
- return acc.concat(val.nft).concat(val.direct);
84
- }, []);
85
- /** Fill user positions */
86
- const finalRes = {};
87
- if (!!poolsByAmm) {
88
- for (const [amm, poolDatas] of Object.entries(poolsByAmm)) {
89
- const pools = Object.keys(poolDatas);
90
- let i = callsByAmm[amm].start;
91
- for (const p of pools) {
92
- try {
93
- const res = {
94
- userPositions: [],
95
- apr: {},
96
- };
97
- const updateUserPosition = (x) => {
98
- res.userPositions?.push(x);
99
- // Update global data logic
100
- res.userTVL += x.tvl ?? 0;
101
- res.userBalanceToken0 += x.balance0 ?? 0;
102
- res.userBalanceToken1 += x.balance1 ?? 0;
103
- res.userTotalLiquidity += x.totalLiquidity ?? 0;
104
- res.userInRangeLiquidity += x.inRangeLiquidity ?? 0;
105
- };
106
- const sqrtPriceX96 = PoolInterface[AMMAlgorithmMapping[amm]]
107
- .decodeFunctionResult(PoolState[AMMAlgorithmMapping[amm]], result[i++])[SqrtPrice[AMMAlgorithmMapping[amm]]].toString();
108
- res.tick = getTickAtSqrtRatio(JSBI.BigInt(sqrtPriceX96));
109
- res.userTVL = 0;
110
- res.userBalanceToken0 = 0;
111
- res.userBalanceToken1 = 0;
112
- res.userTotalLiquidity = 0;
113
- res.userInRangeLiquidity = 0;
114
- const priceToken0 = (await pricer.get({
115
- address: poolDatas[p].token0,
116
- chainId,
117
- symbol: poolDatas[p].symbolToken0,
118
- }));
119
- const priceToken1 = (await pricer.get({
120
- address: poolDatas[p].token1,
121
- chainId,
122
- symbol: poolDatas[p].symbolToken1,
123
- }));
124
- /** 1_ User Liquidity Positions */
125
- const userNonALMPositions = positions?.filter((x) => utils.getAddress(x.pool.id) === utils.getAddress(p));
126
- for (const pos of userNonALMPositions) {
75
+ const result = await ChainInteractionService(chainId).fetchState(calls);
76
+ const [promisePosition, promiseALMPosition] = await Promise.all(promisePositions);
77
+ const positions = Object.values((await Promise.resolve(promisePosition))).reduce((acc, val) => {
78
+ return acc.concat(val.nft).concat(val.direct);
79
+ }, []);
80
+ /** Fill user positions */
81
+ const finalRes = {};
82
+ if (!!poolsByAmm) {
83
+ for (const [amm, poolDatas] of Object.entries(poolsByAmm)) {
84
+ const pools = Object.keys(poolDatas);
85
+ let i = callsByAmm[amm].start;
86
+ for (const p of pools) {
87
+ try {
88
+ const res = {
89
+ userPositions: [],
90
+ apr: {},
91
+ userTVL: 0,
92
+ userBalanceToken0: 0,
93
+ userBalanceToken1: 0,
94
+ userTotalLiquidity: 0,
95
+ userInRangeLiquidity: 0,
96
+ };
97
+ const updateUserPosition = (x) => {
98
+ res.userPositions?.push(x);
99
+ // Update global data logic
100
+ res.userTVL += x.tvl ?? 0;
101
+ res.userBalanceToken0 += x.balance0 ?? 0;
102
+ res.userBalanceToken1 += x.balance1 ?? 0;
103
+ res.userTotalLiquidity += x.totalLiquidity ?? 0;
104
+ res.userInRangeLiquidity += x.inRangeLiquidity ?? 0;
105
+ };
106
+ const sqrtPriceX96 = PoolInterface[AMMAlgorithmMapping[amm]]
107
+ .decodeFunctionResult(PoolState[AMMAlgorithmMapping[amm]], result[i++].returnData)[SqrtPrice[AMMAlgorithmMapping[amm]]].toString();
108
+ res.tick = getTickAtSqrtRatio(JSBI.BigInt(sqrtPriceX96));
109
+ res.userTVL = 0;
110
+ res.userBalanceToken0 = 0;
111
+ res.userBalanceToken1 = 0;
112
+ res.userTotalLiquidity = 0;
113
+ res.userInRangeLiquidity = 0;
114
+ const priceToken0 = (await pricer.get({
115
+ address: poolDatas[p].token0,
116
+ chainId,
117
+ symbol: poolDatas[p].symbolToken0,
118
+ }));
119
+ const priceToken1 = (await pricer.get({
120
+ address: poolDatas[p].token1,
121
+ chainId,
122
+ symbol: poolDatas[p].symbolToken1,
123
+ }));
124
+ /** 1_ User Liquidity Positions */
125
+ const userNonALMPositions = positions?.filter((x) => utils.getAddress(x.pool.id) === utils.getAddress(p));
126
+ for (const pos of userNonALMPositions) {
127
+ try {
128
+ const [amount0, amount1] = getAmountsForLiquidity(sqrtPriceX96, Number.parseInt(pos.tickLower), Number.parseInt(pos.tickUpper), BigNumber.from(pos.liquidity));
129
+ const balance0 = BN2Number(amount0, poolDatas[p].decimalsToken0);
130
+ const balance1 = BN2Number(amount1, poolDatas[p].decimalsToken1);
131
+ const tvl = balance0 * priceToken0 + balance1 * priceToken1;
132
+ updateUserPosition({
133
+ balance0,
134
+ balance1,
135
+ id: pos.id,
136
+ inRangeLiquidity: balance0 > 0 && balance1 > 0 ? BN2Number(pos.liquidity) : 0,
137
+ lowerTick: Number.parseInt(pos.tickLower),
138
+ origin: -1,
139
+ totalLiquidity: BN2Number(pos.liquidity),
140
+ tvl,
141
+ upperTick: Number.parseInt(pos.tickUpper),
142
+ });
143
+ }
144
+ catch {
145
+ log.local(`Merkl User Data: error handling positions for pool ${NETWORK_LABELS[chainId]}-${p}`);
146
+ }
147
+ }
148
+ /** 2_ User Positions through ALMs */
149
+ for (const almAddress of Object.keys(promiseALMPosition).filter(almAddress => !!poolDatas[p].forwarders[utils.getAddress(almAddress)])) {
150
+ try {
151
+ const almDetail = poolDatas[p].forwarders[utils.getAddress(almAddress)];
152
+ let balance;
153
+ if (almDetail.origin === ALM.spNFT) {
154
+ const tokenId = promiseALMPosition[almAddress];
155
+ balance = (await new Contract(almAddress, spNFTInterface, providers[chainId]).getStakingPosition(tokenId))[0];
156
+ }
157
+ else {
158
+ balance = promiseALMPosition[almAddress];
159
+ }
160
+ for (const pos of almDetail.positions) {
161
+ updateUserPosition({
162
+ almAddress: almAddress, // FIXME: typing is bypassed here
163
+ balance0: (BN2Number(balance) * pos?.balance0) / almDetail.totalSupply,
164
+ balance1: (BN2Number(balance) * pos?.balance1) / almDetail.totalSupply,
165
+ id: pos.id,
166
+ inRangeLiquidity: (BN2Number(balance) * pos?.inRangeLiquidity) / almDetail.totalSupply,
167
+ lowerTick: pos.lowerTick,
168
+ origin: almDetail.origin,
169
+ totalLiquidity: (BN2Number(balance) * pos?.totalLiquidity) / almDetail.totalSupply,
170
+ tvl: (BN2Number(balance) * pos?.tvl) / almDetail.totalSupply,
171
+ upperTick: pos.upperTick,
172
+ });
173
+ }
174
+ }
175
+ catch (_error) {
176
+ log.local(`Merkl User Data: error handling alm positions for pool ${NETWORK_LABELS[chainId]}-${p}`);
177
+ }
178
+ }
179
+ /** 3_ User positions that were deposited on non fungible positions wrapper contracts (eg. farms) */
180
+ let j = callsByAmm[amm].start + callsByAmm[amm].poolCalls;
181
+ if (getSupportedNFPWrapperMapping(chainId, amm).length > 0) {
182
+ const nonFungiblePositionManagerInterface = NonFungiblePositionManagerInterface[AMMAlgorithmMapping[amm]];
183
+ for (const wrapper of getSupportedNFPWrapperMapping(chainId, amm)) {
184
+ if (!!farmedTokenIds?.[amm] && farmedTokenIds?.[amm][wrapper].length > 0) {
185
+ for (let k = 0; k < farmedTokenIds[amm][wrapper].length; k++) {
186
+ if (farmedTokenIds[amm][wrapper][k].pool !== p.toLowerCase()) {
187
+ j++;
188
+ continue;
189
+ }
190
+ const posLiquidity = nonFungiblePositionManagerInterface.decodeFunctionResult("positions", result[j].returnData).liquidity;
191
+ const tickLower = Number.parseFloat(nonFungiblePositionManagerInterface
192
+ .decodeFunctionResult("positions", result[j].returnData)
193
+ .tickLower.toString());
194
+ const tickUpper = Number.parseFloat(nonFungiblePositionManagerInterface
195
+ .decodeFunctionResult("positions", result[j++].returnData)
196
+ .tickUpper.toString());
127
197
  try {
128
- const [amount0, amount1] = getAmountsForLiquidity(sqrtPriceX96, Number.parseInt(pos.tickLower), Number.parseInt(pos.tickUpper), BigNumber.from(pos.liquidity));
198
+ const [amount0, amount1] = getAmountsForLiquidity(sqrtPriceX96, tickLower, tickUpper, posLiquidity);
129
199
  const balance0 = BN2Number(amount0, poolDatas[p].decimalsToken0);
130
200
  const balance1 = BN2Number(amount1, poolDatas[p].decimalsToken1);
201
+ const priceToken0 = (await pricer.get({
202
+ address: poolDatas[p].token0,
203
+ chainId,
204
+ symbol: poolDatas[p].symbolToken0,
205
+ }));
206
+ const priceToken1 = (await pricer.get({
207
+ address: poolDatas[p].token1,
208
+ chainId,
209
+ symbol: poolDatas[p].symbolToken1,
210
+ }));
131
211
  const tvl = balance0 * priceToken0 + balance1 * priceToken1;
132
212
  updateUserPosition({
213
+ almAddress: farmedTokenIds[amm][wrapper][k].farmAddress,
133
214
  balance0,
134
215
  balance1,
135
- id: pos.id,
136
- inRangeLiquidity: balance0 > 0 && balance1 > 0 ? BN2Number(pos.liquidity) : 0,
137
- lowerTick: Number.parseInt(pos.tickLower),
138
- origin: -1,
139
- totalLiquidity: BN2Number(pos.liquidity),
216
+ id: farmedTokenIds[amm][wrapper][k].id,
217
+ inRangeLiquidity: balance0 > 0 && balance1 > 0 ? BN2Number(posLiquidity) : 0,
218
+ lowerTick: tickLower,
219
+ origin: -2 - Number.parseInt(String(wrapper)), // -2 - X means NFT Wrapper
220
+ totalLiquidity: BN2Number(posLiquidity),
140
221
  tvl,
141
- upperTick: Number.parseInt(pos.tickUpper),
222
+ upperTick: tickUpper,
142
223
  });
143
224
  }
144
- catch {
225
+ catch (_e) {
145
226
  log.local(`Merkl User Data: error handling positions for pool ${NETWORK_LABELS[chainId]}-${p}`);
146
227
  }
147
228
  }
148
- /** 2_ User Positions through ALMs */
149
- for (const almAddress of Object.keys(promiseALMPosition).filter(almAddress => !!poolDatas[p].forwarders[utils.getAddress(almAddress)])) {
150
- try {
151
- const almDetail = poolDatas[p].forwarders[utils.getAddress(almAddress)];
152
- let balance;
153
- if (almDetail.origin === ALM.spNFT) {
154
- const tokenId = promiseALMPosition[almAddress];
155
- balance = (await new Contract(almAddress, spNFTInterface, providers[chainId]).getStakingPosition(tokenId))[0];
156
- }
157
- else {
158
- balance = promiseALMPosition[almAddress];
229
+ }
230
+ }
231
+ }
232
+ if (withIndividualAPRs) {
233
+ /** Individual APRs */
234
+ let individualApr = 0;
235
+ const oneDistributionIsBoosted = false;
236
+ for (const campaignDatas of Object.values(poolDatas[p].campaigns)) {
237
+ const campaignData = campaignDatas;
238
+ let userBalance0 = res.userBalanceToken0;
239
+ let userBalance1 = res.userBalanceToken1;
240
+ let userInRangeLiquidity = res.userInRangeLiquidity;
241
+ let userTotalLiquidity = res.userTotalLiquidity;
242
+ const priceRewardToken = (await pricer.get({
243
+ address: campaignData.rewardToken,
244
+ chainId,
245
+ symbol: campaignData.symbolRewardToken,
246
+ }));
247
+ /** Handle whitelist to compute APR */
248
+ if (campaignData.whitelist.length > 0) {
249
+ const aux = campaignData.whitelist.reduce((acc, whitelistedAddress) => {
250
+ /** 1_ User is whitelisted */
251
+ if (user.toLowerCase() === whitelistedAddress.toLowerCase()) {
252
+ acc.balance0 += res.userBalanceToken0;
253
+ acc.balance1 += res.userBalanceToken1;
254
+ acc.inRangeLiquidity += res.userInRangeLiquidity;
255
+ acc.totalLiquidity += res.userTotalLiquidity;
256
+ }
257
+ else if (res.userPositions
258
+ .map((w) => (!!w?.almAddress ? w?.almAddress?.toLowerCase() : null))
259
+ ?.filter((a) => !!a)
260
+ .includes(whitelistedAddress.toLowerCase())) {
261
+ /**
262
+ * 2_ ALM is whitelisted and/or Nf positions vault is whitelisted
263
+ * @dev for simplicity purposes, vault address is marked as `almAddress`
264
+ */
265
+ let index = -1;
266
+ const positionsAddresses = res.userPositions.map((w) => w.almAddress?.toLowerCase());
267
+ while (true) {
268
+ const newIndex = positionsAddresses.slice(index + 1).indexOf(whitelistedAddress.toLowerCase());
269
+ if (newIndex === -1)
270
+ break;
271
+ index = index + 1 + newIndex;
272
+ acc.balance0 += res.userPositions[index].balance0;
273
+ acc.balance1 += res.userPositions[index].balance1;
274
+ acc.inRangeLiquidity += res.userPositions[index].inRangeLiquidity;
275
+ acc.totalLiquidity += res.userPositions[index].totalLiquidity;
159
276
  }
160
- for (const pos of almDetail.positions) {
161
- updateUserPosition({
162
- almAddress: almAddress, // FIXME: typing is bypassed here
163
- balance0: (BN2Number(balance) * pos?.balance0) / almDetail.totalSupply,
164
- balance1: (BN2Number(balance) * pos?.balance1) / almDetail.totalSupply,
165
- id: pos.id,
166
- inRangeLiquidity: (BN2Number(balance) * pos?.inRangeLiquidity) / almDetail.totalSupply,
167
- lowerTick: pos.lowerTick,
168
- origin: almDetail.origin,
169
- totalLiquidity: (BN2Number(balance) * pos?.totalLiquidity) / almDetail.totalSupply,
170
- tvl: (BN2Number(balance) * pos?.tvl) / almDetail.totalSupply,
171
- upperTick: pos.upperTick,
172
- });
277
+ }
278
+ else if (res.userPositions
279
+ .map((w) => !!w?.almAddress
280
+ ? poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target?.toLowerCase()
281
+ : null)
282
+ ?.filter((a) => !!a)
283
+ .includes(whitelistedAddress?.toLowerCase())) {
284
+ /** 3_ ALM target is whitelisted */
285
+ let index = -1;
286
+ const positionsAddresses = res.userPositions.map((w) => !!w?.almAddress &&
287
+ poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target.toLowerCase());
288
+ while (true) {
289
+ const newIndex = positionsAddresses.slice(index + 1).indexOf(whitelistedAddress.toLowerCase());
290
+ if (newIndex === -1)
291
+ break;
292
+ index = index + 1 + newIndex;
293
+ acc.balance0 += res.userPositions[index].balance0;
294
+ acc.balance1 += res.userPositions[index].balance1;
295
+ acc.inRangeLiquidity += res.userPositions[index].inRangeLiquidity;
296
+ acc.totalLiquidity += res.userPositions[index].totalLiquidity;
173
297
  }
174
298
  }
175
- catch (_error) {
176
- log.local(`Merkl User Data: error handling alm positions for pool ${NETWORK_LABELS[chainId]}-${p}`);
299
+ return acc;
300
+ }, {
301
+ balance0: 0,
302
+ balance1: 0,
303
+ inRangeLiquidity: 0,
304
+ totalLiquidity: 0,
305
+ });
306
+ userBalance0 = Math.max(0, aux.balance0);
307
+ userBalance1 = Math.max(0, aux.balance1);
308
+ userInRangeLiquidity = Math.max(0, aux.inRangeLiquidity);
309
+ userTotalLiquidity = Math.max(0, aux.inRangeLiquidity);
310
+ }
311
+ else if (campaignData.blacklist.length > 0) {
312
+ /** Handle blacklist to compute APR */
313
+ const aux = campaignData.blacklist.reduce((acc, blacklistedAddress) => {
314
+ /** 1_ User is blacklisted */
315
+ if (user.toLowerCase() === blacklistedAddress.toLowerCase()) {
316
+ acc.balance0 -= res.userBalanceToken0;
317
+ acc.balance1 -= res.userBalanceToken1;
318
+ acc.inRangeLiquidity -= res.userInRangeLiquidity;
177
319
  }
178
- }
179
- /** 3_ User positions that were deposited on non fungible positions wrapper contracts (eg. farms) */
180
- let j = callsByAmm[amm].start + callsByAmm[amm].poolCalls;
181
- if (getSupportedNFPWrapperMapping(chainId, amm).length > 0) {
182
- const nonFungiblePositionManagerInterface = NonFungiblePositionManagerInterface[AMMAlgorithmMapping[amm]];
183
- for (const wrapper of getSupportedNFPWrapperMapping(chainId, amm)) {
184
- if (!!farmedTokenIds?.[amm] && farmedTokenIds?.[amm][wrapper].length > 0) {
185
- for (let k = 0; k < farmedTokenIds[amm][wrapper].length; k++) {
186
- if (farmedTokenIds[amm][wrapper][k].pool !== p.toLowerCase()) {
187
- j++;
188
- continue;
189
- }
190
- const posLiquidity = nonFungiblePositionManagerInterface.decodeFunctionResult("positions", result[j]).liquidity;
191
- const tickLower = Number.parseFloat(nonFungiblePositionManagerInterface
192
- .decodeFunctionResult("positions", result[j])
193
- .tickLower.toString());
194
- const tickUpper = Number.parseFloat(nonFungiblePositionManagerInterface
195
- .decodeFunctionResult("positions", result[j++])
196
- .tickUpper.toString());
197
- try {
198
- const [amount0, amount1] = getAmountsForLiquidity(sqrtPriceX96, tickLower, tickUpper, posLiquidity);
199
- const balance0 = BN2Number(amount0, poolDatas[p].decimalsToken0);
200
- const balance1 = BN2Number(amount1, poolDatas[p].decimalsToken1);
201
- const priceToken0 = (await pricer.get({
202
- address: poolDatas[p].token0,
203
- chainId,
204
- symbol: poolDatas[p].symbolToken0,
205
- }));
206
- const priceToken1 = (await pricer.get({
207
- address: poolDatas[p].token1,
208
- chainId,
209
- symbol: poolDatas[p].symbolToken1,
210
- }));
211
- const tvl = balance0 * priceToken0 + balance1 * priceToken1;
212
- updateUserPosition({
213
- almAddress: farmedTokenIds[amm][wrapper][k].farmAddress,
214
- balance0,
215
- balance1,
216
- id: farmedTokenIds[amm][wrapper][k].id,
217
- inRangeLiquidity: balance0 > 0 && balance1 > 0 ? BN2Number(posLiquidity) : 0,
218
- lowerTick: tickLower,
219
- origin: -2 - Number.parseInt(String(wrapper)), // -2 - X means NFT Wrapper
220
- totalLiquidity: BN2Number(posLiquidity),
221
- tvl,
222
- upperTick: tickUpper,
223
- });
224
- }
225
- catch (_e) {
226
- log.local(`Merkl User Data: error handling positions for pool ${NETWORK_LABELS[chainId]}-${p}`);
227
- }
228
- }
320
+ else if (res.userPositions
321
+ .map((w) => (!!w?.almAddress ? w?.almAddress?.toLowerCase() : null))
322
+ ?.filter((a) => !!a)
323
+ .includes(blacklistedAddress.toLowerCase())) {
324
+ /** 2_ ALM and/or NFP vault is blacklisted */
325
+ let index = -1;
326
+ const positionsAddresses = res.userPositions.map((w) => w.almAddress?.toLowerCase());
327
+ while (true) {
328
+ const newIndex = positionsAddresses.slice(index + 1).indexOf(blacklistedAddress.toLowerCase());
329
+ if (newIndex === -1)
330
+ break;
331
+ index = index + 1 + newIndex;
332
+ acc.balance0 -= res.userPositions[index].balance0;
333
+ acc.balance1 -= res.userPositions[index].balance1;
334
+ acc.inRangeLiquidity -= res.userPositions[index].inRangeLiquidity;
335
+ acc.totalLiquidity -= res.userPositions[index].totalLiquidity;
229
336
  }
230
337
  }
231
- }
232
- /** Individual APRs */
233
- let individualApr = 0;
234
- const oneDistributionIsBoosted = false;
235
- for (const campaignDatas of Object.values(poolDatas[p].campaigns)) {
236
- const campaignData = campaignDatas;
237
- let userBalance0 = res.userBalanceToken0;
238
- let userBalance1 = res.userBalanceToken1;
239
- let userInRangeLiquidity = res.userInRangeLiquidity;
240
- let userTotalLiquidity = res.userTotalLiquidity;
241
- const priceRewardToken = (await pricer.get({
242
- address: campaignData.rewardToken,
243
- chainId,
244
- symbol: campaignData.symbolRewardToken,
245
- }));
246
- /** Handle whitelist to compute APR */
247
- if (campaignData.whitelist.length > 0) {
248
- const aux = campaignData.whitelist.reduce((acc, whitelistedAddress) => {
249
- /** 1_ User is whitelisted */
250
- if (user.toLowerCase() === whitelistedAddress.toLowerCase()) {
251
- acc.balance0 += res.userBalanceToken0;
252
- acc.balance1 += res.userBalanceToken1;
253
- acc.inRangeLiquidity += res.userInRangeLiquidity;
254
- acc.totalLiquidity += res.userTotalLiquidity;
255
- }
256
- else if (res.userPositions
257
- .map((w) => (!!w?.almAddress ? w?.almAddress?.toLowerCase() : null))
258
- ?.filter((a) => !!a)
259
- .includes(whitelistedAddress.toLowerCase())) {
260
- /**
261
- * 2_ ALM is whitelisted and/or Nf positions vault is whitelisted
262
- * @dev for simplicity purposes, vault address is marked as `almAddress`
263
- */
264
- let index = -1;
265
- const positionsAddresses = res.userPositions.map((w) => w.almAddress?.toLowerCase());
266
- while (true) {
267
- const newIndex = positionsAddresses
268
- .slice(index + 1)
269
- .indexOf(whitelistedAddress.toLowerCase());
270
- if (newIndex === -1)
271
- break;
272
- index = index + 1 + newIndex;
273
- acc.balance0 += res.userPositions[index].balance0;
274
- acc.balance1 += res.userPositions[index].balance1;
275
- acc.inRangeLiquidity += res.userPositions[index].inRangeLiquidity;
276
- acc.totalLiquidity += res.userPositions[index].totalLiquidity;
277
- }
278
- }
279
- else if (res.userPositions
280
- .map((w) => !!w?.almAddress
281
- ? poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target?.toLowerCase()
282
- : null)
283
- ?.filter((a) => !!a)
284
- .includes(whitelistedAddress?.toLowerCase())) {
285
- /** 3_ ALM target is whitelisted */
286
- let index = -1;
287
- const positionsAddresses = res.userPositions.map((w) => !!w?.almAddress &&
288
- poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target.toLowerCase());
289
- while (true) {
290
- const newIndex = positionsAddresses
291
- .slice(index + 1)
292
- .indexOf(whitelistedAddress.toLowerCase());
293
- if (newIndex === -1)
294
- break;
295
- index = index + 1 + newIndex;
296
- acc.balance0 += res.userPositions[index].balance0;
297
- acc.balance1 += res.userPositions[index].balance1;
298
- acc.inRangeLiquidity += res.userPositions[index].inRangeLiquidity;
299
- acc.totalLiquidity += res.userPositions[index].totalLiquidity;
300
- }
301
- }
302
- return acc;
303
- }, {
304
- balance0: 0,
305
- balance1: 0,
306
- inRangeLiquidity: 0,
307
- totalLiquidity: 0,
308
- });
309
- userBalance0 = Math.max(0, aux.balance0);
310
- userBalance1 = Math.max(0, aux.balance1);
311
- userInRangeLiquidity = Math.max(0, aux.inRangeLiquidity);
312
- userTotalLiquidity = Math.max(0, aux.inRangeLiquidity);
313
- }
314
- else if (campaignData.blacklist.length > 0) {
315
- /** Handle blacklist to compute APR */
316
- const aux = campaignData.blacklist.reduce((acc, blacklistedAddress) => {
317
- /** 1_ User is blacklisted */
318
- if (user.toLowerCase() === blacklistedAddress.toLowerCase()) {
319
- acc.balance0 -= res.userBalanceToken0;
320
- acc.balance1 -= res.userBalanceToken1;
321
- acc.inRangeLiquidity -= res.userInRangeLiquidity;
322
- }
323
- else if (res.userPositions
324
- .map((w) => (!!w?.almAddress ? w?.almAddress?.toLowerCase() : null))
325
- ?.filter((a) => !!a)
326
- .includes(blacklistedAddress.toLowerCase())) {
327
- /** 2_ ALM and/or NFP vault is blacklisted */
328
- let index = -1;
329
- const positionsAddresses = res.userPositions.map((w) => w.almAddress?.toLowerCase());
330
- while (true) {
331
- const newIndex = positionsAddresses
332
- .slice(index + 1)
333
- .indexOf(blacklistedAddress.toLowerCase());
334
- if (newIndex === -1)
335
- break;
336
- index = index + 1 + newIndex;
337
- acc.balance0 -= res.userPositions[index].balance0;
338
- acc.balance1 -= res.userPositions[index].balance1;
339
- acc.inRangeLiquidity -= res.userPositions[index].inRangeLiquidity;
340
- acc.totalLiquidity -= res.userPositions[index].totalLiquidity;
341
- }
342
- }
343
- else if (
344
- /** 3_ Target is blacklisted */
345
- res.userPositions
346
- .map((w) => !!w?.almAddress
347
- ? poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target?.toLowerCase()
348
- : null)
349
- ?.filter((a) => !!a)
350
- .includes(blacklistedAddress.toLowerCase())) {
351
- let index = -1;
352
- const positionsAddresses = res.userPositions.map((w) => !!w?.almAddress &&
353
- poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target.toLowerCase());
354
- while (true) {
355
- const newIndex = positionsAddresses
356
- .slice(index + 1)
357
- .indexOf(blacklistedAddress.toLowerCase());
358
- if (newIndex === -1)
359
- break;
360
- index = index + 1 + newIndex;
361
- acc.balance0 -= res.userPositions[index].balance0;
362
- acc.balance1 -= res.userPositions[index].balance1;
363
- acc.inRangeLiquidity -= res.userPositions[index].inRangeLiquidity;
364
- acc.totalLiquidity -= res.userPositions[index].totalLiquidity;
365
- }
366
- }
367
- return acc;
368
- }, {
369
- balance0: res.userBalanceToken0,
370
- balance1: res.userBalanceToken1,
371
- inRangeLiquidity: res.userInRangeLiquidity,
372
- totalLiquidity: 0,
373
- });
374
- userBalance0 = Math.max(0, aux.balance0);
375
- userBalance1 = Math.max(0, aux.balance1);
376
- userInRangeLiquidity = Math.max(0, aux.inRangeLiquidity);
377
- userTotalLiquidity = Math.max(0, aux.inRangeLiquidity);
338
+ else if (
339
+ /** 3_ Target is blacklisted */
340
+ res.userPositions
341
+ .map((w) => !!w?.almAddress
342
+ ? poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target?.toLowerCase()
343
+ : null)
344
+ ?.filter((a) => !!a)
345
+ .includes(blacklistedAddress.toLowerCase())) {
346
+ let index = -1;
347
+ const positionsAddresses = res.userPositions.map((w) => !!w?.almAddress &&
348
+ poolDatas[p].forwarders[utils.getAddress(w?.almAddress)]?.target.toLowerCase());
349
+ while (true) {
350
+ const newIndex = positionsAddresses.slice(index + 1).indexOf(blacklistedAddress.toLowerCase());
351
+ if (newIndex === -1)
352
+ break;
353
+ index = index + 1 + newIndex;
354
+ acc.balance0 -= res.userPositions[index].balance0;
355
+ acc.balance1 -= res.userPositions[index].balance1;
356
+ acc.inRangeLiquidity -= res.userPositions[index].inRangeLiquidity;
357
+ acc.totalLiquidity -= res.userPositions[index].totalLiquidity;
358
+ }
378
359
  }
379
- /** Yearly rewards in $ */
380
- const distributionDuration = campaignData.endTimestamp - campaignData.startTimestamp;
381
- const yearlyAmountDistributed = priceRewardToken * campaignData.amount * YEAR;
382
- const yearlyToken0Rewards = (campaignData.propToken0 * yearlyAmountDistributed) / distributionDuration;
383
- const yearlyToken1Rewards = (campaignData.propToken1 * yearlyAmountDistributed) / distributionDuration;
384
- const yearlyFeeRewards = (campaignData.propFees * yearlyAmountDistributed) / distributionDuration;
385
- // Approximation because it uses `poolTotalLiquidity` which may contain out of range liquidity
386
- // Approximation as well because the out of range / in range is for all positions and not "per position"
387
- let campaignIndividualAPR = !campaignData.isOutOfRangeIncentivized && (!userBalance0 || !userBalance1)
388
- ? 0
389
- : ((yearlyToken0Rewards * userBalance0) /
390
- (poolDatas[p].poolBalanceToken0 - (campaignData.blacklistedBalance0 ?? 0)) +
391
- (yearlyToken1Rewards * userBalance1) /
392
- (poolDatas[p].poolBalanceToken1 - (campaignData.blacklistedBalance1 ?? 0)) +
393
- (yearlyFeeRewards *
394
- (campaignData.isOutOfRangeIncentivized ? userTotalLiquidity : userInRangeLiquidity)) /
395
- (poolDatas[p].poolTotalLiquidity - (campaignData.blacklistedLiquidity ?? 0))) /
396
- res.userTVL;
397
- campaignIndividualAPR =
398
- !campaignIndividualAPR || Number.isNaN(campaignIndividualAPR) ? 0 : campaignIndividualAPR;
399
- individualApr += campaignIndividualAPR;
400
- }
401
- if (!!individualApr) {
402
- res.apr[`User current APR ${oneDistributionIsBoosted ? "(based on last distribution boost)" : ""}`] =
403
- individualApr;
404
- }
405
- if (res.userPositions.length > 0) {
406
- finalRes[`2_${Object.values(poolDatas[p].campaigns)[0].mainParameter}`] = { ...res };
407
- }
408
- }
409
- catch (_e) {
410
- log.local(`merkl User Data: error for pool ${NETWORK_LABELS[chainId]}-${p}`);
360
+ return acc;
361
+ }, {
362
+ balance0: res.userBalanceToken0,
363
+ balance1: res.userBalanceToken1,
364
+ inRangeLiquidity: res.userInRangeLiquidity,
365
+ totalLiquidity: 0,
366
+ });
367
+ userBalance0 = Math.max(0, aux.balance0);
368
+ userBalance1 = Math.max(0, aux.balance1);
369
+ userInRangeLiquidity = Math.max(0, aux.inRangeLiquidity);
370
+ userTotalLiquidity = Math.max(0, aux.inRangeLiquidity);
411
371
  }
372
+ /** Yearly rewards in $ */
373
+ const distributionDuration = campaignData.endTimestamp - campaignData.startTimestamp;
374
+ const yearlyAmountDistributed = priceRewardToken * campaignData.amount * YEAR;
375
+ const yearlyToken0Rewards = (campaignData.propToken0 * yearlyAmountDistributed) / distributionDuration;
376
+ const yearlyToken1Rewards = (campaignData.propToken1 * yearlyAmountDistributed) / distributionDuration;
377
+ const yearlyFeeRewards = (campaignData.propFees * yearlyAmountDistributed) / distributionDuration;
378
+ // Approximation because it uses `poolTotalLiquidity` which may contain out of range liquidity
379
+ // Approximation as well because the out of range / in range is for all positions and not "per position"
380
+ let campaignIndividualAPR = !campaignData.isOutOfRangeIncentivized && (!userBalance0 || !userBalance1)
381
+ ? 0
382
+ : ((yearlyToken0Rewards * userBalance0) /
383
+ (poolDatas[p].poolBalanceToken0 - (campaignData.blacklistedBalance0 ?? 0)) +
384
+ (yearlyToken1Rewards * userBalance1) /
385
+ (poolDatas[p].poolBalanceToken1 - (campaignData.blacklistedBalance1 ?? 0)) +
386
+ (yearlyFeeRewards *
387
+ (campaignData.isOutOfRangeIncentivized ? userTotalLiquidity : userInRangeLiquidity)) /
388
+ (poolDatas[p].poolTotalLiquidity - (campaignData.blacklistedLiquidity ?? 0))) /
389
+ res.userTVL;
390
+ campaignIndividualAPR =
391
+ !campaignIndividualAPR || Number.isNaN(campaignIndividualAPR) ? 0 : campaignIndividualAPR;
392
+ individualApr += campaignIndividualAPR;
412
393
  }
394
+ if (!!individualApr) {
395
+ res.apr[`User current APR ${oneDistributionIsBoosted ? "(based on last distribution boost)" : ""}`] =
396
+ individualApr;
397
+ }
398
+ }
399
+ if (res.userPositions.length > 0) {
400
+ finalRes[`2_${poolDatas[p].mainParameter}`] = { ...res };
413
401
  }
414
402
  }
415
- return finalRes;
416
- },
417
- },
418
- };
403
+ catch (_e) {
404
+ console.error(_e);
405
+ log.local(`merkl User Data: error for pool ${NETWORK_LABELS[chainId]}-${p}`);
406
+ }
407
+ }
408
+ }
409
+ }
410
+ return finalRes;
419
411
  }