@merkl/api 0.20.92 → 0.20.93
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/redis.js +1 -0
- package/dist/src/engine/campaignTVL/factory.js +2 -0
- package/dist/src/engine/{dynamicData → campaignTVL}/implementations/Ambiant.d.ts +3 -3
- package/dist/src/engine/campaignTVL/implementations/Ambiant.js +178 -0
- package/dist/src/engine/dynamicData/factory.js +1 -2
- package/dist/src/jobs/update-analytics.js +123 -99
- package/dist/src/modules/v4/router.js +8 -1
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/dist/src/engine/dynamicData/implementations/Ambiant.js +0 -232
- package/dist/src/engine/dynamicData/utils/fetchAmbientInfo.d.ts +0 -12
- package/dist/src/engine/dynamicData/utils/fetchAmbientInfo.js +0 -59
package/package.json
CHANGED
@@ -57,6 +57,7 @@
|
|
57
57
|
"@notionhq/client": "^2.2.15",
|
58
58
|
"@opentelemetry/auto-instrumentations-node": "^0.56.0",
|
59
59
|
"@opentelemetry/instrumentation": "^0.57.1",
|
60
|
+
"@opentelemetry/instrumentation-redis-4": "^0.47.0",
|
60
61
|
"@prisma/client": "6.3.1",
|
61
62
|
"@prisma/instrumentation": "^6.3.1",
|
62
63
|
"@sinclair/typebox": "^0.34.15",
|
@@ -109,5 +110,5 @@
|
|
109
110
|
"access": "public",
|
110
111
|
"registry": "https://registry.npmjs.org/"
|
111
112
|
},
|
112
|
-
"version": "v0.20.
|
113
|
+
"version": "v0.20.93"
|
113
114
|
}
|
@@ -1,232 +0,0 @@
|
|
1
|
-
import { TokenService } from "@/modules/v4/token/token.service";
|
2
|
-
import { AmbientAddresses, AmbientLens, BN2Number, ChainInteractionService, EAprBreakdownType, NETWORK_LABELS, YEAR, } from "@sdk";
|
3
|
-
import moment from "moment";
|
4
|
-
import { log } from "../../../utils/logger";
|
5
|
-
import { Pricer } from "../../../utils/pricer";
|
6
|
-
import { fetchAmbientPotentialPositions } from "../utils/fetchAmbientInfo";
|
7
|
-
var AmbiantPositionType;
|
8
|
-
(function (AmbiantPositionType) {
|
9
|
-
AmbiantPositionType["Concentrated"] = "concentrated";
|
10
|
-
AmbiantPositionType["Ambient"] = "ambient";
|
11
|
-
AmbiantPositionType["Knockout"] = "knockout";
|
12
|
-
})(AmbiantPositionType || (AmbiantPositionType = {}));
|
13
|
-
function encodeCall(chainId, holder) {
|
14
|
-
if (holder.positionType === AmbiantPositionType.Concentrated) {
|
15
|
-
return [
|
16
|
-
{
|
17
|
-
allowFailure: true,
|
18
|
-
callData: AmbientLens.encodeFunctionData("queryRangeTokens", [
|
19
|
-
holder.owner,
|
20
|
-
holder.base,
|
21
|
-
holder.quote,
|
22
|
-
holder.poolIdx,
|
23
|
-
holder.lowerTick,
|
24
|
-
holder.upperTick,
|
25
|
-
]),
|
26
|
-
target: AmbientAddresses[chainId].CrocQuery,
|
27
|
-
},
|
28
|
-
];
|
29
|
-
}
|
30
|
-
return [
|
31
|
-
{
|
32
|
-
allowFailure: true,
|
33
|
-
callData: AmbientLens.encodeFunctionData("queryAmbientTokens", [
|
34
|
-
holder.owner,
|
35
|
-
holder.base,
|
36
|
-
holder.quote,
|
37
|
-
holder.poolIdx,
|
38
|
-
]),
|
39
|
-
target: AmbientAddresses[chainId].CrocQuery,
|
40
|
-
},
|
41
|
-
];
|
42
|
-
}
|
43
|
-
async function decodeCall(result, holder) {
|
44
|
-
let amount0 = BigInt(0n);
|
45
|
-
let amount1 = BigInt(0n);
|
46
|
-
if (holder.positionType === AmbiantPositionType.Concentrated) {
|
47
|
-
const resTok = AmbientLens.decodeFunctionResult("queryRangeTokens", result);
|
48
|
-
amount0 = BigInt(resTok[1]);
|
49
|
-
amount1 = BigInt(resTok[2]);
|
50
|
-
}
|
51
|
-
if (holder.positionType === AmbiantPositionType.Ambient) {
|
52
|
-
const resTok = AmbientLens.decodeFunctionResult("queryAmbientTokens", result);
|
53
|
-
amount0 = BigInt(resTok[1]);
|
54
|
-
amount1 = BigInt(resTok[2]);
|
55
|
-
}
|
56
|
-
return { amount0, amount1 };
|
57
|
-
}
|
58
|
-
export class AmbiantDynamicData {
|
59
|
-
async build(chainId, campaigns) {
|
60
|
-
const dynamicData = [];
|
61
|
-
const pricer = await Pricer.load();
|
62
|
-
let calls = [];
|
63
|
-
/** Dedupe pools from campaigns to build pool list */
|
64
|
-
const poolList = [];
|
65
|
-
for (const campaign of campaigns ?? []) {
|
66
|
-
/** Loop through all campaigns to add pools */
|
67
|
-
if (!poolList?.map(p => p.mainParameter.toLowerCase()).includes(campaign.mainParameter.toLowerCase())) {
|
68
|
-
poolList.push({
|
69
|
-
id: campaign.campaignParameters.poolId,
|
70
|
-
mainParameter: campaign.mainParameter, // main parameter containes info of poolAddress + AMM (in case its a priority AMM)
|
71
|
-
baseToken: campaign.campaignParameters.baseToken,
|
72
|
-
quoteToken: campaign.campaignParameters.quoteToken,
|
73
|
-
poolIdx: campaign.campaignParameters.poolIdx,
|
74
|
-
potentialHolders: await fetchAmbientPotentialPositions(campaign.campaignParameters.poolId, campaign.computeChainId),
|
75
|
-
});
|
76
|
-
}
|
77
|
-
}
|
78
|
-
if (!!poolList) {
|
79
|
-
for (const pool of poolList) {
|
80
|
-
calls.push({
|
81
|
-
allowFailure: true,
|
82
|
-
callData: AmbientLens.encodeFunctionData("queryLiquidity", [pool.baseToken, pool.quoteToken, pool.poolIdx]),
|
83
|
-
target: AmbientAddresses[chainId].CrocQuery,
|
84
|
-
}, {
|
85
|
-
allowFailure: true,
|
86
|
-
callData: AmbientLens.encodeFunctionData("queryCurveTick", [pool.baseToken, pool.quoteToken, pool.poolIdx]),
|
87
|
-
target: AmbientAddresses[chainId].CrocQuery,
|
88
|
-
});
|
89
|
-
calls = calls.concat(...pool.potentialHolders.map(holder => encodeCall(chainId, holder)));
|
90
|
-
}
|
91
|
-
const result = await ChainInteractionService(chainId).fetchState(calls);
|
92
|
-
let i = 0;
|
93
|
-
if (!!poolList) {
|
94
|
-
for (const pool of poolList) {
|
95
|
-
// This liquidity call gives the active liquidity on the pool. To get the total liquidity we would need to loop over all positions
|
96
|
-
let poolTotalLiquidity;
|
97
|
-
let activeTick;
|
98
|
-
let poolBalanceToken0 = 0;
|
99
|
-
let poolBalanceToken1 = 0;
|
100
|
-
const d = campaigns?.filter(campaign => campaign.mainParameter.toLowerCase() === pool.mainParameter.toLowerCase())[0];
|
101
|
-
const decimalsBaseToken = d.campaignParameters.decimalsBaseToken;
|
102
|
-
const decimalsQuoteToken = d.campaignParameters.decimalsQuoteToken;
|
103
|
-
const symbolBaseToken = d.campaignParameters.symbolBaseToken;
|
104
|
-
const symbolQuoteToken = d.campaignParameters.symbolQuoteToken;
|
105
|
-
const prevI = i;
|
106
|
-
try {
|
107
|
-
poolTotalLiquidity = BN2Number(AmbientLens.decodeFunctionResult("queryLiquidity", result[i++].returnData)[0]);
|
108
|
-
activeTick = Number(AmbientLens.decodeFunctionResult("queryCurveTick", result[i++].returnData)[0]);
|
109
|
-
for (let index = 0; index < pool.potentialHolders.length; index++) {
|
110
|
-
// to verify
|
111
|
-
if (calls.length === 0) {
|
112
|
-
break;
|
113
|
-
}
|
114
|
-
const res = await decodeCall(result[i++].returnData, pool.potentialHolders[index]);
|
115
|
-
poolBalanceToken0 += BN2Number(res.amount0, decimalsBaseToken);
|
116
|
-
poolBalanceToken1 += BN2Number(res.amount1, decimalsQuoteToken);
|
117
|
-
}
|
118
|
-
}
|
119
|
-
catch (e) {
|
120
|
-
log.warn(`merklDynamic data - failed to decode state of pool ${pool.id} on ${NETWORK_LABELS[chainId]}`);
|
121
|
-
i = prevI + 2 + pool.potentialHolders.length;
|
122
|
-
continue;
|
123
|
-
}
|
124
|
-
const priceToken0 = (await pricer.get({
|
125
|
-
address: d.campaignParameters.baseToken,
|
126
|
-
chainId: chainId,
|
127
|
-
symbol: symbolBaseToken,
|
128
|
-
}));
|
129
|
-
const priceToken1 = (await pricer.get({
|
130
|
-
address: d.campaignParameters.quoteToken,
|
131
|
-
chainId: chainId,
|
132
|
-
symbol: symbolQuoteToken,
|
133
|
-
}));
|
134
|
-
/** Iterate over distributions to compute APRs */
|
135
|
-
for (const campaign of campaigns.filter(campaign => campaign.mainParameter.toLowerCase() === pool.mainParameter.toLowerCase())) {
|
136
|
-
const c = campaign;
|
137
|
-
const amount = BN2Number(c.amount, c.campaignParameters.decimalsRewardToken);
|
138
|
-
const startTimestamp = BN2Number(c.startTimestamp, 0);
|
139
|
-
const endTimestamp = BN2Number(c.endTimestamp, 0);
|
140
|
-
const isLive = moment().unix() >= startTimestamp && moment().unix() < endTimestamp;
|
141
|
-
const totalWeight = BN2Number(c.campaignParameters.weightFees, 4) +
|
142
|
-
BN2Number(c.campaignParameters.weightToken0, 4) +
|
143
|
-
BN2Number(c.campaignParameters.weightToken1, 4);
|
144
|
-
// Proportions in percentage
|
145
|
-
const propFees = (BN2Number(c.campaignParameters.weightFees, 4) / totalWeight) * 100;
|
146
|
-
const propToken0 = (BN2Number(c.campaignParameters.weightToken0, 4) / totalWeight) * 100;
|
147
|
-
const propToken1 = (BN2Number(c.campaignParameters.weightToken1, 4) / totalWeight) * 100;
|
148
|
-
let distributionMeanAPR = 0;
|
149
|
-
const aprs = {};
|
150
|
-
const aprBreakdowns = [];
|
151
|
-
let priceRewardToken = 0;
|
152
|
-
if (isLive && c.campaignParameters.symbolRewardToken !== "aglaMerkl") {
|
153
|
-
// priceRewardToken = (await pricer.get({
|
154
|
-
// address: c.rewardToken,
|
155
|
-
// chainId: chainId,
|
156
|
-
// symbol: c.campaignParameters.symbolRewardToken,
|
157
|
-
// })) as number;
|
158
|
-
priceRewardToken = await TokenService.getRewardTokenPrice(campaign);
|
159
|
-
/** Yearly rewards in $ */
|
160
|
-
const yearlyToken0Rewards = (propToken0 * priceRewardToken * amount * YEAR) / (endTimestamp - startTimestamp);
|
161
|
-
const yearlyToken1Rewards = (propToken1 * priceRewardToken * amount * YEAR) / (endTimestamp - startTimestamp);
|
162
|
-
const yearlyFeeRewards = (propFees * priceRewardToken * amount * YEAR) / (endTimestamp - startTimestamp);
|
163
|
-
let poolAPRkey = "";
|
164
|
-
const tvl = poolBalanceToken0 * priceToken0 + poolBalanceToken1 * priceToken1;
|
165
|
-
distributionMeanAPR = (yearlyToken0Rewards + yearlyToken1Rewards + yearlyFeeRewards) / tvl;
|
166
|
-
distributionMeanAPR = !distributionMeanAPR || Number.isNaN(distributionMeanAPR) ? 0 : distributionMeanAPR;
|
167
|
-
/**
|
168
|
-
* @dev We cannot include a whitelisted distrib apr into the mean APR
|
169
|
-
*/
|
170
|
-
if (c.campaignParameters.whitelist.length === 0) {
|
171
|
-
poolAPRkey = "Average APR (rewards / pool TVL)";
|
172
|
-
if (!aprs[poolAPRkey])
|
173
|
-
aprs[poolAPRkey] = 0;
|
174
|
-
aprs[poolAPRkey] += distributionMeanAPR;
|
175
|
-
// @Hugo wip: new way to structure aprBreakdowns
|
176
|
-
aprBreakdowns.push({
|
177
|
-
address: pool.id,
|
178
|
-
value: distributionMeanAPR,
|
179
|
-
type: EAprBreakdownType.AVERAGE,
|
180
|
-
label: "Average APR (rewards / pool TVL)",
|
181
|
-
});
|
182
|
-
// APR per token
|
183
|
-
poolAPRkey = `APR for holding ${c.campaignParameters.symbolBaseToken} in pool`;
|
184
|
-
if (!aprs[poolAPRkey])
|
185
|
-
aprs[poolAPRkey] = 0;
|
186
|
-
aprs[poolAPRkey] += yearlyToken0Rewards / (poolBalanceToken0 * priceToken0);
|
187
|
-
// @Hugo wip: new way to structure aprBreakdowns
|
188
|
-
aprBreakdowns.push({
|
189
|
-
address: pool.id,
|
190
|
-
value: yearlyToken0Rewards / (poolBalanceToken0 * priceToken0),
|
191
|
-
type: EAprBreakdownType.TOKEN1,
|
192
|
-
label: c.campaignParameters.symbolBaseToken,
|
193
|
-
});
|
194
|
-
poolAPRkey = `APR for holding ${c.campaignParameters.symbolQuoteToken} in pool`;
|
195
|
-
if (!aprs[poolAPRkey])
|
196
|
-
aprs[poolAPRkey] = 0;
|
197
|
-
aprs[poolAPRkey] += yearlyToken1Rewards / (poolBalanceToken1 * priceToken1);
|
198
|
-
// @Hugo wip: new way to structure aprBreakdowns
|
199
|
-
aprBreakdowns.push({
|
200
|
-
address: pool.id,
|
201
|
-
value: yearlyToken1Rewards / (poolBalanceToken1 * priceToken1),
|
202
|
-
type: EAprBreakdownType.TOKEN2,
|
203
|
-
label: c.campaignParameters.symbolQuoteToken,
|
204
|
-
});
|
205
|
-
}
|
206
|
-
else {
|
207
|
-
}
|
208
|
-
}
|
209
|
-
const rewardTokens = await TokenService.findManyOrCreate([
|
210
|
-
{ chainId: campaign.chainId, address: campaign.rewardToken },
|
211
|
-
]);
|
212
|
-
const rewardToken = rewardTokens[0];
|
213
|
-
distributionMeanAPR = rewardToken.isPoint ? distributionMeanAPR / 365 / 100 : distributionMeanAPR;
|
214
|
-
dynamicData.push({
|
215
|
-
...campaign,
|
216
|
-
apr: distributionMeanAPR,
|
217
|
-
aprs,
|
218
|
-
forwarders: [],
|
219
|
-
poolBalanceToken0,
|
220
|
-
poolBalanceToken1,
|
221
|
-
poolTotalLiquidity,
|
222
|
-
tick: activeTick,
|
223
|
-
priceRewardToken: priceRewardToken,
|
224
|
-
tvl: poolBalanceToken0 * priceToken0 + poolBalanceToken1 * priceToken1,
|
225
|
-
});
|
226
|
-
}
|
227
|
-
}
|
228
|
-
}
|
229
|
-
}
|
230
|
-
return dynamicData;
|
231
|
-
}
|
232
|
-
}
|
@@ -1,12 +0,0 @@
|
|
1
|
-
import { type MerklChainId } from "@sdk";
|
2
|
-
export type AmbientHolderType = {
|
3
|
-
owner: string;
|
4
|
-
base: string;
|
5
|
-
quote: string;
|
6
|
-
poolIdx: number;
|
7
|
-
lowerTick: number;
|
8
|
-
upperTick: number;
|
9
|
-
positionType: string;
|
10
|
-
};
|
11
|
-
export declare const holdersQuery: string;
|
12
|
-
export declare function fetchAmbientPotentialPositions(poolId: string, chainId: MerklChainId): Promise<AmbientHolderType[]>;
|
@@ -1,59 +0,0 @@
|
|
1
|
-
import { subgraphAmbientEndpoints, withRetry } from "@sdk";
|
2
|
-
import { gql, request } from "graphql-request";
|
3
|
-
const BATCH_NUMBER = 1000;
|
4
|
-
export const holdersQuery = gql `
|
5
|
-
query LiquidityChanges($poolId: String!, $minId: String!) {
|
6
|
-
liquidityChanges(
|
7
|
-
where: { pool_: { id: $poolId }, id_gt: $minId , positionType_not: "knockout"},
|
8
|
-
first: ${BATCH_NUMBER},
|
9
|
-
orderBy: pool__id
|
10
|
-
) {
|
11
|
-
id
|
12
|
-
user
|
13
|
-
positionType
|
14
|
-
bidTick
|
15
|
-
askTick
|
16
|
-
pool {
|
17
|
-
base
|
18
|
-
quote
|
19
|
-
poolIdx
|
20
|
-
}
|
21
|
-
}
|
22
|
-
}
|
23
|
-
`;
|
24
|
-
export async function fetchAmbientPotentialPositions(poolId, chainId) {
|
25
|
-
let isFullyFetched = false;
|
26
|
-
let holders = [];
|
27
|
-
let minId = "";
|
28
|
-
while (!isFullyFetched) {
|
29
|
-
const data = await withRetry(request, [
|
30
|
-
subgraphAmbientEndpoints[chainId],
|
31
|
-
holdersQuery,
|
32
|
-
{
|
33
|
-
poolId: poolId,
|
34
|
-
minId: minId,
|
35
|
-
},
|
36
|
-
]);
|
37
|
-
const fetchedHolders = data.liquidityChanges?.map(entry => {
|
38
|
-
return {
|
39
|
-
owner: entry.user,
|
40
|
-
base: entry.pool.base,
|
41
|
-
quote: entry.pool.quote,
|
42
|
-
poolIdx: Number(entry.pool.poolIdx),
|
43
|
-
lowerTick: Number(entry.bidTick),
|
44
|
-
upperTick: Number(entry.askTick),
|
45
|
-
positionType: entry.positionType,
|
46
|
-
};
|
47
|
-
});
|
48
|
-
if (fetchedHolders.length < BATCH_NUMBER) {
|
49
|
-
isFullyFetched = true;
|
50
|
-
}
|
51
|
-
else {
|
52
|
-
minId = data.liquidityChanges[fetchedHolders.length - 1].id;
|
53
|
-
}
|
54
|
-
holders = holders.concat(fetchedHolders);
|
55
|
-
}
|
56
|
-
// Only keep unique positions
|
57
|
-
holders = Array.from(new Set(holders.map(h => JSON.stringify(h)))).map(h => JSON.parse(h));
|
58
|
-
return holders;
|
59
|
-
}
|