@merkl/api 0.14.3 → 0.14.5
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/jobs/etl/dynamic-data.d.ts +9 -0
- package/dist/src/jobs/etl/dynamic-data.js +165 -0
- package/dist/src/jobs/etl/reward-breakdowns.js +30 -3
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/EulerBorrowProcessor.js +1 -1
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +2 -1
@@ -0,0 +1,9 @@
|
|
1
|
+
import { Campaign, type CampaignDynamicData } from "@sdk";
|
2
|
+
export type CampaignsCacheUpdaterReturnType = {
|
3
|
+
[type_mainParameter: string]: {
|
4
|
+
[campaignId: string]: CampaignDynamicData<Campaign>;
|
5
|
+
};
|
6
|
+
};
|
7
|
+
export declare const main: () => Promise<{
|
8
|
+
success: boolean;
|
9
|
+
}>;
|
@@ -0,0 +1,165 @@
|
|
1
|
+
import { Redis } from "../../cache";
|
2
|
+
import { campaignsToOldFormat } from "../../libs/deprecated-merklv3";
|
3
|
+
import { AprService, CampaignService, OpportunityService } from "../../modules/v4";
|
4
|
+
import { CacheService } from "../../modules/v4/cache";
|
5
|
+
import { TTLPresets } from "../../modules/v4/cache/cache.model";
|
6
|
+
import { OpportunityRepository } from "../../modules/v4/opportunity/opportunity.repository";
|
7
|
+
import { RewardService } from "../../modules/v4/reward";
|
8
|
+
import { TvlService } from "../../modules/v4/tvl";
|
9
|
+
import { engineDbClient } from "../../utils/prisma";
|
10
|
+
import { Campaign, ChainId, NETWORK_LABELS, isSupportedChain, } from "@sdk";
|
11
|
+
import moment from "moment";
|
12
|
+
import { campaignsDynamicData } from "../../libs/campaigns/campaignsDynamicData";
|
13
|
+
import { merklChainData } from "../../libs/merklChainData";
|
14
|
+
import { staticCampaignWithCache } from "../../libs/staticCampaigns";
|
15
|
+
import { InvalidParameter, UnsupportedNetwork } from "../../utils/error";
|
16
|
+
import { executeSimple } from "../../utils/execute";
|
17
|
+
import { log } from "../../utils/logger";
|
18
|
+
import { ALL_CAMPAIGNS_FOR_CHAIN_AFTER } from "../../utils/queries/allCampaigns";
|
19
|
+
let campaignTypes = process.env.CAMPAIGN_TYPES ? JSON.parse(process.env.CAMPAIGN_TYPES) : [];
|
20
|
+
const highCampaignsChains = [ChainId.ARBITRUM, ChainId.POLYGON, ChainId.BLAST, ChainId.BASE];
|
21
|
+
export const main = async () => {
|
22
|
+
const rawChainId = process.env.CHAIN_ID;
|
23
|
+
let chainId;
|
24
|
+
if (typeof rawChainId === "string") {
|
25
|
+
chainId = Number.parseInt(rawChainId);
|
26
|
+
if (!isSupportedChain(chainId, "merkl"))
|
27
|
+
throw new UnsupportedNetwork(chainId);
|
28
|
+
}
|
29
|
+
else {
|
30
|
+
throw new InvalidParameter("Invalid chainId provided");
|
31
|
+
}
|
32
|
+
let success = true;
|
33
|
+
try {
|
34
|
+
await Redis.safeSet(`MerklChainData_${chainId}`, await merklChainData(chainId));
|
35
|
+
}
|
36
|
+
catch (error) {
|
37
|
+
log.error(`❌ update merklChainData cache failed for ${NETWORK_LABELS[chainId]}`, error);
|
38
|
+
success = false;
|
39
|
+
}
|
40
|
+
log.local(`🔁 updating ${NETWORK_LABELS[chainId]} Campaigns cache`);
|
41
|
+
let campaignsAfter = moment().subtract(3, "months").unix();
|
42
|
+
if (highCampaignsChains.includes(chainId)) {
|
43
|
+
campaignsAfter = moment().subtract(1, "months").unix();
|
44
|
+
}
|
45
|
+
const TWO_WEEKS_AGO = moment().subtract(2, "weeks").unix();
|
46
|
+
try {
|
47
|
+
const dynamicData = {};
|
48
|
+
let staticData = (await engineDbClient.$queryRaw(ALL_CAMPAIGNS_FOR_CHAIN_AFTER(chainId, campaignsAfter)));
|
49
|
+
log.local(`Data length before filtering: ${staticData.length}`);
|
50
|
+
const mainParameters = {};
|
51
|
+
staticData = staticData.filter(campaign => {
|
52
|
+
if (campaign.endTimestamp < TWO_WEEKS_AGO && campaign.campaignType === Campaign.CLAMM) {
|
53
|
+
campaign.campaignParameters.forwarders = [];
|
54
|
+
}
|
55
|
+
mainParameters[campaign.mainParameter] = true;
|
56
|
+
return true;
|
57
|
+
});
|
58
|
+
log.local(`Data length after filtering: ${staticData.length}`);
|
59
|
+
if (!!staticData) {
|
60
|
+
// Build list of existing campaign types for this chain
|
61
|
+
campaignTypes = !!campaignTypes.length
|
62
|
+
? campaignTypes
|
63
|
+
: !staticData
|
64
|
+
? []
|
65
|
+
: staticData
|
66
|
+
.map(campaign => campaign.campaignType)
|
67
|
+
.reduce((prev, campaignType) => {
|
68
|
+
if (!prev.includes(campaignType))
|
69
|
+
prev.push(campaignType);
|
70
|
+
return prev;
|
71
|
+
}, []);
|
72
|
+
// Fetch dynamic data for all these types
|
73
|
+
const promisesPerType = campaignTypes.map(async (campaignType) => {
|
74
|
+
const campaigns = staticData.filter(campaign => campaign.campaignType === campaignType);
|
75
|
+
await executeSimple(chainId, campaignsDynamicData(chainId, campaigns, campaignType)).then(r => {
|
76
|
+
for (const d of r) {
|
77
|
+
if (!!d) {
|
78
|
+
// Main Parameter OVERRIDING
|
79
|
+
if (d.campaignType === Campaign.SILO && d.campaignParameters.whitelist?.length === 1) {
|
80
|
+
d.mainParameter = `${d.mainParameter}-${d.campaignParameters.whitelist[0]}`;
|
81
|
+
}
|
82
|
+
if (!dynamicData[`${d.campaignType}_${d.mainParameter}`])
|
83
|
+
dynamicData[`${d.campaignType}_${d.mainParameter}`] = {};
|
84
|
+
dynamicData[`${d.campaignType}_${d.mainParameter}`][d.campaignId] = d;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
});
|
88
|
+
});
|
89
|
+
await Promise.all(promisesPerType);
|
90
|
+
}
|
91
|
+
if (!!dynamicData && Object.keys(dynamicData).length > 0) {
|
92
|
+
await Redis.safeSet(`Campaigns_${chainId}`, dynamicData);
|
93
|
+
// Set live or future campaigns
|
94
|
+
const liveDynamicData = {};
|
95
|
+
for (const [type_mainParameter, value] of Object.entries(dynamicData)) {
|
96
|
+
const liveDynamicData = {};
|
97
|
+
for (const [campaignId, data] of Object.entries(value)) {
|
98
|
+
if (data.endTimestamp > moment().unix()) {
|
99
|
+
if (!liveDynamicData[type_mainParameter]) {
|
100
|
+
liveDynamicData[type_mainParameter] = {};
|
101
|
+
}
|
102
|
+
liveDynamicData[type_mainParameter][campaignId] = data;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
await Redis.safeSet(`LiveCampaigns_${chainId}`, liveDynamicData);
|
107
|
+
const merklChainData = await Redis.get("MerklChainData", chainId);
|
108
|
+
await Redis.safeSet(`CampaignsOldFormat_${chainId}`, campaignsToOldFormat(dynamicData, merklChainData));
|
109
|
+
await Redis.safeSet(`LiveCampaignsOldFormat_${chainId}`, campaignsToOldFormat(liveDynamicData, merklChainData));
|
110
|
+
log.info(`✅ ${NETWORK_LABELS[chainId]} caches updated successfully`);
|
111
|
+
for (const entry of Object.entries(liveDynamicData)) {
|
112
|
+
await CampaignService.fill(Object.values(entry[1]).map(({ campaignId, chainId }) => ({
|
113
|
+
campaignId,
|
114
|
+
distributionChain: chainId,
|
115
|
+
})));
|
116
|
+
const [type, mainParameter] = entry[0].split("_");
|
117
|
+
const apr = AprService.extractFromDynamicData(+type, Object.values(entry[1]));
|
118
|
+
const tvl = TvlService.extractFromDynamicData(+type, Object.values(entry[1]));
|
119
|
+
const dailyRewards = await RewardService.extractDailyRewardsRecordFromDynamicData(+type, Object.values(entry[1]));
|
120
|
+
const opportunityId = OpportunityService.hashId({
|
121
|
+
chainId,
|
122
|
+
identifier: mainParameter,
|
123
|
+
type: CampaignService.getTypeFromV3(+type),
|
124
|
+
});
|
125
|
+
await OpportunityRepository.updateRecords(opportunityId, apr, tvl, dailyRewards);
|
126
|
+
}
|
127
|
+
await OpportunityService.updateMetadata(chainId);
|
128
|
+
// ─── Refresh Cache For GET /opportunities ────
|
129
|
+
await CacheService.set(TTLPresets.MIN_5, OpportunityService.getMany, { items: 50, page: 0 });
|
130
|
+
log.info(`✅ ${NETWORK_LABELS[chainId]} DB records updated successfully`);
|
131
|
+
}
|
132
|
+
else {
|
133
|
+
if (chainId === ChainId.CORE || chainId === ChainId.THUNDERCORE) {
|
134
|
+
log.info(`⚠️ no campaigns found for ${NETWORK_LABELS[chainId]}, setting empty cache`);
|
135
|
+
for (const key of [
|
136
|
+
"Campaigns",
|
137
|
+
"LiveCampaigns",
|
138
|
+
"CampaignsOldFormat",
|
139
|
+
"LiveCampaignsOldFormat",
|
140
|
+
]) {
|
141
|
+
await Redis.safeSet(`${key}_${chainId}`, {});
|
142
|
+
}
|
143
|
+
log.info(`✅ ${NETWORK_LABELS[chainId]} caches updated - empty cache`);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
catch (error) {
|
148
|
+
log.error(`❌ update Campaigns cache failed for ${NETWORK_LABELS[chainId]}`, error);
|
149
|
+
success = false;
|
150
|
+
}
|
151
|
+
// This is independant of campaigns cache update, so not in the if condition
|
152
|
+
try {
|
153
|
+
await staticCampaignWithCache(chainId);
|
154
|
+
}
|
155
|
+
catch (error) {
|
156
|
+
log.error(`❌ update Campaigns cache failed for ${NETWORK_LABELS[chainId]}`, error);
|
157
|
+
}
|
158
|
+
return { success };
|
159
|
+
};
|
160
|
+
main()
|
161
|
+
.then(success => (success ? process.exit(0) : process.exit(1)))
|
162
|
+
.catch((err) => {
|
163
|
+
console.error(err);
|
164
|
+
process.exit(1);
|
165
|
+
});
|
@@ -1,6 +1,7 @@
|
|
1
1
|
// ─── Reward Breakdowns ETL ───────────────────────────────────────────────────
|
2
2
|
if (!process.env.ENV || !process.env.CHAIN_ID || !process.env.ROOT)
|
3
3
|
throw new Error("[ENV]: missing variable");
|
4
|
+
import { CampaignService } from "../../modules/v4";
|
4
5
|
import { log } from "../../utils/logger";
|
5
6
|
import { apiDbClient } from "../../utils/prisma";
|
6
7
|
import { withRetry } from "@sdk";
|
@@ -32,7 +33,7 @@ const extract = async () => {
|
|
32
33
|
buffer = lines.pop() || "";
|
33
34
|
for (const line of lines)
|
34
35
|
if (line.trim()) {
|
35
|
-
data.push(...transform(JSON.parse(line)));
|
36
|
+
data.push(...(await transform(JSON.parse(line))));
|
36
37
|
}
|
37
38
|
try {
|
38
39
|
count += await withRetry(load, [data], 5, 60_000);
|
@@ -47,7 +48,33 @@ const extract = async () => {
|
|
47
48
|
return count;
|
48
49
|
};
|
49
50
|
// ─── Transform ───────────────────────────────────────────────────────────────
|
50
|
-
const transform = (rewardBreakdowns) => {
|
51
|
+
const transform = async (rewardBreakdowns) => {
|
52
|
+
const missingCampaigns = [];
|
53
|
+
const foundCampaigns = [];
|
54
|
+
const campaigns = rewardBreakdowns.reduce((acc, x) => {
|
55
|
+
const campaignUnique = {
|
56
|
+
campaignId: x.campaignId,
|
57
|
+
distributionChain: Number.parseInt(process.env.CHAIN_ID),
|
58
|
+
};
|
59
|
+
if (!Object.keys(acc).includes(CampaignService.hashId(campaignUnique)))
|
60
|
+
acc[CampaignService.hashId(campaignUnique)] = {
|
61
|
+
campaignId: x.campaignId,
|
62
|
+
distributionChain: Number.parseInt(process.env.CHAIN_ID),
|
63
|
+
};
|
64
|
+
return acc;
|
65
|
+
}, {});
|
66
|
+
for (const campaign of Object.values(campaigns)) {
|
67
|
+
const campaignExists = await CampaignService.checkIfExist(campaign);
|
68
|
+
if (!campaignExists) {
|
69
|
+
const { success, fail } = await CampaignService.fill([campaign]);
|
70
|
+
if (fail === 1 || success !== 1) {
|
71
|
+
missingCampaigns.push(CampaignService.hashId(campaign));
|
72
|
+
log.warn(`createManyBreakdown - Missing campaign: ${campaign.campaignId}`);
|
73
|
+
continue;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
foundCampaigns.push(CampaignService.hashId(campaign));
|
77
|
+
}
|
51
78
|
const transformedRewardBreakdowns = [];
|
52
79
|
for (const rewardBreakdown of rewardBreakdowns) {
|
53
80
|
const campaignId = Bun.hash(`${process.env.CHAIN_ID}${rewardBreakdown.campaignId}`).toString();
|
@@ -63,7 +90,7 @@ const transform = (rewardBreakdowns) => {
|
|
63
90
|
pending: rewardBreakdown.pending,
|
64
91
|
});
|
65
92
|
}
|
66
|
-
return transformedRewardBreakdowns;
|
93
|
+
return transformedRewardBreakdowns.filter(x => !missingCampaigns.includes(x.campaignId));
|
67
94
|
};
|
68
95
|
// ─── Load ────────────────────────────────────────────────────────────────────
|
69
96
|
const load = async (rewardBreakdowns) => {
|
package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/EulerBorrowProcessor.js
CHANGED
@@ -21,8 +21,8 @@ export class EulerBorrowProcessor extends GenericProcessor {
|
|
21
21
|
const decimalsAsset = Number(campaign.campaignParameters.decimalsAsset);
|
22
22
|
const priceAsset = (await pricer.get({ symbol: symbolAsset })) ?? 1;
|
23
23
|
const totalBorrows = BN2Number(typeInfo.totalAssets, decimalsAsset);
|
24
|
-
let cardName = generateCardName(type, typeInfo, campaign);
|
25
24
|
typeInfo.symbolUnderlyingToken = symbolAsset;
|
25
|
+
let cardName = generateCardName(type, typeInfo, campaign);
|
26
26
|
const vaultName = await fetchEulerVaultName(campaign.campaignParameters.evkAddress, campaign.chainId);
|
27
27
|
if (!!vaultName) {
|
28
28
|
cardName = `Borrow from ${vaultName} vault`;
|