@merkl/api 0.19.35 → 0.19.36

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.
@@ -1752,7 +1752,9 @@ declare const app: Elysia<"", false, {
1752
1752
  withOpportunity?: boolean | undefined;
1753
1753
  createdAfter?: Date | null | undefined;
1754
1754
  };
1755
- headers: unknown;
1755
+ headers: {
1756
+ authorization: string;
1757
+ };
1756
1758
  response: {
1757
1759
  200: {
1758
1760
  [x: string]: number;
@@ -1764,7 +1766,7 @@ declare const app: Elysia<"", false, {
1764
1766
  } & {
1765
1767
  campaigns: {
1766
1768
  count: {
1767
- "by-type": {
1769
+ "by-types": {
1768
1770
  get: {
1769
1771
  body: unknown;
1770
1772
  params: {};
@@ -1792,7 +1794,9 @@ declare const app: Elysia<"", false, {
1792
1794
  withOpportunity?: boolean | undefined;
1793
1795
  createdAfter?: Date | null | undefined;
1794
1796
  };
1795
- headers: unknown;
1797
+ headers: {
1798
+ authorization: string;
1799
+ };
1796
1800
  response: {
1797
1801
  200: {
1798
1802
  [x: string]: number;
@@ -1805,7 +1809,7 @@ declare const app: Elysia<"", false, {
1805
1809
  } & {
1806
1810
  campaigns: {
1807
1811
  count: {
1808
- "by-protocol": {
1812
+ "by-protocols": {
1809
1813
  get: {
1810
1814
  body: unknown;
1811
1815
  params: {};
@@ -1833,7 +1837,9 @@ declare const app: Elysia<"", false, {
1833
1837
  withOpportunity?: boolean | undefined;
1834
1838
  createdAfter?: Date | null | undefined;
1835
1839
  };
1836
- headers: unknown;
1840
+ headers: {
1841
+ authorization: string;
1842
+ };
1837
1843
  response: {
1838
1844
  200: {
1839
1845
  [x: string]: number;
@@ -2661,6 +2667,117 @@ declare const app: Elysia<"", false, {
2661
2667
  };
2662
2668
  };
2663
2669
  };
2670
+ } & {
2671
+ rewards: {
2672
+ total: {
2673
+ distributed: {
2674
+ get: {
2675
+ body: unknown;
2676
+ params: {};
2677
+ query: {
2678
+ since: Date;
2679
+ };
2680
+ headers: {
2681
+ authorization: string;
2682
+ };
2683
+ response: {
2684
+ 200: number;
2685
+ };
2686
+ };
2687
+ };
2688
+ };
2689
+ };
2690
+ } & {
2691
+ rewards: {
2692
+ total: {
2693
+ distributed: {
2694
+ "by-opportunities": {
2695
+ get: {
2696
+ body: unknown;
2697
+ params: {};
2698
+ query: {
2699
+ since: Date;
2700
+ };
2701
+ headers: {
2702
+ authorization: string;
2703
+ };
2704
+ response: {
2705
+ 200: string;
2706
+ };
2707
+ };
2708
+ };
2709
+ };
2710
+ };
2711
+ };
2712
+ } & {
2713
+ rewards: {
2714
+ total: {
2715
+ distributed: {
2716
+ "by-chains": {
2717
+ get: {
2718
+ body: unknown;
2719
+ params: {};
2720
+ query: {
2721
+ since: Date;
2722
+ };
2723
+ headers: {
2724
+ authorization: string;
2725
+ };
2726
+ response: {
2727
+ [x: string]: any;
2728
+ 200: any;
2729
+ };
2730
+ };
2731
+ };
2732
+ };
2733
+ };
2734
+ };
2735
+ } & {
2736
+ rewards: {
2737
+ total: {
2738
+ distributed: {
2739
+ "by-types": {
2740
+ get: {
2741
+ body: unknown;
2742
+ params: {};
2743
+ query: {
2744
+ since: Date;
2745
+ };
2746
+ headers: {
2747
+ authorization: string;
2748
+ };
2749
+ response: {
2750
+ [x: string]: any;
2751
+ 200: any;
2752
+ };
2753
+ };
2754
+ };
2755
+ };
2756
+ };
2757
+ };
2758
+ } & {
2759
+ rewards: {
2760
+ total: {
2761
+ distributed: {
2762
+ "by-protocols": {
2763
+ get: {
2764
+ body: unknown;
2765
+ params: {};
2766
+ query: {
2767
+ since: Date;
2768
+ };
2769
+ headers: {
2770
+ authorization: string;
2771
+ };
2772
+ response: {
2773
+ [x: string]: any;
2774
+ 200: any;
2775
+ };
2776
+ };
2777
+ };
2778
+ };
2779
+ };
2780
+ };
2664
2781
  };
2665
2782
  } & {
2666
2783
  v4: {
@@ -664,7 +664,9 @@ export declare const CampaignController: Elysia<"/campaigns", false, {
664
664
  withOpportunity?: boolean | undefined;
665
665
  createdAfter?: Date | null | undefined;
666
666
  };
667
- headers: unknown;
667
+ headers: {
668
+ authorization: string;
669
+ };
668
670
  response: {
669
671
  200: {
670
672
  [x: string]: number;
@@ -676,7 +678,7 @@ export declare const CampaignController: Elysia<"/campaigns", false, {
676
678
  } & {
677
679
  campaigns: {
678
680
  count: {
679
- "by-type": {
681
+ "by-types": {
680
682
  get: {
681
683
  body: unknown;
682
684
  params: {};
@@ -704,7 +706,9 @@ export declare const CampaignController: Elysia<"/campaigns", false, {
704
706
  withOpportunity?: boolean | undefined;
705
707
  createdAfter?: Date | null | undefined;
706
708
  };
707
- headers: unknown;
709
+ headers: {
710
+ authorization: string;
711
+ };
708
712
  response: {
709
713
  200: {
710
714
  [x: string]: number;
@@ -717,7 +721,7 @@ export declare const CampaignController: Elysia<"/campaigns", false, {
717
721
  } & {
718
722
  campaigns: {
719
723
  count: {
720
- "by-protocol": {
724
+ "by-protocols": {
721
725
  get: {
722
726
  body: unknown;
723
727
  params: {};
@@ -745,7 +749,9 @@ export declare const CampaignController: Elysia<"/campaigns", false, {
745
749
  withOpportunity?: boolean | undefined;
746
750
  createdAfter?: Date | null | undefined;
747
751
  };
748
- headers: unknown;
752
+ headers: {
753
+ authorization: string;
754
+ };
749
755
  response: {
750
756
  200: {
751
757
  [x: string]: number;
@@ -764,7 +770,9 @@ export declare const CampaignController: Elysia<"/campaigns", false, {
764
770
  resolve: {};
765
771
  schema: {
766
772
  body: unknown;
767
- headers: unknown;
773
+ headers: {
774
+ authorization: string;
775
+ };
768
776
  query: {
769
777
  type?: string | undefined;
770
778
  status?: "NONE" | "PAST" | "LIVE" | "SOON" | undefined;
@@ -58,7 +58,7 @@ export const CampaignController = new Elysia({ prefix: "/campaigns", detail: { t
58
58
  .group("/dry-run", app => app
59
59
  // ─── Test Dynamic data computation given a campaignId ───────────────────────
60
60
  .get("/:campaignId/dynamic-data", async ({ params }) => {
61
- const id = (await CampaignService.findMany({ campaignId: params.campaignId, test: true }))[0].id;
61
+ const id = (await CampaignService.findMany({ campaignId: params.campaignId, test: true }))?.[0]?.id;
62
62
  if (!id)
63
63
  throw new NotFoundError();
64
64
  const campaign = await CampaignService.findUniqueOrThrow(id, true);
@@ -68,13 +68,15 @@ export const CampaignController = new Elysia({ prefix: "/campaigns", detail: { t
68
68
  // ─── Test Opportunity creation through a campaign Id and a chain ───────────────────────
69
69
  // @dev Starts from the engine db to debug opportunity creation failing and preventing the api db to be filled
70
70
  .get("/metadata", async ({ query }) => {
71
- const [campaign] = await CampaignService.findEngineCampaigns([
71
+ const engineCampaigns = await CampaignService.findEngineCampaigns([
72
72
  {
73
73
  distributionChain: query.distributionChain,
74
74
  campaignId: query.campaignId,
75
75
  },
76
76
  ]);
77
- return await OpportunityService.createFromCampaign(campaign, false, false);
77
+ if (!engineCampaigns.length)
78
+ throw new NotFoundError("Campaign not found in engine db");
79
+ return await OpportunityService.createFromCampaign(engineCampaigns[0], false, false);
78
80
  }, {
79
81
  beforeHandle: BackOfficeGuard,
80
82
  headers: AuthorizationHeadersDto,
@@ -171,7 +173,12 @@ export const CampaignController = new Elysia({ prefix: "/campaigns", detail: { t
171
173
  <p>This endpoint enables you to count campaigns corresponding to filters specified in the query params.</p>`,
172
174
  },
173
175
  })
174
- .guard({ response: t.Record(t.String(), t.Number()) })
176
+ .guard({
177
+ response: t.Record(t.String(), t.Number()),
178
+ headers: AuthorizationHeadersDto,
179
+ beforeHandle: BackOfficeGuard,
180
+ detail: { hide: true },
181
+ })
175
182
  .get("count/by-chains", async ({ query }) => await CampaignService.countByChains(query))
176
- .get("/count/by-type", async ({ query }) => await CampaignService.countByType(query))
177
- .get("/count/by-protocol", async ({ query }) => await CampaignService.countByProtocol(query));
183
+ .get("/count/by-types", async ({ query }) => await CampaignService.countByType(query))
184
+ .get("/count/by-protocols", async ({ query }) => await CampaignService.countByProtocol(query));
@@ -313,13 +313,13 @@ export class OpportunityRepository {
313
313
  // pagination by offset; might have to change to cursor based for performance
314
314
  const { page: _page, items: _items } = query;
315
315
  const page = _page ? _page : 0;
316
- const items = _items ? _items : 20;
316
+ const items = _items !== undefined ? _items : 20;
317
317
  const withTest = query.test ?? false;
318
318
  const withPoints = query.point ?? false;
319
319
  const withCampaigns = query.campaigns ?? false;
320
320
  const args = OpportunityRepository.#transformQueryToPrismaFilters(query);
321
321
  return await apiDbClient.opportunity.findMany({
322
- take: items,
322
+ take: items === 0 ? undefined : items,
323
323
  skip: page * items,
324
324
  include: {
325
325
  ...OpportunityRepository.#getRecordInclusion(withTest, withPoints),
@@ -75,8 +75,8 @@ export class OpportunityService {
75
75
  ? params.url
76
76
  : !!metadata.depositUrl
77
77
  ? metadata.depositUrl
78
- : !!metadata.mainProtocol && !!protocol.url
79
- ? protocol.url
78
+ : !!metadata.mainProtocol && !!protocol?.url
79
+ ? protocol?.url
80
80
  : undefined,
81
81
  tags,
82
82
  };
@@ -327,6 +327,117 @@ export declare const RewardController: Elysia<"/rewards", false, {
327
327
  };
328
328
  };
329
329
  };
330
+ } & {
331
+ rewards: {
332
+ total: {
333
+ distributed: {
334
+ get: {
335
+ body: unknown;
336
+ params: {};
337
+ query: {
338
+ since: Date;
339
+ };
340
+ headers: {
341
+ authorization: string;
342
+ };
343
+ response: {
344
+ 200: number;
345
+ };
346
+ };
347
+ };
348
+ };
349
+ };
350
+ } & {
351
+ rewards: {
352
+ total: {
353
+ distributed: {
354
+ "by-opportunities": {
355
+ get: {
356
+ body: unknown;
357
+ params: {};
358
+ query: {
359
+ since: Date;
360
+ };
361
+ headers: {
362
+ authorization: string;
363
+ };
364
+ response: {
365
+ 200: string;
366
+ };
367
+ };
368
+ };
369
+ };
370
+ };
371
+ };
372
+ } & {
373
+ rewards: {
374
+ total: {
375
+ distributed: {
376
+ "by-chains": {
377
+ get: {
378
+ body: unknown;
379
+ params: {};
380
+ query: {
381
+ since: Date;
382
+ };
383
+ headers: {
384
+ authorization: string;
385
+ };
386
+ response: {
387
+ [x: string]: any;
388
+ 200: any;
389
+ };
390
+ };
391
+ };
392
+ };
393
+ };
394
+ };
395
+ } & {
396
+ rewards: {
397
+ total: {
398
+ distributed: {
399
+ "by-types": {
400
+ get: {
401
+ body: unknown;
402
+ params: {};
403
+ query: {
404
+ since: Date;
405
+ };
406
+ headers: {
407
+ authorization: string;
408
+ };
409
+ response: {
410
+ [x: string]: any;
411
+ 200: any;
412
+ };
413
+ };
414
+ };
415
+ };
416
+ };
417
+ };
418
+ } & {
419
+ rewards: {
420
+ total: {
421
+ distributed: {
422
+ "by-protocols": {
423
+ get: {
424
+ body: unknown;
425
+ params: {};
426
+ query: {
427
+ since: Date;
428
+ };
429
+ headers: {
430
+ authorization: string;
431
+ };
432
+ response: {
433
+ [x: string]: any;
434
+ 200: any;
435
+ };
436
+ };
437
+ };
438
+ };
439
+ };
440
+ };
330
441
  }, {
331
442
  derive: {};
332
443
  resolve: {};
@@ -334,5 +445,16 @@ export declare const RewardController: Elysia<"/rewards", false, {
334
445
  }, {
335
446
  derive: {};
336
447
  resolve: {};
337
- schema: {};
448
+ schema: {
449
+ body: unknown;
450
+ headers: {
451
+ authorization: string;
452
+ };
453
+ query: {
454
+ since: Date;
455
+ };
456
+ params: unknown;
457
+ cookie: unknown;
458
+ response: {};
459
+ };
338
460
  }>;
@@ -3,8 +3,9 @@ import { AuthorizationHeadersDto, EngineGuard } from "@/guards/Engine.guard";
3
3
  import { ChainDto } from "@/modules/v4/accounting/accounting.model";
4
4
  import { CampaignService } from "@/modules/v4/campaign/campaign.service";
5
5
  import { TokenService } from "@/modules/v4/token/token.service";
6
+ import bigintToString from "@/utils/bigintToString";
6
7
  import { throwOnInvalidRequiredAddress, throwOnUnsupportedChainId } from "@/utils/throw";
7
- import Elysia from "elysia";
8
+ import Elysia, { t } from "elysia";
8
9
  import { CampaignIdDto, CampaignIdWithoutPageDto, CampaignRewardsDto, CreateManyBreakdownDto, CreateManyRewardDto, RegisterClaimsDto, TokenIdDto, } from "./reward.model";
9
10
  import { RewardService } from "./reward.service";
10
11
  // ─── Rewards Controller ──────────────────────────────────────────────────────
@@ -121,4 +122,15 @@ export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags:
121
122
  },
122
123
  detail: { description: "Returns the total of unclaimed rewards for given campaigns" },
123
124
  });
124
- });
125
+ })
126
+ .guard({
127
+ query: t.Object({ since: t.Date() }),
128
+ headers: AuthorizationHeadersDto,
129
+ beforeHandle: BackOfficeGuard,
130
+ detail: { hide: true },
131
+ })
132
+ .get("/total/distributed", async ({ query }) => await RewardService.getTotalDistributed(query.since.getTime() / 1000))
133
+ .get("/total/distributed/by-opportunities", async ({ query }) => JSON.stringify(bigintToString(Array.from((await RewardService.getTotalDistributedByOpportunities(query.since.getTime() / 1000)).entries()))))
134
+ .get("/total/distributed/by-chains", async ({ query }) => await RewardService.getTotalDistributedByChains(query.since.getTime() / 1000))
135
+ .get("/total/distributed/by-types", async ({ query }) => await RewardService.getTotalDistributedByType(query.since.getTime() / 1000))
136
+ .get("/total/distributed/by-protocols", async ({ query }) => await RewardService.getTotalDistributedByProtocol(query.since.getTime() / 1000));
@@ -210,6 +210,13 @@ export declare const RewardV3Dto: import("@sinclair/typebox").TRecord<import("@s
210
210
  symbol: import("@sinclair/typebox").TString;
211
211
  }>>;
212
212
  }>>;
213
+ export declare const QueryTotalDailyRewardsSinceDto: import("@sinclair/typebox").TObject<{
214
+ since: import("@sinclair/typebox").TDate;
215
+ chainId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
216
+ protocol: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
217
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
218
+ }>;
219
+ export type QueryTotalDailyRewardsSinceModel = typeof QueryTotalDailyRewardsSinceDto.static;
213
220
  export type CreateManyRewardModel = typeof CreateManyRewardDto.static;
214
221
  export type CreateManyBreakdownModel = typeof CreateManyBreakdownDto.static;
215
222
  export type RewardEntity = typeof RewardDto.static;
@@ -157,3 +157,9 @@ export const RewardV3Dto = t.Record(t.String({ title: "Chain" }), t.Object({
157
157
  symbol: t.String(),
158
158
  })),
159
159
  }), { $id: "Rewards", additionalProperties: true });
160
+ export const QueryTotalDailyRewardsSinceDto = t.Object({
161
+ since: t.Date(),
162
+ chainId: t.Optional(t.Numeric()),
163
+ protocol: t.Optional(t.String()),
164
+ type: t.Optional(t.String()),
165
+ });
@@ -1,4 +1,4 @@
1
- import type { ChainId } from "@sdk";
1
+ import { type ChainId } from "@sdk";
2
2
  import type { BreakdownForCampaignsRaw, CampaignIdModel, CampaignIdWithoutPageModel, CreateManyBreakdownModel, CreateManyRewardModel, TokenIdModel } from "./reward.model";
3
3
  export declare abstract class RewardRepository {
4
4
  static createManyReward(rewards: CreateManyRewardModel): Promise<import("database/api/.generated/runtime/library").GetBatchResult>;
@@ -153,4 +153,12 @@ export declare abstract class RewardRepository {
153
153
  amount: string;
154
154
  claimed: string;
155
155
  }[]>;
156
+ /**
157
+ * Calculates the sum of daily average rewards for a given opportunity since a specified timestamp.
158
+ *
159
+ * @param since - The timestamp (in seconds) from which to start calculating the rewards.
160
+ * @param opportunityId - The ID of the opportunity for which to calculate the rewards.
161
+ * @returns The sum of daily average rewards for the specified opportunity since the given timestamp.
162
+ */
163
+ static sumDailyRewardsAvgByOpportunity(since: number, opportunityId: string): Promise<number>;
156
164
  }
@@ -2,6 +2,7 @@ import { Campaign, Reward, RewardBreakdown } from "@db/api:drizzle";
2
2
  import { TokenService } from "@/modules/v4/token/token.service";
3
3
  import { UserService } from "@/modules/v4/user/user.service";
4
4
  import { apiDbClient } from "@db";
5
+ import { DAY } from "@sdk";
5
6
  import { and, eq, exists, inArray, sql } from "drizzle-orm";
6
7
  import { CampaignService } from "../campaign";
7
8
  import { RewardService } from "./reward.service";
@@ -302,4 +303,30 @@ export class RewardRepository {
302
303
  },
303
304
  });
304
305
  }
306
+ /**
307
+ * Calculates the sum of daily average rewards for a given opportunity since a specified timestamp.
308
+ *
309
+ * @param since - The timestamp (in seconds) from which to start calculating the rewards.
310
+ * @param opportunityId - The ID of the opportunity for which to calculate the rewards.
311
+ * @returns The sum of daily average rewards for the specified opportunity since the given timestamp.
312
+ */
313
+ static async sumDailyRewardsAvgByOpportunity(since, opportunityId) {
314
+ const dateToRewards = new Map();
315
+ const dailyAvg = [];
316
+ const records = await apiDbClient.dailyRewardsRecord.findMany({
317
+ where: { opportunityId, timestamp: { gte: since }, total: { gt: 0 } },
318
+ select: { timestamp: true, total: true },
319
+ });
320
+ if (records.length === 0)
321
+ return 0;
322
+ for (const record of records) {
323
+ const date = Math.floor(Number(record.timestamp) / DAY).toString();
324
+ const rewards = dateToRewards.get(date) ?? [];
325
+ rewards.push(record.total);
326
+ dateToRewards.set(date, rewards);
327
+ }
328
+ for (const rewards of dateToRewards.values())
329
+ dailyAvg.push(rewards.reduce((prev, curr) => prev + curr, 0) / rewards.length);
330
+ return dailyAvg.reduce((prev, curr) => prev + curr, 0);
331
+ }
305
332
  }