@merkl/api 0.20.87 → 0.20.89

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.
Files changed (38) hide show
  1. package/dist/src/eden/index.d.ts +18 -12
  2. package/dist/src/engine/campaignTVL/factory.d.ts +3 -0
  3. package/dist/src/engine/campaignTVL/factory.js +9 -0
  4. package/dist/src/engine/dynamicData/implementations/ERCMultiToken.js +4 -4
  5. package/dist/src/errors/InvalidParameter.error.d.ts +4 -0
  6. package/dist/src/errors/InvalidParameter.error.js +7 -0
  7. package/dist/src/index.d.ts +6 -4
  8. package/dist/src/jobs/dynamic-data.js +7 -1
  9. package/dist/src/jobs/update-dynamic-data.js +17 -21
  10. package/dist/src/modules/v4/apr/apr.service.d.ts +3 -0
  11. package/dist/src/modules/v4/apr/apr.service.js +3 -0
  12. package/dist/src/modules/v4/campaign/campaign.controller.d.ts +1 -1
  13. package/dist/src/modules/v4/campaign/campaign.controller.js +1 -1
  14. package/dist/src/modules/v4/campaign/campaign.test.controller.d.ts +3 -3
  15. package/dist/src/modules/v4/campaign/campaign.test.controller.js +12 -8
  16. package/dist/src/modules/v4/dynamicData/dynamicData.service.d.ts +14 -5
  17. package/dist/src/modules/v4/dynamicData/dynamicData.service.js +187 -83
  18. package/dist/src/modules/v4/programPayload/programPayload.repository.d.ts +3 -1
  19. package/dist/src/modules/v4/programPayload/programPayload.repository.js +30 -0
  20. package/dist/src/modules/v4/reward/reward.service.d.ts +4 -1
  21. package/dist/src/modules/v4/reward/reward.service.js +7 -5
  22. package/dist/src/modules/v4/router.d.ts +6 -4
  23. package/dist/src/modules/v4/token/token.service.d.ts +1 -1
  24. package/dist/src/modules/v4/token/token.service.js +3 -9
  25. package/dist/src/modules/v4/tvl/tvl.service.d.ts +3 -0
  26. package/dist/src/modules/v4/tvl/tvl.service.js +3 -0
  27. package/dist/src/modules/v4/user/user.controller.d.ts +2 -0
  28. package/dist/src/modules/v4/user/user.controller.js +2 -2
  29. package/dist/src/modules/v4/user/user.model.d.ts +2 -0
  30. package/dist/src/modules/v4/user/user.model.js +2 -0
  31. package/dist/tsconfig.package.tsbuildinfo +1 -1
  32. package/package.json +1 -1
  33. package/dist/src/backgroundJobs/index.d.ts +0 -1
  34. package/dist/src/backgroundJobs/index.js +0 -55
  35. package/dist/src/backgroundJobs/jobs/health.d.ts +0 -41
  36. package/dist/src/backgroundJobs/jobs/health.js +0 -15
  37. package/dist/src/backgroundJobs/jobs/opportunityUpdater.d.ts +0 -39
  38. package/dist/src/backgroundJobs/jobs/opportunityUpdater.js +0 -22
@@ -1,15 +1,191 @@
1
+ import { campaignDynamicDataBuilderFactory } from "@/engine/campaignTVL/factory";
1
2
  import { dynamicDataBuilderFactory } from "@/engine/dynamicData/factory";
2
3
  import { HttpError } from "@/errors";
4
+ import { InvalidParameter } from "@/errors/InvalidParameter.error";
3
5
  import { OpportunityRepository } from "@/modules/v4/opportunity/opportunity.repository";
4
6
  import { OpportunityService } from "@/modules/v4/opportunity/opportunity.service";
5
7
  import { RewardService } from "@/modules/v4/reward/reward.service";
6
8
  import { TvlService } from "@/modules/v4/tvl/tvl.service";
7
9
  import bigintToString from "@/utils/bigintToString";
8
10
  import { log } from "@/utils/logger";
9
- import { Campaign, NETWORK_LABELS } from "@sdk";
10
- import { Campaign as CampaignEnum } from "@sdk";
11
+ import { AprType } from "@db/api";
12
+ import { Campaign as CampaignEnum, DAY, NETWORK_LABELS, } from "@sdk";
13
+ import moment from "moment";
11
14
  import { AprService } from "../apr";
