@merkl/api 0.10.168 → 0.10.169
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|