@merkl/api 0.10.167 → 0.10.169

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/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
  }