15
+ import { CampaignService } from "../campaign";
16
+ import { TokenService } from "../token/token.service";
12
17
  export class DynamicDataService {
18
+ /**
19
+ * @notice Updates all records for opportunities associated to the given campaigns
20
+ *
21
+ * @dev The list must ONLY contain campaigns of the same type and the same computeChainId
22
+ */
23
+ static async update(chainId, type, campaigns, dryRun = false) {
24
+ // 1 - Safety check
25
+ for (const campaign of campaigns) {
26
+ if (campaign.computeChainId !== chainId || campaign.campaignType !== type) {
27
+ throw new InvalidParameter(`Campaign ${campaign.campaignId} is not of type ${type} on chain ${chainId}`);
28
+ }
29
+ }
30
+ // 2 - If the new dynamic data builder is available, use it
31
+ const builder = campaignDynamicDataBuilderFactory(type);
32
+ if (!!builder) {
33
+ // 2.a - Call the builder
34
+ const records = await DynamicDataService.fetchWithRecursiveErrorHandling(builder.build, campaigns, chainId);
35
+ // 2.b - Regroup by opportunity and build records
36
+ const opportunityIds = new Set(records.map(r => OpportunityService.hashId({
37
+ chainId,
38
+ identifier: r.campaign.mainParameter,
39
+ type: CampaignEnum[+type],
40
+ })));
41
+ const now = moment().unix();
42
+ const updates = [];
43
+ for (const opportunityId of opportunityIds) {
44
+ const recordsForOpportunity = records.filter(r => OpportunityService.hashId({
45
+ chainId,
46
+ identifier: r.campaign.mainParameter,
47
+ type: CampaignEnum[+type],
48
+ }) === opportunityId);
49
+ const tvl = {
50
+ timestamp: BigInt(now),
51
+ total: 0,
52
+ breakdowns: [],
53
+ };
54
+ const dailyRewards = {
55
+ timestamp: BigInt(now),
56
+ total: 0,
57
+ breakdowns: [],
58
+ };
59
+ const apr = {
60
+ timestamp: BigInt(now),
61
+ cumulated: 0,
62
+ breakdowns: [],
63
+ };
64
+ for (const record of recordsForOpportunity) {
65
+ // 2.b.1 TVL of the opportunity is the max of all TVLs of the campaigns
66
+ if (record.tvl > tvl.total) {
67
+ tvl.total = record.tvl;
68
+ tvl.breakdowns = record.tvlBreakdown;
69
+ }
70
+ const startTimestamp = record.campaign.startTimestamp;
71
+ const endTimestamp = record.campaign.endTimestamp;
72
+ const rewardToken = record.campaign.rewardToken;
73
+ try {
74
+ // 2.b.2 Daily rewards is the sum of all daily rewards of the campaigns
75
+ const timespan = endTimestamp - startTimestamp;
76
+ const isWithinTimespan = moment().unix() > startTimestamp && moment().unix() < endTimestamp;
77
+ const dayspan = Math.max(1, Math.floor(timespan / DAY));
78
+ const dailyAmount = isWithinTimespan ? BigInt(record.campaign.amount) / BigInt(dayspan) : BigInt(0);
79
+ let token;
80
+ try {
81
+ token = await TokenService.findUniqueOrThrow({ address: rewardToken, chainId });
82
+ }
83
+ catch {
84
+ await TokenService.findManyOrCreate([{ address: rewardToken, chainId }]);
85
+ token = await TokenService.findUniqueOrThrow({ address: rewardToken, chainId });
86
+ }
87
+ const campaignDailyValue = await TokenService.getValueByTokenId(TokenService.hashId({ address: rewardToken, chainId }), dailyAmount);
88
+ dailyRewards.total += campaignDailyValue;
89
+ dailyRewards.breakdowns.push({
90
+ campaignId: CampaignService.hashId({
91
+ campaignId: record.campaign.campaignId,
92
+ distributionChain: record.campaign.chainId,
93
+ }),
94
+ value: campaignDailyValue,
95
+ amount: dailyAmount,
96
+ token,
97
+ });
98
+ // 2.b.3 APR is obtained from daily rewards and tvl following the distribution type
99
+ // TODO: switch on distribution type - dailyRewards can be in token amount
100
+ apr.cumulated += campaignDailyValue / record.tvl;
101
+ apr.breakdowns.push({
102
+ identifier: record.campaign.campaignId,
103
+ type: AprType.CAMPAIGN,
104
+ value: campaignDailyValue / record.tvl,
105
+ });
106
+ }
107
+ catch (_err) {
108
+ log.warn(`token ${rewardToken} not found`);
109
+ }
110
+ updates.push({
111
+ opportunityId,
112
+ tvl,
113
+ apr,
114
+ dailyRewards,
115
+ });
116
+ }
117
+ updates.push({
118
+ opportunityId,
119
+ tvl,
120
+ apr,
121
+ dailyRewards,
122
+ });
123
+ }
124
+ // 2.c - Update the records
125
+ for (const update of updates) {
126
+ try {
127
+ if (!dryRun)
128
+ await OpportunityRepository.updateDynamicData(update.opportunityId, update.apr, update.tvl, update.dailyRewards);
129
+ }
130
+ catch (err) {
131
+ throw new HttpError("Failed to update dynamic data", 500, {
132
+ err,
133
+ chainId,
134
+ type,
135
+ });
136
+ }
137
+ }
138
+ }
139
+ else {
140
+ const dynamicDataArray = [];
141
+ const dynamicData = await DynamicDataService.fetchWithRecursiveErrorHandling(dynamicDataBuilderFactory(type).build, campaigns, chainId);
142
+ const oppMap = {};
143
+ for (const data of dynamicData) {
144
+ if (!!data) {
145
+ // Main Parameter OVERRIDING
146
+ if (data.campaignType === CampaignEnum.SILO && data.campaignParameters.whitelist?.length === 1)
147
+ data.mainParameter = `${data.mainParameter}-${data.campaignParameters.whitelist[0]}`;
148
+ if (!oppMap[`${data.campaignType}_${data.mainParameter}`])
149
+ oppMap[`${data.campaignType}_${data.mainParameter}`] = {};
150
+ oppMap[`${data.campaignType}_${data.mainParameter}`][data.campaignId] = data;
151
+ }
152
+ }
153
+ for (const entry of Object.entries(oppMap)) {
154
+ const [type, mainParameter] = entry[0].split("_");
155
+ const apr = AprService.extractFromDynamicData(+type, Object.values(entry[1]));
156
+ const tvl = TvlService.extractFromDynamicData(+type, Object.values(entry[1]));
157
+ const dailyRewards = await RewardService.extractDailyRewardsRecordFromDynamicData(+type, Object.values(entry[1]));
158
+ const opportunityId = OpportunityService.hashId({
159
+ chainId,
160
+ identifier: mainParameter,
161
+ type: CampaignEnum[+type],
162
+ });
163
+ try {
164
+ if (!dryRun)
165
+ await OpportunityRepository.updateDynamicData(opportunityId, apr, tvl, dailyRewards);
166
+ }
167
+ catch (err) {
168
+ throw new HttpError("Failed to update dynamic data", 500, {
169
+ err,
170
+ opportunityId,
171
+ campaigns: Object.values(entry[1]),
172
+ dynamicData: bigintToString({
173
+ campaignId: Object.values(entry[1])[0].campaignId,
174
+ apr,
175
+ tvl,
176
+ dailyRewards,
177
+ }),
178
+ });
179
+ }
180
+ dynamicDataArray.push(bigintToString({ campaignId: Object.values(entry[1])[0].campaignId, apr, tvl, dailyRewards }));
181
+ }
182
+ log.info(`[${NETWORK_LABELS[chainId]}][${CampaignEnum[type]}] updated ${dynamicData.length}/${campaigns.length} campaigns`);
183
+ return dynamicDataArray;
184
+ }
185
+ }
186
+ /**
187
+ * @dev Test function used to create mock ERC20 static campaigns and check tvl and metadata
188
+ */
13
189
  static async queryERC20DynamicData(chainId, tokenAddress, rewardTokenAddress, symbolRewardToken, decimals = 18) {
14
190
  const campaigns = [
15
191
  {
@@ -38,11 +214,11 @@ export class DynamicDataService {
38
214
  symbolTargetToken: "EXT",
39
215
  targetToken: tokenAddress,
40
216
  },
41
- campaignType: Campaign.ERC20,
217
+ campaignType: CampaignEnum.ERC20,
42
218
  },
43
219
  ];
44
220
  log.info(`querying mock campaign on chain: ${NETWORK_LABELS[chainId]}`);
45
- const result = await dynamicDataBuilderFactory(Campaign.ERC20).build(Number(chainId), campaigns);
221
+ const result = await dynamicDataBuilderFactory(CampaignEnum.ERC20).build(Number(chainId), campaigns);
46
222
  return {
47
223
  priceTargetToken: result[0]?.typeInfo.priceTargetToken,
48
224
  totalSupply: result[0]?.typeInfo.totalSupply,
@@ -52,13 +228,15 @@ export class DynamicDataService {
52
228
  type: result[0]?.type ?? "defaultType",
53
229
  };
54
230
  }
55
- static async getDynamicData(campaigns, type, chainId) {
231
+ /**
232
+ * @dev Recursive function to handle errors in fetching dynamic data
233
+ */
234
+ static async fetchWithRecursiveErrorHandling(fn, campaigns, chainId) {
56
235
  // Base case: empty input
57
236
  if (campaigns.length === 0)
58
237
  return [];
59
238
  try {
60
- const campaignType = typeof type === "number" ? type : Campaign[type];
61
- return await dynamicDataBuilderFactory(campaignType).build(chainId, campaigns);
239
+ return await fn(chainId, campaigns);
62
240
  }
63
241
  catch (error) {
64
242
  // Base case: single failing campaign
@@ -70,85 +248,11 @@ export class DynamicDataService {
70
248
  const mid = Math.ceil(campaigns.length / 2);
71
249
  const [firstResults, secondResults] = await Promise.all([
72
250
  // Process first half with error propagation
73
- DynamicDataService.getDynamicData(campaigns.slice(0, mid), type, chainId),
251
+ DynamicDataService.fetchWithRecursiveErrorHandling(fn, campaigns.slice(0, mid), chainId),
74
252
  // Process second half with error propagation
75
- DynamicDataService.getDynamicData(campaigns.slice(mid), type, chainId),
253
+ DynamicDataService.fetchWithRecursiveErrorHandling(fn, campaigns.slice(mid), chainId),
76
254
  ]);
77
255
  return [...firstResults, ...secondResults];
78
256
  }
79
257
  }
80
- static async updateForCampaignType(campaigns, type, dryRun = false) {
81
- const chainId = campaigns[0].computeChainId;
82
- const dynamicDataArray = [];
83
- const dynamicData = await DynamicDataService.getDynamicData(campaigns, typeof type === "number" ? type : Campaign[type], chainId);
84
- const oppMap = {};
85
- for (const data of dynamicData) {
86
- if (!!data) {
87
- // Main Parameter OVERRIDING
88
- if (data.campaignType === CampaignEnum.SILO && data.campaignParameters.whitelist?.length === 1)
89
- data.mainParameter = `${data.mainParameter}-${data.campaignParameters.whitelist[0]}`;
90
- if (!oppMap[`${data.campaignType}_${data.mainParameter}`])
91
- oppMap[`${data.campaignType}_${data.mainParameter}`] = {};
92
- oppMap[`${data.campaignType}_${data.mainParameter}`][data.campaignId] = data;
93
- }
94
- }
95
- for (const entry of Object.entries(oppMap)) {
96
- const [type, mainParameter] = entry[0].split("_");
97
- const apr = AprService.extractFromDynamicData(+type, Object.values(entry[1]));
98
- const tvl = TvlService.extractFromDynamicData(+type, Object.values(entry[1]));
99
- const dailyRewards = await RewardService.extractDailyRewardsRecordFromDynamicData(+type, Object.values(entry[1]));
100
- const opportunityId = OpportunityService.hashId({
101
- chainId,
102
- identifier: mainParameter,
103
- type: Campaign[+type],
104
- });
105
- try {
106
- if (!dryRun)
107
- await OpportunityRepository.updateDynamicData(opportunityId, apr, tvl, dailyRewards);
108
- }
109
- catch (err) {
110
- throw new HttpError("Failed to update dynamic data", 500, {
111
- err,
112
- opportunityId,
113
- campaigns: Object.values(entry[1]),
114
- dynamicData: bigintToString({
115
- campaignId: Object.values(entry[1])[0].campaignId,
116
- apr,
117
- tvl,
118
- dailyRewards,
119
- }),
120
- });
121
- }
122
- dynamicDataArray.push(bigintToString({ campaignId: Object.values(entry[1])[0].campaignId, apr, tvl, dailyRewards }));
123
- }
124
- log.info(`[${NETWORK_LABELS[chainId]}][${CampaignEnum[type]}] Updated ${dynamicData.length}/${campaigns.length} campaigns`);
125
- return dynamicDataArray;
126
- }
127
- static async updateForCampaigns(campaigns, dryRun = false) {
128
- const campaignTypeToCampaigns = new Map();
129
- for (const campaign of campaigns) {
130
- const type = campaign.campaignType;
131
- const campaigns = campaignTypeToCampaigns.get(campaign.campaignType) ?? [];
132
- campaigns.push(campaign);
133
- campaignTypeToCampaigns.set(type, campaigns);
134
- }
135
- const dynamicDataArray = [];
136
- for (const [campaignType, campaigns] of campaignTypeToCampaigns.entries()) {
137
- try {
138
- try {
139
- dynamicDataArray.push(await DynamicDataService.updateForCampaignType(campaigns, campaignType, dryRun));
140
- }
141
- catch (err) {
142
- console.error(`Failed to update dynamic data for campaign type ${CampaignEnum[campaignType]}`, err);
143
- }
144
- }
145
- catch (err) {
146
- throw new HttpError(`Failed to update dynamic data for campaign type ${CampaignEnum[campaignType]}`, 500, {
147
- err: err,
148
- campaigns: campaigns,
149
- });
150
- }
151
- }
152
- return dynamicDataArray.flat();
153
- }
154
258
  }
@@ -86,7 +86,9 @@ export declare enum swapxCampaigns {
86
86
  Swapx_wstkscUSD_scUSD_wstkscUSD_gauge_Swapx = "Swapx wstkscUSD/scUSD wstkscUSD gauge Swapx 0xEd08f5caD599E7F523d6B3FD598005B43aA003bb",
87
87
  Swapx_wstkscUSD_scUSD_scUSD_gauge_Swapx = "Swapx wstkscUSD/scUSD scUSD gauge Swapx 0xEd08f5caD599E7F523d6B3FD598005B43aA003bb",
88
88
  Swapx_wstkscUSD_bUSDCe20_wstkscUSD_gauge_Swapx = "Swapx wstkscUSD/bUSDC.e-20 wstkscUSD gauge Swapx 0x85279f76f6ce5bb26f721931ba4e3188cd28ad51",
89
- Swapx_wstkscUSD_bUSDCe20_bUSDCe20_gauge_Swapx = "Swapx wstkscUSD/bUSDC.e-20 bUSDC.e-20 gauge Swapx 0x85279f76f6ce5bb26f721931ba4e3188cd28ad51"
89
+ Swapx_wstkscUSD_bUSDCe20_bUSDCe20_gauge_Swapx = "Swapx wstkscUSD/bUSDC.e-20 bUSDC.e-20 gauge Swapx 0x85279f76f6ce5bb26f721931ba4e3188cd28ad51",
90
+ Swapx_wstkscUSD_aSonUSDC_aSonUSDC_gauge_Swapx = "Swapx wstkscUSD/aSonUSDC aSonUSDC gauge Swapx 0xf248b0EF6d45Aa492C73699B71748b5D1a6770C6",
91
+ Swapx_wstkscUSD_aSonUSDC_wstkscUSD_gauge_Swapx = "Swapx wstkscUSD/aSonUSDC wstkscUSD gauge Swapx 0xF2a497F783C6bfEe0670757462a31f9429fdE53d"
90
92
  }
91
93
  export declare enum celoCampaigns {
92
94
  UniswapV3_cUSD_USDT_Celo = "UniswapV3 cUSD/USDT Celo 0x5dC631aD6C26BEA1a59fBF2C2680CF3df43d249f",
@@ -153,6 +153,8 @@ export var swapxCampaigns;
153
153
  swapxCampaigns["Swapx_wstkscUSD_scUSD_scUSD_gauge_Swapx"] = "Swapx wstkscUSD/scUSD scUSD gauge Swapx 0xEd08f5caD599E7F523d6B3FD598005B43aA003bb";
154
154
  swapxCampaigns["Swapx_wstkscUSD_bUSDCe20_wstkscUSD_gauge_Swapx"] = "Swapx wstkscUSD/bUSDC.e-20 wstkscUSD gauge Swapx 0x85279f76f6ce5bb26f721931ba4e3188cd28ad51";
155
155
  swapxCampaigns["Swapx_wstkscUSD_bUSDCe20_bUSDCe20_gauge_Swapx"] = "Swapx wstkscUSD/bUSDC.e-20 bUSDC.e-20 gauge Swapx 0x85279f76f6ce5bb26f721931ba4e3188cd28ad51";
156
+ swapxCampaigns["Swapx_wstkscUSD_aSonUSDC_aSonUSDC_gauge_Swapx"] = "Swapx wstkscUSD/aSonUSDC aSonUSDC gauge Swapx 0xf248b0EF6d45Aa492C73699B71748b5D1a6770C6";
157
+ swapxCampaigns["Swapx_wstkscUSD_aSonUSDC_wstkscUSD_gauge_Swapx"] = "Swapx wstkscUSD/aSonUSDC wstkscUSD gauge Swapx 0xF2a497F783C6bfEe0670757462a31f9429fdE53d";
156
158
  })(swapxCampaigns || (swapxCampaigns = {}));
157
159
  export var celoCampaigns;
158
160
  (function (celoCampaigns) {
@@ -932,6 +934,34 @@ const EtherlinkInterfaceCampaigns = {
932
934
  },
933
935
  };
934
936
  const SwapxInterfaceCampaigns = {
937
+ [swapxCampaigns.Swapx_wstkscUSD_aSonUSDC_wstkscUSD_gauge_Swapx]: {
938
+ campaignType: Campaign.CLAMM,
939
+ computeChainId: ChainId.SONIC,
940
+ hooks: [],
941
+ poolAddress: "0x151283BEfA4301619b1Ec2BdAA23A2e49B41D8E7",
942
+ whitelist: ["0xF2a497F783C6bfEe0670757462a31f9429fdE53d"],
943
+ blacklist: [],
944
+ url: "https://swapx.fi/earn?ownerType=pools&filter=conc-liquidity",
945
+ forwarders: [],
946
+ isOutOfRangeIncentivized: false,
947
+ weightFees: 2000,
948
+ weightToken0: 4000,
949
+ weightToken1: 4000,
950
+ },
951
+ [swapxCampaigns.Swapx_wstkscUSD_aSonUSDC_aSonUSDC_gauge_Swapx]: {
952
+ campaignType: Campaign.CLAMM,
953
+ computeChainId: ChainId.SONIC,
954
+ hooks: [],
955
+ poolAddress: "0x151283BEfA4301619b1Ec2BdAA23A2e49B41D8E7",
956
+ whitelist: ["0xf248b0EF6d45Aa492C73699B71748b5D1a6770C6"],
957
+ blacklist: [],
958
+ url: "https://swapx.fi/earn?ownerType=pools&filter=conc-liquidity",
959
+ forwarders: [],
960
+ isOutOfRangeIncentivized: false,
961
+ weightFees: 2000,
962
+ weightToken0: 4000,
963
+ weightToken1: 4000,
964
+ },
935
965
  [swapxCampaigns.Swapx_SWPx_USDCe_Swapx]: {
936
966
  campaignType: Campaign.CLAMM,
937
967
  computeChainId: ChainId.SONIC,
@@ -744,7 +744,7 @@ export declare abstract class RewardService {
744
744
  claimed: string;
745
745
  proofs: string[];
746
746
  })[]>;
747
- static getUserRewardsByChain(user: string, withToken: boolean, chainFilter?: ChainId[], connectedChainId?: MerklChainId | null, withTestTokens?: boolean): Promise<{
747
+ static getUserRewardsByChain(user: string, withToken: boolean, chainFilter?: ChainId[], connectedChainId?: MerklChainId | null, withTestTokens?: boolean, claimableOnly?: boolean): Promise<{
748
748
  chain: Chain;
749
749
  rewards: Awaited<ReturnType<(typeof RewardService)["format"]>>;
750
750
  }[]>;
@@ -782,5 +782,8 @@ export declare abstract class RewardService {
782
782
  claimed: string;
783
783
  }[]>;
784
784
  static getUnclaimed(x: CampaignIdWithoutPageModel): Promise<Record<string, string>>;
785
+ /**
786
+ * @deprecated
787
+ */
785
788
  static extractDailyRewardsRecordFromDynamicData<C extends CampaignEnum>(type: C, dynamicData: CampaignDynamicData<C>[], timestamp?: bigint): Promise<DailyRewardsRecord["model"]>;
786
789
  }
@@ -226,7 +226,7 @@ export class RewardService {
226
226
  }
227
227
  return rewards;
228
228
  }
229
- static async getUserRewardsByChain(user, withToken, chainFilter = [], connectedChainId = null, withTestTokens = false) {
229
+ static async getUserRewardsByChain(user, withToken, chainFilter = [], connectedChainId = null, withTestTokens = false, claimableOnly = false) {
230
230
  const chains = await ChainService.findMany({});
231
231
  let chainIds = !chainFilter || !chainFilter.length
232
232
  ? chains.map(({ id }) => id)
@@ -244,7 +244,8 @@ export class RewardService {
244
244
  .filter(({ status }) => status === "fulfilled")
245
245
  .map(x => x.value);
246
246
  /** Load rewards from api DB */
247
- const rewards = (await RewardService.getByRecipient(user, merkleRoots.map(({ live }) => live), withToken, withTestTokens)).filter(reward => chainIds.includes(reward.RewardToken.chainId));
247
+ const rewards = (await RewardService.getByRecipient(user, merkleRoots.map(({ live }) => live), withToken, withTestTokens)).filter(reward => chainIds.includes(reward.RewardToken.chainId) &&
248
+ (claimableOnly ? BigInt(reward.amount) - BigInt(reward.claimed) > 0n : true));
248
249
  const promises = [];
249
250
  for (const [index, chainId] of chainIds.entries()) {
250
251
  const chain = chains.find(chain => chain.id === chainId);
@@ -353,6 +354,9 @@ export class RewardService {
353
354
  return acc;
354
355
  }, {});
355
356
  }
357
+ /**
358
+ * @deprecated
359
+ */
356
360
  static async extractDailyRewardsRecordFromDynamicData(type, dynamicData, timestamp = BigInt(moment().unix())) {
357
361
  const typesWithoutApr = [CampaignEnum.INVALID];
358
362
  if (typesWithoutApr.includes(type))
@@ -366,8 +370,6 @@ export class RewardService {
366
370
  const dayspan = Math.max(1, Math.floor(timespan / DAY));
367
371
  const dailyAmount = isWithinTimespan ? BigInt(amount) / BigInt(dayspan) : BigInt(0);
368
372
  const campaignIdInDb = CampaignService.hashId({ campaignId, distributionChain: chainId });
369
- const tokenToAmount = new Map();
370
- tokenToAmount.set(TokenService.hashId({ address: rewardToken, chainId }), dailyAmount);
371
373
  try {
372
374
  let token;
373
375
  try {
@@ -377,7 +379,7 @@ export class RewardService {
377
379
  await TokenService.findManyOrCreate([{ address: rewardToken, chainId }]);
378
380
  token = await TokenService.findUniqueOrThrow({ address: rewardToken, chainId });
379
381
  }
380
- const campaignDailyValue = await TokenService.getValueByTokenId(tokenToAmount);
382
+ const campaignDailyValue = await TokenService.getValueByTokenId(TokenService.hashId({ address: rewardToken, chainId }), dailyAmount);
381
383
  breakdowns.push({
382
384
  campaignId: campaignIdInDb,
383
385
  value: campaignDailyValue,
@@ -1197,7 +1197,7 @@ export declare const v4: Elysia<"/v4", false, {
1197
1197
  authorization: string;
1198
1198
  };
1199
1199
  response: {
1200
- 200: unknown[];
1200
+ 200: unknown[] | undefined;
1201
1201
  };
1202
1202
  };
1203
1203
  };
@@ -1764,7 +1764,7 @@ export declare const v4: Elysia<"/v4", false, {
1764
1764
  authorization: string;
1765
1765
  };
1766
1766
  response: {
1767
- 200: unknown[];
1767
+ 200: unknown[] | undefined;
1768
1768
  };
1769
1769
  };
1770
1770
  };
@@ -1782,7 +1782,7 @@ export declare const v4: Elysia<"/v4", false, {
1782
1782
  authorization: string;
1783
1783
  };
1784
1784
  response: {
1785
- 200: unknown[];
1785
+ 200: unknown[] | undefined;
1786
1786
  };
1787
1787
  };
1788
1788
  };
@@ -1812,7 +1812,7 @@ export declare const v4: Elysia<"/v4", false, {
1812
1812
  authorization: string;
1813
1813
  };
1814
1814
  response: {
1815
- 200: unknown[];
1815
+ 200: unknown[] | undefined;
1816
1816
  };
1817
1817
  };
1818
1818
  };
@@ -3324,6 +3324,7 @@ export declare const v4: Elysia<"/v4", false, {
3324
3324
  test?: boolean | undefined;
3325
3325
  chainIds?: number[] | undefined;
3326
3326
  reloadChainId?: number | undefined;
3327
+ claimableOnly?: boolean | undefined;
3327
3328
  };
3328
3329
  headers: unknown;
3329
3330
  response: {
@@ -3443,6 +3444,7 @@ export declare const v4: Elysia<"/v4", false, {
3443
3444
  query: {
3444
3445
  test?: boolean | undefined;
3445
3446
  reloadChainId?: number | undefined;
3447
+ claimableOnly?: boolean | undefined;
3446
3448
  chainId: number[];
3447
3449
  };
3448
3450
  headers: unknown;
@@ -161,7 +161,7 @@ export declare abstract class TokenService {
161
161
  address: string;
162
162
  chainId: number;
163
163
  }[]): Promise<number>;
164
- static getValueByTokenId(tokenAmountMap: Map<string, bigint>): Promise<number>;
164
+ static getValueByTokenId(id: string, amount: bigint): Promise<number>;
165
165
  /**
166
166
  * Counts the number of tokens that complies to query
167
167
  * @description used for pagination purposes
@@ -206,15 +206,9 @@ export class TokenService {
206
206
  return sum + value;
207
207
  }, 0);
208
208
  }
209
- static async getValueByTokenId(tokenAmountMap) {
210
- const ids = Array.from(tokenAmountMap.keys());
211
- const tokens = await TokenService.findMany({ id: ids });
212
- if (tokens.length !== ids.length)
213
- throw new Error("Cannot find one or more token(s) in the database.");
214
- let totalValue = 0;
215
- for (const token of tokens)
216
- totalValue += (bigIntToNumber(tokenAmountMap.get(token.id) ?? 0n, token.decimals) ?? 0) * (token.price ?? 0);
217
- return totalValue;
209
+ static async getValueByTokenId(id, amount) {
210
+ const token = await TokenService.findUniqueOrThrow(id);
211
+ return (bigIntToNumber(amount, token.decimals) ?? 0) * (token.price ?? 0);
218
212
  }
219
213
  /**
220
214
  * Counts the number of tokens that complies to query
@@ -2,5 +2,8 @@ import { Campaign, type CampaignDynamicData } from "@sdk";
2
2
  import type { TvlRecord } from "./tvl.model";
3
3
  export declare abstract class TvlService {
4
4
  static hashId(opportunityId: string, timestamp: bigint): string;
5
+ /**
6
+ * @deprecated
7
+ */
5
8
  static extractFromDynamicData<C extends Campaign>(type: C, dynamicData: CampaignDynamicData<C>[], timestamp?: bigint): TvlRecord["model"];
6
9
  }
@@ -5,6 +5,9 @@ export class TvlService {
5
5
  static hashId(opportunityId, timestamp) {
6
6
  return Bun.hash(`${opportunityId}${timestamp}`).toString();
7
7
  }
8
+ /**
9
+ * @deprecated
10
+ */
8
11
  static extractFromDynamicData(type, dynamicData, timestamp = BigInt(moment().unix())) {
9
12
  const typesWithoutApr = [Campaign.INVALID, Campaign.JSON_AIRDROP, Campaign.ERC20_SNAPSHOT];
10
13
  if (typesWithoutApr.includes(type))
@@ -112,6 +112,7 @@ export declare const UserController: Elysia<"/users", false, {
112
112
  test?: boolean | undefined;
113
113
  chainIds?: number[] | undefined;
114
114
  reloadChainId?: number | undefined;
115
+ claimableOnly?: boolean | undefined;
115
116
  };
116
117
  headers: unknown;
117
118
  response: {
@@ -231,6 +232,7 @@ export declare const UserController: Elysia<"/users", false, {
231
232
  query: {
232
233
  test?: boolean | undefined;
233
234
  reloadChainId?: number | undefined;
235
+ claimableOnly?: boolean | undefined;
234
236
  chainId: number[];
235
237
  };
236
238
  headers: unknown;
@@ -25,7 +25,7 @@ export const UserController = new Elysia({ prefix: "/users", detail: { tags: ["U
25
25
  })
26
26
  // ─── Get User's Rewards With Breakdown And Details for our FE ────────
27
27
  .get("/:address/rewards/breakdowns", async ({ params, query }) => {
28
- const rewardsByChain = await RewardService.getUserRewardsByChain(params.address, true, query?.chainIds, query.reloadChainId ?? null, !!query.test ? query.test : false);
28
+ const rewardsByChain = await RewardService.getUserRewardsByChain(params.address, true, query?.chainIds, query.reloadChainId ?? null, !!query.test ? query.test : false, !!query.claimableOnly);
29
29
  return RewardService.splitRewardsBreakdownByOpportunity(rewardsByChain);
30
30
  }, {
31
31
  params: UserUniqueDto,
@@ -42,7 +42,7 @@ export const UserController = new Elysia({ prefix: "/users", detail: { tags: ["U
42
42
  })
43
43
  // ─── Get User's Rewards With Breakdown ──────────────────────────────
44
44
  .get("/:address/rewards", async ({ params, query }) => {
45
- const rewardsByChain = await RewardService.getUserRewardsByChain(params.address, false, typeof query.chainId === "number" ? [query.chainId] : query.chainId, !!query.reloadChainId ? query.reloadChainId : null, !!query.test ? query.test : false);
45
+ const rewardsByChain = await RewardService.getUserRewardsByChain(params.address, false, typeof query.chainId === "number" ? [query.chainId] : query.chainId, !!query.reloadChainId ? query.reloadChainId : null, !!query.test ? query.test : false, !!query.claimableOnly);
46
46
  return RewardService.removeOpportunityFromRewardBreakdown(rewardsByChain);
47
47
  }, {
48
48
  params: UserUniqueDto,
@@ -10,6 +10,7 @@ export declare const UserRewardRouteDto: import("@sinclair/typebox").TObject<{
10
10
  chainId: import("@sinclair/typebox").TTransform<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TNumber>]>, number[]>;
11
11
  reloadChainId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
12
12
  test: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
13
+ claimableOnly: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
13
14
  }>;
14
15
  export declare const UserRewardsResourceDto: import("@sinclair/typebox").TObject<{
15
16
  chain: import("@sinclair/typebox").TObject<{
@@ -46,6 +47,7 @@ export declare const OptionalChainIdDto: import("@sinclair/typebox").TObject<{
46
47
  chainIds: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TTransform<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TNumber>]>, number[]>>;
47
48
  reloadChainId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
48
49
  test: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
50
+ claimableOnly: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
49
51
  }>;
50
52
  export declare const UserDto: import("@sinclair/typebox").TObject<{
51
53
  address: import("@sinclair/typebox").TString;
@@ -10,6 +10,7 @@ export const UserRewardRouteDto = t.Object({
10
10
  description: "An optional chainId to bypass the cache and check if there was very recently a claim on this chain",
11
11
  })),
12
12
  test: t.Optional(t.Boolean({ description: "Include test token rewards" })),
13
+ claimableOnly: t.Optional(t.Boolean({ description: "Include only claimable rewards (to avoid transferring zero amounts)" })),
13
14
  }, {
14
15
  description: "A required comma separated list of chain ids.<br>You can get the list of all supported chains by calling [GET /v4/chains](#tag/chains/GET/v4/chains/)",
15
16
  });
@@ -47,6 +48,7 @@ export const OptionalChainIdDto = t.Object({
47
48
  .Encode(value => [...value])),
48
49
  reloadChainId: t.Optional(t.Numeric()),
49
50
  test: t.Optional(t.Boolean({ description: "Include test token rewards" })),
51
+ claimableOnly: t.Optional(t.Boolean({ description: "Include only claimable rewards (to avoid transferring zero amounts)" })),
50
52
  });
51
53
  export const UserDto = t.Object({
52
54
  address: t.String(),