@merkl/api 0.10.297 → 0.10.299

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.
@@ -2,7 +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 { CampaignIdDto, CampaignIdWithoutPageDto, CreateManyBreakdownDto, CreateManyRewardDto, RegisterClaimsDto, UpdatePendingDto, } from "./reward.model";
5
+ import { CampaignIdDto, CampaignIdWithoutPageDto, CreateManyBreakdownDto, CreateManyRewardDto, RegisterClaimsDto, TokenIdDto, UpdatePendingDto, } from "./reward.model";
6
6
  import { RewardService } from "./reward.service";
7
7
  // ─── Rewards Controller ──────────────────────────────────────────────────────
8
8
  export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags: ["Rewards"] } })
@@ -17,12 +17,49 @@ export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags:
17
17
  },
18
18
  })
19
19
  // ─── Get Total Amount Rewarded For Campaign Id ──────────────────────
20
- .get("/total", async ({ query }) => await RewardService.total(query), {
20
+ .get("/total", async ({ query }) => await RewardService.totalForCampaign(query), {
21
21
  query: CampaignIdDto,
22
22
  beforeHandle: ({ query }) => {
23
23
  throwOnUnsupportedChainId(query.chainId);
24
24
  },
25
25
  detail: { description: "Returns the total amount distributed for a given campaign" },
26
+ })
27
+ // ─── Get Reward Count By Chain And Root For Campaign Id ─────────────
28
+ .get("/count", async ({ query }) => await RewardService.countForCampaign(query), {
29
+ query: CampaignIdDto,
30
+ beforeHandle: ({ query }) => {
31
+ throwOnUnsupportedChainId(query.chainId);
32
+ },
33
+ detail: { hide: true },
34
+ })
35
+ // ─── Get Reward Breakdowns For Token ───────────────────────────────
36
+ .get("/token", async ({ query }) => await RewardService.breakdownForToken(query), {
37
+ query: TokenIdDto,
38
+ beforeHandle: ({ query }) => {
39
+ query.address = throwOnInvalidRequiredAddress(query.address);
40
+ throwOnUnsupportedChainId(query.chainId);
41
+ },
42
+ detail: {
43
+ description: "Returns the all the address that received rewards for a given token, sorted by descending amounts",
44
+ },
45
+ })
46
+ // ─── Get Total Amount Rewarded For Token ───────────────────────────
47
+ .get("/token/total", async ({ query }) => await RewardService.totalForToken(query), {
48
+ query: TokenIdDto,
49
+ beforeHandle: ({ query }) => {
50
+ query.address = throwOnInvalidRequiredAddress(query.address);
51
+ throwOnUnsupportedChainId(query.chainId);
52
+ },
53
+ detail: { description: "Returns the total amount distributed for a given token" },
54
+ })
55
+ // ─── Get Reward Count By Chain And Root For Token ───────────────────
56
+ .get("/token/count", async ({ query }) => await RewardService.countForToken(query), {
57
+ query: TokenIdDto,
58
+ beforeHandle: ({ query }) => {
59
+ query.address = throwOnInvalidRequiredAddress(query.address);
60
+ throwOnUnsupportedChainId(query.chainId);
61
+ },
62
+ detail: { hide: true },
26
63
  })
27
64
  // ─── Create Many Rewards ─────────────────────────────────────────────
28
65
  .post("/engine", async ({ body }) => await RewardService.createManyReward(body), {
@@ -58,14 +95,6 @@ export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags:
58
95
  body: UpdatePendingDto,
59
96
  beforeHandle: EngineGuard,
60
97
  detail: { hide: true },
61
- })
62
- // ─── Get Reward Count By Chain And Root ──────────────────────────────
63
- .get("/count", async ({ query }) => await RewardService.count(query), {
64
- query: CampaignIdDto,
65
- beforeHandle: ({ query }) => {
66
- throwOnUnsupportedChainId(query.chainId);
67
- },
68
- detail: { hide: true },
69
98
  })
70
99
  // ─── Get Reward Count By Chain And Root ──────────────────────────────
71
100
  .get("/count/chains", async () => await RewardService.countAllchains(), {
@@ -167,6 +167,12 @@ export declare const CampaignIdDto: import("@sinclair/typebox").TObject<{
167
167
  page: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
168
168
  items: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
169
169
  }>;
170
+ export declare const TokenIdDto: import("@sinclair/typebox").TObject<{
171
+ chainId: import("@sinclair/typebox").TNumber;
172
+ address: import("@sinclair/typebox").TString;
173
+ page: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
174
+ items: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
175
+ }>;
170
176
  export declare const UserRewardV3Dto: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
171
177
  symbol: import("@sinclair/typebox").TString;
172
178
  decimals: import("@sinclair/typebox").TNumber;
@@ -214,6 +220,7 @@ export type CampaignIdWithoutPageModel = {
214
220
  };
215
221
  export type CampaignIdListModel = typeof CampaignIdListDto.static;
216
222
  export type CampaignIdModel = typeof CampaignIdDto.static;
223
+ export type TokenIdModel = typeof TokenIdDto.static;
217
224
  export type UserRewardV3Model = typeof UserRewardV3Dto.static;
218
225
  export type RewardV3Model = typeof RewardV3Dto.static;
219
226
  export type BreakdownForCampaignsRaw = {
@@ -112,6 +112,16 @@ export const CampaignIdDto = t.Object({
112
112
  page: t.Optional(t.Numeric({ description: "Page number", default: 0 })), // 0-indexed
113
113
  items: t.Optional(t.Numeric({ description: "Number of returned rows", default: 20 })), // items per page
114
114
  });
115
+ export const TokenIdDto = t.Object({
116
+ chainId: t.Numeric({
117
+ description: "Chain ID",
118
+ }),
119
+ address: t.String({
120
+ description: "Token Address",
121
+ }),
122
+ page: t.Optional(t.Numeric({ description: "Page number", default: 0 })), // 0-indexed
123
+ items: t.Optional(t.Numeric({ description: "Number of returned rows", default: 20 })), // items per page
124
+ });
115
125
  export const UserRewardV3Dto = t.Record(t.String({ title: "TokenAddress" }), t.Object({
116
126
  symbol: t.String(),
117
127
  decimals: t.Number(),
@@ -1,6 +1,6 @@
1
1
  import type { Prisma } from "../../../../database/api/.generated";
2
2
  import type { ChainId } from "@sdk";
3
- import type { BreakdownForCampaignsRaw, CampaignIdModel, CampaignIdWithoutPageModel, CreateManyBreakdownModel, CreateManyRewardModel, PendingEntity } from "./reward.model";
3
+ import type { BreakdownForCampaignsRaw, CampaignIdModel, CampaignIdWithoutPageModel, CreateManyBreakdownModel, CreateManyRewardModel, PendingEntity, TokenIdModel } from "./reward.model";
4
4
  export declare abstract class RewardRepository {
5
5
  static createManyReward(rewards: CreateManyRewardModel): Promise<Prisma.BatchPayload>;
6
6
  static createManyBreakdown(data: CreateManyBreakdownModel): Promise<Prisma.BatchPayload>;
@@ -170,16 +170,24 @@ export declare abstract class RewardRepository {
170
170
  }[]>;
171
171
  static findManyRootsWithRewardOnChain(chainId: number): Promise<string[]>;
172
172
  static breakdownForCampaign(root: string, id: string, query: CampaignIdModel): Promise<BreakdownForCampaignsRaw[]>;
173
- static total(campaignId: string, root: string): Promise<{
173
+ static totalForCampaign(campaignId: string, root: string): Promise<{
174
174
  campaignId: string;
175
175
  amount: bigint;
176
176
  }>;
177
+ static countForCampaign(campaignId: string, root: string): Promise<{
178
+ count: number;
179
+ }>;
180
+ static breakdownForToken(root: string, id: string, query: TokenIdModel): Promise<BreakdownForCampaignsRaw[]>;
181
+ static totalForToken(tokenId: string, root: string): Promise<{
182
+ tokenId: string;
183
+ amount: bigint;
184
+ }>;
185
+ static countForToken(tokenId: string, root: string): Promise<{
186
+ count: number;
187
+ }>;
177
188
  static getAmountAndClaimedForCampaigns(root: string, x: CampaignIdWithoutPageModel): Promise<{
178
189
  campaignId: string;
179
190
  amount: string;
180
191
  claimed: string;
181
192
  }[]>;
182
- static count(campaignId: string, root: string): Promise<{
183
- count: number;
184
- }>;
185
193
  }
@@ -284,7 +284,7 @@ export class RewardRepository {
284
284
  `, root, id, items, items * page);
285
285
  return result;
286
286
  }
287
- static async total(campaignId, root) {
287
+ static async totalForCampaign(campaignId, root) {
288
288
  const totalAmount = await apiDbClient.rewardBreakdown.findMany({
289
289
  where: {
290
290
  campaignId,
@@ -304,32 +304,88 @@ export class RewardRepository {
304
304
  }, { campaignId, amount: 0n });
305
305
  return reducedData;
306
306
  }
307
- static async getAmountAndClaimedForCampaigns(root, x) {
308
- return await apiDbClient.rewardBreakdown.findMany({
307
+ static async countForCampaign(campaignId, root) {
308
+ const count = await apiDbClient.rewardBreakdown.count({
309
+ where: {
310
+ campaignId,
311
+ Reward: {
312
+ root,
313
+ },
314
+ },
315
+ });
316
+ return { count };
317
+ }
318
+ static async breakdownForToken(root, id, query) {
319
+ const { page: _page, items: _items } = query;
320
+ const page = _page || 0;
321
+ const items = _items || 50;
322
+ const result = await apiDbClient.$queryRawUnsafe(`
323
+ SELECT
324
+ r."recipient",
325
+ SUM(rb."amount"::numeric)::text AS amount,
326
+ SUM(rb."claimed"::numeric)::text AS claimed,
327
+ SUM(rb."pending"::numeric)::text AS pending
328
+ FROM
329
+ "RewardBreakdown" rb
330
+ INNER JOIN
331
+ "Reward" r ON rb."rewardId" = r."id"
332
+ WHERE
333
+ r."root" = $1 AND r."rewardTokenId" = $2
334
+ GROUP BY
335
+ r."recipient"
336
+ ORDER BY
337
+ (SUM(rb."amount"::numeric) + SUM(rb."pending"::numeric)) DESC
338
+ LIMIT $3
339
+ OFFSET $4
340
+ `, root, id, items, items * page);
341
+ return result;
342
+ }
343
+ static async totalForToken(tokenId, root) {
344
+ const totalAmount = await apiDbClient.rewardBreakdown.findMany({
345
+ where: {
346
+ Reward: {
347
+ rewardTokenId: tokenId,
348
+ root,
349
+ },
350
+ },
309
351
  select: {
310
- claimed: true,
311
352
  amount: true,
353
+ pending: true,
312
354
  campaignId: true,
313
355
  },
356
+ });
357
+ const reducedData = totalAmount.reduce((acc, { amount, pending }) => {
358
+ acc.amount += BigInt(amount) + BigInt(pending ?? 0);
359
+ return acc;
360
+ }, { tokenId, amount: 0n });
361
+ return reducedData;
362
+ }
363
+ static async countForToken(tokenId, root) {
364
+ const count = await apiDbClient.rewardBreakdown.count({
314
365
  where: {
315
- campaignId: {
316
- in: x.campaignIds.map(campaignId => CampaignService.hashId({ distributionChain: x.chainId, campaignId })),
317
- },
318
366
  Reward: {
367
+ rewardTokenId: tokenId,
319
368
  root,
320
369
  },
321
370
  },
322
371
  });
372
+ return { count };
323
373
  }
324
- static async count(campaignId, root) {
325
- const count = await apiDbClient.rewardBreakdown.count({
374
+ static async getAmountAndClaimedForCampaigns(root, x) {
375
+ return await apiDbClient.rewardBreakdown.findMany({
376
+ select: {
377
+ claimed: true,
378
+ amount: true,
379
+ campaignId: true,
380
+ },
326
381
  where: {
327
- campaignId,
382
+ campaignId: {
383
+ in: x.campaignIds.map(campaignId => CampaignService.hashId({ distributionChain: x.chainId, campaignId })),
384
+ },
328
385
  Reward: {
329
386
  root,
330
387
  },
331
388
  },
332
389
  });
333
- return { count };
334
390
  }
335
391
  }
@@ -2,7 +2,7 @@ import type { CacheKeys } from "../../../cache/keys";
2
2
  import type { Chain } from "../../../../database/api/.generated";
3
3
  import { Campaign, type CampaignDynamicData, type ChainId, type MerklChainId } from "@sdk";
4
4
  import { type LightOpportunityFromDB, type Opportunity } from "../opportunity";
5
- import type { CampaignIdModel, CampaignIdWithoutPageModel, CreateManyBreakdownModel, CreateManyRewardModel, DailyRewardsRecord, RegisterClaimsModel, RewardBreakdown, UpdatePendingModel } from "./reward.model";
5
+ import type { CampaignIdModel, CampaignIdWithoutPageModel, CreateManyBreakdownModel, CreateManyRewardModel, DailyRewardsRecord, RegisterClaimsModel, RewardBreakdown, TokenIdModel, UpdatePendingModel } from "./reward.model";
6
6
  import { RewardRepository } from "./reward.repository";
7
7
  export declare abstract class RewardService {
8
8
  static hashId(root: string, recipient: string, rewardTokenId: string): string;
@@ -557,13 +557,21 @@ export declare abstract class RewardService {
557
557
  rewards: Record<string, number>;
558
558
  }>>;
559
559
  static breakdownForCampaign(query: CampaignIdModel): Promise<import("./reward.model").BreakdownForCampaignsRaw[]>;
560
- static count(query: CampaignIdModel): Promise<{
560
+ static countForCampaign(query: CampaignIdModel): Promise<{
561
561
  count: number;
562
562
  }>;
563
- static total(query: CampaignIdModel): Promise<{
563
+ static totalForCampaign(query: CampaignIdModel): Promise<{
564
564
  campaignId: string;
565
565
  amount: bigint;
566
566
  }>;
567
+ static breakdownForToken(query: TokenIdModel): Promise<import("./reward.model").BreakdownForCampaignsRaw[]>;
568
+ static countForToken(query: TokenIdModel): Promise<{
569
+ count: number;
570
+ }>;
571
+ static totalForToken(query: TokenIdModel): Promise<{
572
+ tokenId: string;
573
+ amount: bigint;
574
+ }>;
567
575
  static getAmountAndClaimedForCampaigns(x: CampaignIdWithoutPageModel): Promise<{
568
576
  campaignId: string;
569
577
  amount: string;
@@ -285,14 +285,29 @@ export class RewardService {
285
285
  const id = CampaignService.hashId({ distributionChain: query.chainId, campaignId: query.campaignId });
286
286
  return RewardRepository.breakdownForCampaign(root.live, id, query);
287
287
  }
288
- static async count(query) {
288
+ static async countForCampaign(query) {
289
289
  const root = await MerklRootService.fetch(query.chainId);
290
290
  const id = CampaignService.hashId({ distributionChain: query.chainId, campaignId: query.campaignId });
291
- return RewardRepository.count(id, root.live);
291
+ return RewardRepository.countForCampaign(id, root.live);
292
292
  }
293
- static async total(query) {
293
+ static async totalForCampaign(query) {
294
294
  const root = await MerklRootService.fetch(query.chainId);
295
- return RewardRepository.total(CampaignService.hashId({ distributionChain: query.chainId, campaignId: query.campaignId }), root.live);
295
+ return RewardRepository.totalForCampaign(CampaignService.hashId({ distributionChain: query.chainId, campaignId: query.campaignId }), root.live);
296
+ }
297
+ static async breakdownForToken(query) {
298
+ const root = await MerklRootService.fetch(query.chainId);
299
+ const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
300
+ return RewardRepository.breakdownForToken(root.live, id, query);
301
+ }
302
+ static async countForToken(query) {
303
+ const root = await MerklRootService.fetch(query.chainId);
304
+ const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
305
+ return RewardRepository.countForToken(id, root.live);
306
+ }
307
+ static async totalForToken(query) {
308
+ const root = await MerklRootService.fetch(query.chainId);
309
+ const id = TokenService.hashId({ chainId: query.chainId, address: query.address });
310
+ return RewardRepository.totalForToken(id, root.live);
296
311
  }
297
312
  static async getAmountAndClaimedForCampaigns(x) {
298
313
  const currentRoot = await MerklRootService.fetch(x.chainId);
@@ -1169,6 +1169,85 @@ export declare const v4: Elysia<"/v4", false, {
1169
1169
  };
1170
1170
  };
1171
1171
  };
1172
+ } & {
1173
+ count: {
1174
+ get: {
1175
+ body: unknown;
1176
+ params: {};
1177
+ query: {
1178
+ items?: number | undefined;
1179
+ page?: number | undefined;
1180
+ chainId: number;
1181
+ campaignId: string;
1182
+ };
1183
+ headers: unknown;
1184
+ response: {
1185
+ 200: {
1186
+ count: number;
1187
+ };
1188
+ };
1189
+ };
1190
+ };
1191
+ } & {
1192
+ token: {
1193
+ get: {
1194
+ body: unknown;
1195
+ params: {};
1196
+ query: {
1197
+ items?: number | undefined;
1198
+ page?: number | undefined;
1199
+ chainId: number;
1200
+ address: string;
1201
+ };
1202
+ headers: unknown;
1203
+ response: {
1204
+ 200: import("./reward").BreakdownForCampaignsRaw[];
1205
+ };
1206
+ };
1207
+ };
1208
+ } & {
1209
+ token: {
1210
+ total: {
1211
+ get: {
1212
+ body: unknown;
1213
+ params: {};
1214
+ query: {
1215
+ items?: number | undefined;
1216
+ page?: number | undefined;
1217
+ chainId: number;
1218
+ address: string;
1219
+ };
1220
+ headers: unknown;
1221
+ response: {
1222
+ 200: {
1223
+ tokenId: string;
1224
+ amount: bigint;
1225
+ };
1226
+ };
1227
+ };
1228
+ };
1229
+ };
1230
+ } & {
1231
+ token: {
1232
+ count: {
1233
+ get: {
1234
+ body: unknown;
1235
+ params: {};
1236
+ query: {
1237
+ items?: number | undefined;
1238
+ page?: number | undefined;
1239
+ chainId: number;
1240
+ address: string;
1241
+ };
1242
+ headers: unknown;
1243
+ response: {
1244
+ 200: {
1245
+ count: number;
1246
+ };
1247
+ };
1248
+ };
1249
+ };
1250
+ };
1172
1251
  } & {
1173
1252
  engine: {
1174
1253
  post: {
@@ -1276,25 +1355,6 @@ export declare const v4: Elysia<"/v4", false, {
1276
1355
  };
1277
1356
  };
1278
1357
  };
1279
- } & {
1280
- count: {
1281
- get: {
1282
- body: unknown;
1283
- params: {};
1284
- query: {
1285
- items?: number | undefined;
1286
- page?: number | undefined;
1287
- chainId: number;
1288
- campaignId: string;
1289
- };
1290
- headers: unknown;
1291
- response: {
1292
- 200: {
1293
- count: number;
1294
- };
1295
- };
1296
- };
1297
- };
1298
1358
  } & {
1299
1359
  count: {
1300
1360
  chains: {
@@ -2652,9 +2712,7 @@ export declare const v4: Elysia<"/v4", false, {
2652
2712
  }[];
2653
2713
  params: {};
2654
2714
  query: unknown;
2655
- headers: {
2656
- authorization: string;
2657
- };
2715
+ headers: unknown;
2658
2716
  response: {
2659
2717
  200: {
2660
2718
  address: string;
@@ -72,9 +72,6 @@ export class StatusRepository {
72
72
  return await apiDbClient.campaignStatus.update({
73
73
  where: {
74
74
  campaignId: CampaignService.hashId(campaignUnique),
75
- status: {
76
- not: "PROCESSING", // To throw an error if the status is already PROCESSING
77
- },
78
75
  },
79
76
  data: {
80
77
  status: "PROCESSING",
@@ -19,8 +19,8 @@ export class StatusService {
19
19
  }
20
20
  static async update(campaignUnique, status) {
21
21
  // Check if the status exists already, otherwise create it
22
- const campaignExists = await StatusRepository.findUnique(campaignUnique);
23
- if (!campaignExists) {
22
+ const campaignStatus = await StatusRepository.findUnique(campaignUnique);
23
+ if (!campaignStatus) {
24
24
  let campaign = await CampaignService.findUnique(campaignUnique);
25
25
  if (!campaign) {
26
26
  await CampaignService.fill([campaignUnique]);