@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.
- package/dist/src/cache/declaration.d.ts +0 -30
- package/dist/src/cache/declaration.js +0 -30
- package/dist/src/eden/index.d.ts +59 -12
- package/dist/src/index.d.ts +23 -4
- package/dist/src/libs/campaigns/campaignTypes/CLAMMDynamicData.js +2 -2
- package/dist/src/libs/positions/clamm/index.d.ts +1 -2
- package/dist/src/libs/positions/clamm/index.js +322 -330
- package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.d.ts +1 -1
- package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.js +1 -1
- package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.d.ts +1 -1
- package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.js +2 -2
- package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.d.ts +1 -2
- package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.js +1 -4
- package/dist/src/libs/positions/euler/index.js +2 -3
- package/dist/src/libs/positions/index.js +1 -1
- package/dist/src/modules/v4/campaign/campaign.controller.d.ts +2 -2
- package/dist/src/modules/v4/campaign/campaign.service.d.ts +6 -5
- package/dist/src/modules/v4/campaign/campaign.service.js +10 -2
- package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +2 -2
- package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +159 -0
- package/dist/src/modules/v4/opportunity/opportunity.repository.js +29 -0
- package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +131 -3
- package/dist/src/modules/v4/opportunity/opportunity.service.js +11 -0
- package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.js +90 -0
- package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.js +69 -0
- package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.js +71 -0
- package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.js +45 -0
- package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.js +47 -0
- package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.js +40 -0
- package/dist/src/modules/v4/position/index.d.ts +2 -0
- package/dist/src/modules/v4/position/index.js +2 -0
- package/dist/src/modules/v4/position/position.controller.d.ts +39 -0
- package/dist/src/modules/v4/position/position.controller.js +16 -0
- package/dist/src/modules/v4/position/position.model.d.ts +25 -0
- package/dist/src/modules/v4/position/position.model.js +5 -0
- package/dist/src/modules/v4/position/position.repository.d.ts +14 -0
- package/dist/src/modules/v4/position/position.repository.js +6 -0
- package/dist/src/modules/v4/position/position.service.d.ts +5 -0
- package/dist/src/modules/v4/position/position.service.js +34 -0
- package/dist/src/modules/v4/reward/reward.service.d.ts +5 -0
- package/dist/src/modules/v4/reward/reward.service.js +5 -2
- package/dist/src/modules/v4/router.d.ts +23 -4
- package/dist/src/modules/v4/router.js +3 -1
- package/dist/src/routes/v1/prices.js +2 -4
- package/dist/src/routes/v3/blacklist.d.ts +23 -4
- package/dist/src/routes/v3/campaigns.d.ts +24 -5
- package/dist/src/routes/v3/campaignsInfo.d.ts +23 -4
- package/dist/src/routes/v3/multiChainPositions.d.ts +23 -4
- package/dist/src/routes/v3/opportunity.d.ts +23 -4
- package/dist/src/routes/v3/positions.d.ts +23 -4
- package/dist/src/routes/v3/recipients.d.ts +6 -2
- package/dist/src/routes/v3/recipients.js +14 -8
- package/dist/src/routes/v3/rewards.d.ts +23 -4
- package/dist/src/routes/v3/updates.d.ts +23 -4
- package/dist/src/routes/v3/userRewards.d.ts +23 -4
- package/dist/src/utils/prices/services/erc4626Service.js +4 -4
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/libs/reports/campaignReport.d.ts +0 -9
- package/dist/src/libs/reports/campaignReport.js +0 -37
- package/dist/src/libs/reports/mainParameterRewards.d.ts +0 -3
- 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 {
|
8
|
-
import {
|
9
|
-
import {
|
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
|
-
|
17
|
-
|
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] =
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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,
|
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:
|
136
|
-
inRangeLiquidity: balance0 > 0 && balance1 > 0 ? BN2Number(
|
137
|
-
lowerTick:
|
138
|
-
origin: -
|
139
|
-
totalLiquidity: BN2Number(
|
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:
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
176
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
-
|
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
|
}
|