@merkl/api 0.20.110 → 0.20.112

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.
@@ -3162,6 +3162,17 @@ declare const eden: {
3162
3162
  })[];
3163
3163
  }>>;
3164
3164
  };
3165
+ terms: {
3166
+ get: (options: {
3167
+ headers?: Record<string, unknown> | undefined;
3168
+ query: {
3169
+ chainId: number;
3170
+ };
3171
+ fetch?: RequestInit | undefined;
3172
+ }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
3173
+ 200: boolean;
3174
+ }>>;
3175
+ };
3165
3176
  tags: {
3166
3177
  patch: (body: {
3167
3178
  tags: string[];
@@ -8887,6 +8898,26 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
8887
8898
  };
8888
8899
  };
8889
8900
  };
8901
+ } & {
8902
+ users: {
8903
+ ":address": {
8904
+ terms: {
8905
+ get: {
8906
+ body: unknown;
8907
+ params: {
8908
+ address: string;
8909
+ };
8910
+ query: {
8911
+ chainId: number;
8912
+ };
8913
+ headers: unknown;
8914
+ response: {
8915
+ 200: boolean;
8916
+ };
8917
+ };
8918
+ };
8919
+ };
8920
+ };
8890
8921
  } & {
8891
8922
  users: {
8892
8923
  ":address": {
@@ -14289,6 +14320,17 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
14289
14320
  })[];
14290
14321
  }>>;
14291
14322
  };
