@merkl/api 0.10.310 → 0.10.312

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.
@@ -31,6 +31,11 @@ export class ProgramPayloadService {
31
31
  };
32
32
  }
33
33
  /** building payload for a single campaign */
34
+ static buildCampaignData(query) {
35
+ const distributionChainId = query.distributionChainId ? query.distributionChainId : ChainId.MAINNET;
36
+ const args = buildCampaignPayload(ProgramPayloadService.buildConfig(query), distributionChainId).args;
37
+ return { args };
38
+ }
34
39
  static buildPayload(query, initialCampaignPayload = null) {
35
40
  const rewardToken = query.rewardToken;
36
41
  const distributionChainId = query.distributionChainId ? query.distributionChainId : ChainId.MAINNET;
@@ -93,4 +98,14 @@ export class ProgramPayloadService {
93
98
  }
94
99
  return campaignPayloads;
95
100
  }
101
+ static buildProgramPayloadWithAmounts(query, body) {
102
+ let campaignPayloads = null;
103
+ for (const [campaign, amount] of Object.entries(body)) {
104
+ if (Object.keys(MerklInterfaceCampaigns[query.program]).includes(campaign)) {
105
+ const queryCampaign = { ...query, campaign, amount: amount };
106
+ campaignPayloads = ProgramPayloadService.buildPayload(queryCampaign, campaignPayloads);
107
+ }
108
+ }
109
+ return campaignPayloads;
110
+ }
96
111
  }
@@ -244,19 +244,21 @@ export declare const RewardController: Elysia<"/rewards", false, {
244
244
  };
245
245
  } & {
246
246
  count: {
247
- chains: {
247
+ chain: {
248
248
  get: {
249
249
  body: unknown;
250
250
  params: {};
251
- query: unknown;
251
+ query: {
252
+ chainId: number;
253
+ };
252
254
  headers: {
253
255
  authorization: string;
254
256
  };
255
257
  response: {
256
258
  200: {
257
259
  [x: string]: {
258
- breakdown: number;
259
- rewards: Record<string, number>;
260
+ rewardCount: number;
261
+ breakdownCount: number;
260
262
  };
261
263
  };
262
264
  };
@@ -2,6 +2,7 @@ import { BackOfficeGuard } from "../../../guards/BackOffice.guard";
2
2
  import { AuthorizationHeadersDto, EngineGuard } from "../../../guards/Engine.guard";
3
3
  import { throwOnInvalidRequiredAddress, throwOnUnsupportedChainId } from "../../../utils/throw";
4
4
  import Elysia from "elysia";
5
+ import { ChainDto } from "../accounting";
5
6
  import { CampaignIdDto, CampaignIdWithoutPageDto, CreateManyBreakdownDto, CreateManyRewardDto, RegisterClaimsDto, TokenIdDto, UpdatePendingDto, } from "./reward.model";
6
7
  import { RewardService } from "./reward.service";
7
8
  // ─── Rewards Controller ──────────────────────────────────────────────────────
@@ -101,9 +102,13 @@ export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags:
101
102
  }));
102
103
  })
103
104
  // ─── Get Reward Count By Chain And Root ──────────────────────────────
104
- .get("/count/chains", async () => await RewardService.countAllchains(), {
105
+ .get("/count/chain", async ({ query }) => await RewardService.countOnChain(query.chainId), {
106
+ query: ChainDto,
105
107
  headers: AuthorizationHeadersDto,
106
- beforeHandle: BackOfficeGuard,
108
+ beforeHandle: ({ headers, query }) => {
109
+ BackOfficeGuard({ headers });
110
+ throwOnUnsupportedChainId(query.chainId);
111
+ },
107
112
  detail: { hide: true },
108
113
  })
109
114
  // ─── Unclaimed Data routes ───────────────────────────────────────────
@@ -121,16 +121,10 @@ export declare abstract class RewardRepository {
121
121
  claimed: string;
122
122
  proofs: string[];
123
123
  }) | null>;
124
- static countRewardPerRewardTokenIdAndRoot(): Promise<(Prisma.PickEnumerable<Prisma.RewardGroupByOutputType, ("rewardTokenId" | "root")[]> & {
125
- _count: {
126
- _all: number;
127
- };
128
- })[]>;
129
- static countRewardBreakdownPerCampaignId(): Promise<(Prisma.PickEnumerable<Prisma.RewardBreakdownGroupByOutputType, "campaignId"[]> & {
130
- _count: {
131
- _all: number;
132
- };
133
- })[]>;
124
+ static countRewardAndBreakdownOnChain(chainId: number, root: string): Promise<{
125
+ rewardCount: number;
126
+ breakdownCount: number;
127
+ }>;
134
128
  static updateClaimed(recipient: string, rewardTokenId: string, campaignId: string, reason: string, amount: string): Promise<Prisma.BatchPayload>;
135
129
  static findManyBreakdownUniques(uniques: {
136
130
  rewardId: string;
@@ -115,17 +115,26 @@ export class RewardRepository {
115
115
  },
116
116
  });
117
117
  }
