@merkl/api 0.15.48 → 0.16.0

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.
@@ -1,4 +1,4 @@
1
- import { Campaign, type CampaignDynamicData } from "@sdk";
1
+ import { Campaign, type CampaignDynamicData, ChainId } from "@sdk";
2
2
  import { Elysia } from "elysia";
3
3
  export type CampaignsCacheUpdaterReturnType = {
4
4
  [type_mainParameter: string]: {
@@ -45,3 +45,6 @@ export declare const campaignsCacheUpdater: Elysia<"", false, {
45
45
  resolve: {};
46
46
  schema: {};
47
47
  }>;
48
+ export declare const campaignCacheUpdate: (rawChainId: string, queryCampaignTypes: number[], highCampaignsChains: ChainId[]) => Promise<{
49
+ success: boolean;
50
+ }>;
@@ -20,6 +20,15 @@ import { ALL_CAMPAIGNS_FOR_CHAIN_AFTER } from "../../utils/queries/allCampaigns"
20
20
  const highCampaignsChains = [ChainId.ARBITRUM, ChainId.POLYGON, ChainId.BLAST, ChainId.BASE];
21
21
  export const campaignsCacheUpdater = new Elysia().get("/v3/update", async ({ query }) => {
22
22
  const rawChainId = query.chainId;
23
+ const queryCampaignTypes = query?.campaignTypes ? query.campaignTypes : [];
24
+ return campaignCacheUpdate(rawChainId, queryCampaignTypes, highCampaignsChains);
25
+ }, {
26
+ query: t.Object({
27
+ chainId: t.String(),
28
+ campaignTypes: t.Optional(t.Array(t.Numeric())),
29
+ }),
30
+ });
31
+ export const campaignCacheUpdate = async (rawChainId, queryCampaignTypes, highCampaignsChains) => {
23
32
  let chainId;
24
33
  if (typeof rawChainId === "string") {
25
34
  chainId = Number.parseInt(rawChainId);
@@ -58,8 +67,8 @@ export const campaignsCacheUpdater = new Elysia().get("/v3/update", async ({ que
58
67
  log.local(`Data length after filtering: ${staticData.length}`);
59
68
  if (!!staticData) {
60
69
  // Build list of existing campaign types for this chain
61
- const campaignTypes = !!query?.campaignTypes?.length
62
- ? query.campaignTypes
70
+ const campaignTypes = !!queryCampaignTypes?.length
71
+ ? queryCampaignTypes
63
72
  : !staticData
64
73
  ? []
65
74
  : staticData
@@ -160,9 +169,4 @@ export const campaignsCacheUpdater = new Elysia().get("/v3/update", async ({ que
160
169
  log.error(`❌ update Campaigns cache failed for ${NETWORK_LABELS[chainId]}`, error);
161
170
  }
162
171
  return { success };
163
- }, {
164
- query: t.Object({
165
- chainId: t.String(),
166
- campaignTypes: t.Optional(t.Array(t.Numeric())),
167
- }),
168
- });
172
+ };
@@ -1,4 +1,4 @@
1
- import { Campaign, type CampaignDynamicData } from "@sdk";
1
+ import { type Campaign, type CampaignDynamicData } from "@sdk";
2
2
  export type CampaignsCacheUpdaterReturnType = {
3
3
  [type_mainParameter: string]: {
4
4
  [campaignId: string]: CampaignDynamicData<Campaign>;
@@ -1,22 +1,7 @@
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";
1
+ import { campaignCacheUpdate } from "../../backgroundJobs/jobs/campaignsCacheUpdater";
2
+ import { ChainId, isSupportedChain } from "@sdk";
15
3
  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) : [];
4
+ const campaignTypes = process.env.CAMPAIGN_TYPES ? JSON.parse(process.env.CAMPAIGN_TYPES) : [];
20
5
  const highCampaignsChains = [ChainId.ARBITRUM, ChainId.POLYGON, ChainId.BLAST, ChainId.BASE];
21
6
  export const main = async () => {
22
7
  const rawChainId = process.env.CHAIN_ID;
@@ -29,133 +14,7 @@ export const main = async () => {
29
14
  else {
30
15
  throw new InvalidParameter("Invalid chainId provided");
31
16
  }
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 };
17
+ return campaignCacheUpdate(rawChainId, campaignTypes, highCampaignsChains);
159
18
  };
160
19
  main()
161
20
  .then(success => (success ? process.exit(0) : process.exit(1)))
@@ -148,7 +148,8 @@ const updatePendings = async (data) => {
148
148
  }
149
149
  }
150
150
  const totalRewardIncrease = Object.values(rewardIdToPendingIncrease).reduce((prev, current) => prev + BigInt(current.pendingIncrease), 0n);
151
- log.info(`rewards to create: ${rewardToCreate.length}, to update: ${rewardToUpdate.length}, total reward increase: ${totalRewardIncrease.toString()}`);
151
+ log.info(`total increase: ${totalRewardIncrease.toString()}, rewards to create: ${rewardToCreate.length}, to update: ${rewardToUpdate.length}`);
152
+ log.info(`total increase: ${totalBreakdownIncrease.toString()}, breakdowns to create: ${breakdownToCreate.length}, to update: ${breakdownToUpdate.length}`);
152
153
  if (rewardToCreate.length > 0) {
153
154
  const users = rewardToCreate.map(x => x.recipient);
154
155
  await apiDbClient.user.createMany({ data: users.map(x => ({ address: x, tags: [] })), skipDuplicates: true });
@@ -168,20 +169,21 @@ const updatePendings = async (data) => {
168
169
  }),
169
170
  });
170
171
  }
171
- if (rewardToUpdate.length > 0) {
172
- await apiDbClient.$transaction(rewardToUpdate.map(x => {
173
- const rewardId = Bun.hash(`${data.root}${x.recipient}${rewardTokenId}`).toString();
174
- return apiDbClient.reward.update({
175
- where: {
176
- id: rewardId,
177
- },
178
- data: {
179
- pending: x.pending.toString(),
180
- },
181
- });
182
- }));
172
+ if (breakdownToCreate.length > 0) {
173
+ await apiDbClient.rewardBreakdown.createMany({
174
+ data: breakdownToCreate.map(x => {
175
+ const rewardId = Bun.hash(`${root}${x.recipient}${rewardTokenId}`).toString();
176
+ return {
177
+ reason: x.reason,
178
+ amount: "0",
179
+ pending: x.pending,
180
+ claimed: "0",
181
+ rewardId,
182
+ campaignId,
183
+ };
184
+ }),
185
+ });
183
186
  }
184
- log.info(`breakdowns to create: ${breakdownToCreate.length}, to update: ${breakdownToUpdate.length}, total breakdown increase: ${totalBreakdownIncrease.toString()}`);
185
187
  if (breakdownToUpdate.length > 0) {
186
188
  await apiDbClient.$transaction(breakdownToUpdate.map(x => {
187
189
  return apiDbClient.rewardBreakdown.update({
@@ -198,20 +200,18 @@ const updatePendings = async (data) => {
198
200
  });
199
201
  }));
200
202
  }
201
- if (breakdownToCreate.length > 0) {
202
- await apiDbClient.rewardBreakdown.createMany({
203
- data: breakdownToCreate.map(x => {
204
- const rewardId = Bun.hash(`${root}${x.recipient}${rewardTokenId}`).toString();
205
- return {
206
- reason: x.reason,
207
- amount: "0",
208
- pending: x.pending,
209
- claimed: "0",
210
- rewardId,
211
- campaignId,
212
- };
213
- }),
214
- });
203
+ if (rewardToUpdate.length > 0) {
204
+ await apiDbClient.$transaction(rewardToUpdate.map(x => {
205
+ const rewardId = Bun.hash(`${data.root}${x.recipient}${rewardTokenId}`).toString();
206
+ return apiDbClient.reward.update({
207
+ where: {
208
+ id: rewardId,
209
+ },
210
+ data: {
211
+ pending: x.pending.toString(),
212
+ },
213
+ });
214
+ }));
215
215
  }
216
216
  return { created: breakdownToCreate.length, updated: breakdownToUpdate.length };
217
217
  };