14323
+ terms: {
14324
+ get: (options: {
14325
+ headers?: Record<string, unknown> | undefined;
14326
+ query: {
14327
+ chainId: number;
14328
+ };
14329
+ fetch?: RequestInit | undefined;
14330
+ }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
14331
+ 200: boolean;
14332
+ }>>;
14333
+ };
14292
14334
  tags: {
14293
14335
  patch: (body: {
14294
14336
  tags: string[];
@@ -12,7 +12,7 @@ export class Erc20TVLBuilder {
12
12
  const builder = erc20SubTypeTVLBuilderFactory(subType);
13
13
  const campaignsOfSubType = campaigns.filter((_campaign, index) => subTypes[index] === subType);
14
14
  if (!!builder) {
15
- log.info(`building TVLs for subtype ${subType}`);
15
+ log.local(`building TVLs for subtype ${subType}`);
16
16
  const subTypeTVLs = await builder.build(computeChainId, campaignsOfSubType);
17
17
  // Impact blacklist or whitelist
18
18
  const calls = [];
@@ -51,6 +51,7 @@ export class Erc20TVLBuilder {
51
51
  const result = await ChainInteractionService(computeChainId).fetchState(calls);
52
52
  let index = 0;
53
53
  for (const [i, { campaign }] of subTypeTVLs.entries()) {
54
+ log.local(`building TVLs for subtype ${subType} using deprecated code`);
54
55
  const { campaignParameters } = campaign;
55
56
  const { whitelist, blacklist } = campaignParameters;
56
57
  if (whitelist?.length > 0) {
@@ -14,6 +14,10 @@ const map = {
14
14
  [Campaign.AMBIENTPROCESSOR]: new AmbiantTVLBuilder(),
15
15
  [Campaign.UNISWAP_V4]: new UniswapV4TVLBuilder(),
16
16
  [Campaign.ERC20]: new Erc20TVLBuilder(),
17
+ [Campaign.ERC20LOGPROCESSOR]: new Erc20TVLBuilder(),
18
+ [Campaign.ERC20REBASEFIXAPR]: new Erc20TVLBuilder(),
19
+ [Campaign.ERC20_FIX_APR]: new Erc20TVLBuilder(),
20
+ [Campaign.EULER]: new Erc20TVLBuilder(),
17
21
  };
18
22
  export const campaignTVLBuilderFactory = (campaignType) => {
19
23
  if (!map[campaignType]) {
@@ -3932,6 +3932,26 @@ declare const app: Elysia<"", false, {
3932
3932
  };
3933
3933
  };
3934
3934
  };
3935
+ } & {
3936
+ users: {
3937
+ ":address": {
3938
+ terms: {
3939
+ get: {
3940
+ body: unknown;
3941
+ params: {
3942
+ address: string;
3943
+ };
3944
+ query: {
3945
+ chainId: number;
3946
+ };
3947
+ headers: unknown;
3948
+ response: {
3949
+ 200: boolean;
3950
+ };
3951
+ };
3952
+ };
3953
+ };
3954
+ };
3935
3955
  } & {
3936
3956
  users: {
3937
3957
  ":address": {
@@ -10,7 +10,7 @@ import { TvlService } from "@/modules/v4/tvl/tvl.service";
10
10
  import bigintToString from "@/utils/bigintToString";
11
11
  import { log } from "@/utils/logger";
12
12
  import { AprType } from "@db/api";
13
- import { Campaign as CampaignEnum, DAY, NETWORK_LABELS, } from "@sdk";
13
+ import { Campaign as CampaignEnum, DAY, NETWORK_LABELS, bigIntToNumber, } from "@sdk";
14
14
  import moment from "moment";
15
15
  import { AprService } from "../apr";
16
16
  import { CampaignService } from "../campaign";
@@ -96,6 +96,7 @@ export class DynamicDataService {
96
96
  let campaignDailyValue = await TokenService.getValueByTokenId(TokenService.hashId({ address: rewardTokenAddress, chainId: record.campaign.chainId }), dailyAmount);
97
97
  // Fixed APR campaigns
98
98
  if (hasFixedAprConfig(campaignParameters)) {
99
+ log.local(`[${NETWORK_LABELS[chainId]}][${CampaignEnum[type]}] calculating fixed APR for ${record.campaign.campaignId}`);
99
100
  const { targetTokenPricing, rewardTokenPricing, apr: fixedApr } = campaignParameters;
100
101
  const targetApr = Number(fixedApr);
101
102
  if (targetTokenPricing && rewardTokenPricing) {
@@ -106,8 +107,11 @@ export class DynamicDataService {
106
107
  if (targetTokenPricing && !rewardTokenPricing) {
107
108
  // Case 2: Fixed # amount of reward per $ of liquidity
108
109
  // Return 0 if reward token price is invalid
109
- campaignDailyValue = !rewardToken.price ? 0 : targetApr * rewardToken.price * tvl.total;
110
- dailyAmount = BigInt((campaignDailyValue / (rewardToken.price ?? 1)) * 10 ** rewardToken.decimals);
110
+ // targetApr represents a yearly # amount of reward per $ of liquidity
111
+ dailyAmount =
112
+ (BigInt(Math.floor((targetApr * tvl.total * 10 ** 6) / 365)) * 10n ** BigInt(rewardToken.decimals)) /
113
+ 10n ** 6n;
114
+ campaignDailyValue = bigIntToNumber(dailyAmount, rewardToken.decimals) * (rewardToken.price ?? 1);
111
115
  }
112
116
  }
113
117
  dailyRewards.total += campaignDailyValue;
@@ -3802,6 +3802,26 @@ export declare const v4: Elysia<"/v4", false, {
3802
3802
  };
3803
3803
  };
3804
3804
  };
3805
+ } & {
3806
+ users: {
3807
+ ":address": {
3808
+ terms: {
3809
+ get: {
3810
+ body: unknown;
3811
+ params: {
3812
+ address: string;
3813
+ };
3814
+ query: {
3815
+ chainId: number;
3816
+ };
3817
+ headers: unknown;
3818
+ response: {
3819
+ 200: boolean;
3820
+ };
3821
+ };
3822
+ };
3823
+ };
3824
+ };
3805
3825
  } & {
3806
3826
  users: {
3807
3827
  ":address": {
@@ -379,6 +379,26 @@ export declare const UserController: Elysia<"/users", false, {
379
379
  };
380
380
  };
381
381
  };
382
+ } & {
383
+ users: {
384
+ ":address": {
385
+ terms: {
386
+ get: {
387
+ body: unknown;
388
+ params: {
389
+ address: string;
390
+ };
391
+ query: {
392
+ chainId: number;
393
+ };
394
+ headers: unknown;
395
+ response: {
396
+ 200: boolean;
397
+ };
398
+ };
399
+ };
400
+ };
401
+ };
382
402
  } & {
383
403
  users: {
384
404
  ":address": {
@@ -3,7 +3,7 @@ import { throwOnInvalidRequiredAddress, throwOnUnsupportedChainId } from "@/util
3
3
  import { Elysia, t } from "elysia";
4
4
  import { CreatorService } from "../creator";
5
5
  import { RewardService } from "../reward";
6
- import { GetManyUserQuery, OptionalChainIdDto, UpdateUserTagsDto, UserDto, UserRewardRouteDto, UserRewardsResourceDto, UserUniqueDto, } from "./user.model";
6
+ import { CheckTerms, GetManyUserQuery, OptionalChainIdDto, UpdateUserTagsDto, UserDto, UserRewardRouteDto, UserRewardsResourceDto, UserUniqueDto, } from "./user.model";
7
7
  import { UserService } from "./user.service";
8
8
  // ─── Users Controller ────────────────────────────────────────────────────────
9
9
  export const UserController = new Elysia({ prefix: "/users", detail: { tags: ["Users"] } })
@@ -78,6 +78,13 @@ export const UserController = new Elysia({ prefix: "/users", detail: { tags: ["U
78
78
  headers: AuthorizationHeadersDto,
79
79
  beforeHandle: BackOfficeGuard,
80
80
  detail: { hide: true },
81
+ })
82
+ // ─── Check if user has signed terms ─────────────────────────────────
83
+ .get("/:address/terms", async ({ query, params }) => await UserService.checkTerms(params?.address, query?.chainId), {
84
+ query: CheckTerms,
85
+ detail: {
86
+ description: "Check if a user address has signed Merkl's terms.",
87
+ },
81
88
  })
82
89
  // ─── Update User's Tags ──────────────────────────────────────────────
83
90
  .patch("/:address/tags", async ({ params, body }) => UserService.updateTags(params.address, body.tags), {
@@ -63,6 +63,9 @@ export declare const GetManyUserQuery: import("@sinclair/typebox").TObject<{
63
63
  page: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
64
64
  items: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
65
65
  }>;
66
+ export declare const CheckTerms: import("@sinclair/typebox").TObject<{
67
+ chainId: import("@sinclair/typebox").TNumber;
68
+ }>;
66
69
  export declare const UpdateUserTagsDto: import("@sinclair/typebox").TObject<{
67
70
  tags: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
68
71
  }>;
@@ -64,6 +64,9 @@ export const GetManyUserQuery = t.Object({
64
64
  page: t.Optional(t.Number()),
65
65
  items: t.Optional(t.Number()),
66
66
  });
67
+ export const CheckTerms = t.Object({
68
+ chainId: t.Numeric(),
69
+ });
67
70
  export const UpdateUserTagsDto = t.Object({
68
71
  tags: t.Array(t.String()),
69
72
  });
@@ -26,6 +26,7 @@ export declare abstract class UserService {
26
26
  address: string;
27
27
  creatorId: string | null;
28
28
  }>;
29
+ static checkTerms(userAddress: string, chainId: number): Promise<boolean>;
29
30
  static syncTags(): Promise<void>;
30
31
  static syncOpportunityTags(): Promise<void>;
31
32
  }
@@ -1,5 +1,6 @@
1
1
  import { log } from "@/utils/logger";
2
2
  import { engineDbClient } from "@db";
3
+ import { ChainInteractionService, DistributionCreatorInterface, registry } from "@sdk";
3
4
  import { OpportunityService } from "../opportunity";
4
5
  import { UserRepository } from "./user.repository";
5
6
  // ─── Users Services ──────────────────────────────────────────────────────────
@@ -25,6 +26,37 @@ export class UserService {
25
26
  }
26
27
  return await UserRepository.updateTags(user, tags);
27
28
  }
29
+ static async checkTerms(userAddress, chainId) {
30
+ const distributorAddress = registry(chainId)?.Merkl?.DistributionCreator;
31
+ if (!distributorAddress)
32
+ throw new Error(`Distributor address not found for chainId ${chainId}`);
33
+ const calls = [
34
+ {
35
+ callData: DistributionCreatorInterface.encodeFunctionData("userSignatureWhitelist", [userAddress]),
36
+ target: distributorAddress,
37
+ allowFailure: false,
38
+ },
39
+ {
40
+ callData: DistributionCreatorInterface.encodeFunctionData("userSignatures", [userAddress]),
41
+ target: distributorAddress,
42
+ allowFailure: false,
43
+ },
44
+ {
45
+ callData: DistributionCreatorInterface.encodeFunctionData("messageHash"),
46
+ target: distributorAddress,
47
+ allowFailure: false,
48
+ },
49
+ ];
50
+ const decoders = (r) => [
51
+ DistributionCreatorInterface.decodeFunctionResult("userSignatureWhitelist", r[0].returnData)[0].toString(),
52
+ DistributionCreatorInterface.decodeFunctionResult("userSignatures", r[1].returnData)[0].toString(),
53
+ DistributionCreatorInterface.decodeFunctionResult("messageHash", r[2].returnData)[0].toString(),
54
+ ];
55
+ const result = await ChainInteractionService(chainId).fetchAndDecodeMultiple(calls, decoders);
56
+ const [signatureWhitelist, userSignature, messageHash] = result;
57
+ const hasNotSigned = signatureWhitelist === "0" && userSignature !== messageHash;
58
+ return !hasNotSigned;
59
+ }
28
60
  static async syncTags() {
29
61
  // 1 - engine -> api db
30
62
  const creatorTags = await engineDbClient.campaignCreators.findMany();