118
- static async countRewardPerRewardTokenIdAndRoot() {
119
- return await apiDbClient.reward.groupBy({
120
- by: ["root", "rewardTokenId"],
121
- _count: { _all: true },
118
+ static async countRewardAndBreakdownOnChain(chainId, root) {
119
+ const rewardCount = await apiDbClient.reward.count({
120
+ where: {
121
+ root,
122
+ RewardToken: {
123
+ chainId,
124
+ },
125
+ },
122
126
  });
123
- }
124
- static async countRewardBreakdownPerCampaignId() {
125
- return await apiDbClient.rewardBreakdown.groupBy({
126
- by: ["campaignId"],
127
- _count: { _all: true },
127
+ const breakdownCount = await apiDbClient.rewardBreakdown.count({
128
+ where: {
129
+ Reward: {
130
+ root,
131
+ RewardToken: {
132
+ chainId,
133
+ },
134
+ },
135
+ },
128
136
  });
137
+ return { rewardCount, breakdownCount };
129
138
  }
130
139
  static async updateClaimed(recipient, rewardTokenId, campaignId, reason, amount) {
131
140
  return await apiDbClient.rewardBreakdown.updateMany({
@@ -558,10 +558,12 @@ export declare abstract class RewardService {
558
558
  created: number;
559
559
  updated: number;
560
560
  }>;
561
- static countAllchains(): Promise<Record<string, {
562
- breakdown: number;
563
- rewards: Record<string, number>;
564
- }>>;
561
+ static countOnChain(chainId: number): Promise<{
562
+ [x: string]: {
563
+ rewardCount: number;
564
+ breakdownCount: number;
565
+ };
566
+ }>;
565
567
  static breakdownForCampaign(query: CampaignIdModel): Promise<import("./reward.model").BreakdownForCampaignsRaw[]>;
566
568
  static countForCampaign(query: CampaignIdModel): Promise<{
567
569
  count: number;
@@ -167,6 +167,7 @@ export class RewardService {
167
167
  if (!lastTreeRewardsBreakdown)
168
168
  continue;
169
169
  breakdown.claimed = lastTreeRewardsBreakdown.amount;
170
+ await RewardRepository.updateClaimed(user, TokenService.hashId(reward.RewardToken), breakdown.campaignId, breakdown.reason, breakdown.amount);
170
171
  }
171
172
  }
172
173
  }
@@ -247,7 +248,8 @@ export class RewardService {
247
248
  }
248
249
  rewardUniques[rewardId].pending = (BigInt(rewardUniques[rewardId].pending) +
249
250
  BigInt(pending) -
250
- BigInt(previousPending ?? "0")).toString();
251
+ BigInt(previousPending ?? "0")) // Store the delta
252
+ .toString();
251
253
  };
252
254
  const breakdownUniques = await data.data.map(({ recipient, reason }) => {
253
255
  const rewardId = RewardService.hashId(data.root, recipient, rewardTokenId);
@@ -255,72 +257,47 @@ export class RewardService {
255
257
  });
256
258
  const breakdownToUpdate = [];
257
259
  const breakdownToCreate = [];
258
- // for (let i = 0; i < breakdownUniques.length; i += 1_000) {
259
- const exists = await RewardRepository.findManyBreakdownUniques(breakdownUniques);
260
- console.log(exists);
260
+ const breakdownExists = await RewardRepository.findManyBreakdownUniques(breakdownUniques);
261
261
  for (const [pointIndex, point] of data.data.entries()) {
262
- updateRewardUniques(point.recipient, point.pending, exists[pointIndex]?.pending);
263
- console.log(pointIndex, !!exists[pointIndex]);
264
- if (!!exists[pointIndex]) {
262
+ updateRewardUniques(point.recipient, point.pending, breakdownExists[pointIndex]?.pending);
263
+ if (!!breakdownExists[pointIndex]) {
265
264
  breakdownToUpdate.push(point);
266
265
  }
267
266
  else {
268
267
  breakdownToCreate.push(point);
269
268
  }
270
269
  }
271
- // }
272
- try {
273
- for (let i = 0; i < Object.keys(rewardUniques).length; i += 1_000) {
274
- const toUpdate = [];
275
- const toCreate = [];
276
- const exists = await RewardRepository.findManyRewardUniques(Object.keys(rewardUniques).slice(i, Math.min(i + 1_000, Object.keys(rewardUniques).length)));
277
- for (const [pointIndex, point] of Object.values(rewardUniques)
278
- .slice(i, Math.min(i + 1_000, Object.keys(rewardUniques).length))
279
- .entries()) {
280
- if (!!exists[pointIndex]) {
281
- toUpdate.push({
282
- ...point,
283
- pending: (BigInt(exists[pointIndex].pending) + BigInt(point.pending)).toString(),
284
- });
285
- }
286
- else {
287
- toCreate.push(point);
288
- }
289
- }
290
- await RewardRepository.updateRewardPendings(rewardTokenId, data.root, toUpdate);
291
- await RewardRepository.createRewardPendings(rewardTokenId, data.root, toCreate);
270
+ const toUpdate = [];
271
+ const toCreate = [];
272
+ const exists = await RewardRepository.findManyRewardUniques(Object.keys(rewardUniques));
273
+ for (const [pointIndex, point] of Object.values(rewardUniques).entries()) {
274
+ if (!!exists[pointIndex]) {
275
+ toUpdate.push({
276
+ ...point,
277
+ pending: (BigInt(exists[pointIndex].pending) + BigInt(point.pending)).toString(), // Store the delta
278
+ });
279
+ }
280
+ else {
281
+ toCreate.push(point);
292
282
  }
293
283
  }
294
- catch (e) {
295
- log.error("updatePendings - error updating reward pendings", e);
296
- }
284
+ await RewardRepository.updateRewardPendings(rewardTokenId, data.root, toUpdate);
285
+ await RewardRepository.createRewardPendings(rewardTokenId, data.root, toCreate);
297
286
  await RewardRepository.updateBreakdownPendings(rewardTokenId, data.root, campaignId, breakdownToUpdate);
298
287
  await RewardRepository.createBreakdownPendings(rewardTokenId, data.root, campaignId, breakdownToCreate);
299
288
  return { created: breakdownToCreate.length, updated: breakdownToUpdate.length };
300
289
  }
301
- static async countAllchains() {
302
- const rewardPerRewardTokenId = await RewardRepository.countRewardPerRewardTokenIdAndRoot();
303
- const rewardBreakdownPerCampaignId = await RewardRepository.countRewardBreakdownPerCampaignId();
304
- const tokenIdToChain = await TokenService.findChains();
305
- const campaignIdToChain = await CampaignService.findChains();
306
- const res = {};
307
- for (const { _count, rewardTokenId, root } of rewardPerRewardTokenId) {
308
- const chainId = tokenIdToChain[rewardTokenId];
309
- if (!chainId)
310
- continue;
311
- if (!res[NETWORK_LABELS[chainId]])
312
- res[NETWORK_LABELS[chainId]] = { rewards: {}, breakdown: 0 };
313
- if (!res[NETWORK_LABELS[chainId]].rewards[root])
314
- res[NETWORK_LABELS[chainId]].rewards[root] = 0;
315
- res[NETWORK_LABELS[chainId]].rewards[root] += _count._all;
316
- }
317
- for (const { _count, campaignId } of rewardBreakdownPerCampaignId) {
318
- const chainId = campaignIdToChain[campaignId];
319
- if (!chainId)
320
- continue;
321
- res[NETWORK_LABELS[chainId]].breakdown += _count._all;
322
- }
323
- return res;
290
+ static async countOnChain(chainId) {
291
+ const roots = await MerklRootService.fetch(chainId);
292
+ const promises = [
293
+ RewardRepository.countRewardAndBreakdownOnChain(chainId, roots.tree),
294
+ RewardRepository.countRewardAndBreakdownOnChain(chainId, roots.lastTree),
295
+ ];
296
+ await Promise.all(promises);
297
+ return {
298
+ [roots.tree]: await promises[0],
299
+ [roots.lastTree]: await promises[1],
300
+ };
324
301
  }
325
302
  static async breakdownForCampaign(query) {
326
303
  const root = await MerklRootService.fetch(query.chainId);
@@ -337,19 +314,25 @@ export class RewardService {
337
314
  return RewardRepository.totalForCampaign(CampaignService.hashId({ distributionChain: query.chainId, campaignId: query.campaignId }), root.live);
338
315
  }
339
316
  static async breakdownForToken(query) {
340
- const root = await MerklRootService.fetch(query.chainId);
341
- const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
342
- return RewardRepository.breakdownForToken(root.live, id, query);
317
+ return CacheService.wrap(TTLPresets.MIN_10, async (query) => {
318
+ const root = await MerklRootService.fetch(query.chainId);
319
+ const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
320
+ return RewardRepository.breakdownForToken(root.live, id, query);
321
+ }, query);
343
322
  }
344
323
  static async countForToken(query) {
345
- const root = await MerklRootService.fetch(query.chainId);
346
- const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
347
- return RewardRepository.countForToken(id, root.live);
324
+ return CacheService.wrap(TTLPresets.MIN_10, async (query) => {
325
+ const root = await MerklRootService.fetch(query.chainId);
326
+ const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
327
+ return RewardRepository.countForToken(id, root.live);
328
+ }, query);
348
329
  }
349
330
  static async totalForToken(query) {
350
- const root = await MerklRootService.fetch(query.chainId);
351
- const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
352
- return RewardRepository.totalForToken(id, root.live);
331
+ return CacheService.wrap(TTLPresets.MIN_10, async (query) => {
332
+ const root = await MerklRootService.fetch(query.chainId);
333
+ const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
334
+ return RewardRepository.totalForToken(id, root.live);
335
+ }, query);
353
336
  }
354
337
  static async getAmountAndClaimedForCampaigns(x) {
355
338
  const currentRoot = await MerklRootService.fetch(x.chainId);
@@ -1364,19 +1364,21 @@ export declare const v4: Elysia<"/v4", false, {
1364
1364
  };
1365
1365
  } & {
1366
1366
  count: {
1367
- chains: {
1367
+ chain: {
1368
1368
  get: {
1369
1369
  body: unknown;
1370
1370
  params: {};
1371
- query: unknown;
1371
+ query: {
1372
+ chainId: number;
1373
+ };
1372
1374
  headers: {
1373
1375
  authorization: string;
1374
1376
  };
1375
1377
  response: {
1376
1378
  200: {
1377
1379
  [x: string]: {
1378
- breakdown: number;
1379
- rewards: Record<string, number>;
1380
+ rewardCount: number;
1381
+ breakdownCount: number;
1380
1382
  };
1381
1383
  };
1382
1384
  };
@@ -2682,6 +2684,31 @@ export declare const v4: Elysia<"/v4", false, {
2682
2684
  };
2683
2685
  };
2684
2686
  };
2687
+ } & {
2688
+ "program-payload": {
2689
+ campaignData: {
2690
+ get: {
2691
+ body: unknown;
2692
+ params: {};
2693
+ query: {
2694
+ creator: string;
2695
+ startTimestamp: number;
2696
+ endTimestamp: number;
2697
+ campaign: string;
2698
+ distributionChainId: number;
2699
+ amount: string;
2700
+ rewardToken: string;
2701
+ program: string;
2702
+ };
2703
+ headers: unknown;
2704
+ response: {
2705
+ 200: {
2706
+ args: any;
2707
+ };
2708
+ };
2709
+ };
2710
+ };
2711
+ };
2685
2712
  } & {
2686
2713
  "program-payload": {
2687
2714
  program: {
@@ -2705,6 +2732,33 @@ export declare const v4: Elysia<"/v4", false, {
2705
2732
  };
2706
2733
  };
2707
2734
  };
2735
+ } & {
2736
+ "program-payload": {
2737
+ program: {
2738
+ withAmounts: {
2739
+ post: {
2740
+ body: {
2741
+ [x: string]: string;
2742
+ };
2743
+ params: {};
2744
+ query: {
2745
+ amount?: string | undefined;
2746
+ creator: string;
2747
+ startTimestamp: number;
2748
+ endTimestamp: number;
2749
+ distributionChainId: number;
2750
+ rewardToken: string;
2751
+ program: string;
2752
+ };
2753
+ headers: unknown;
2754
+ response: {
2755
+ [x: string]: any;
2756
+ 200: any;
2757
+ };
2758
+ };
2759
+ };
2760
+ };
2761
+ };
2708
2762
  };
2709
2763
  } & {
2710
2764
  v4: {