@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.
- package/dist/src/eden/index.d.ts +18 -12
- package/dist/src/engine/campaignTVL/factory.d.ts +3 -0
- package/dist/src/engine/campaignTVL/factory.js +9 -0
- package/dist/src/engine/dynamicData/implementations/ERCMultiToken.js +4 -4
- package/dist/src/errors/InvalidParameter.error.d.ts +4 -0
- package/dist/src/errors/InvalidParameter.error.js +7 -0
- package/dist/src/index.d.ts +6 -4
- package/dist/src/jobs/dynamic-data.js +7 -1
- package/dist/src/jobs/update-dynamic-data.js +17 -21
- package/dist/src/modules/v4/apr/apr.service.d.ts +3 -0
- package/dist/src/modules/v4/apr/apr.service.js +3 -0
- package/dist/src/modules/v4/campaign/campaign.controller.d.ts +1 -1
- package/dist/src/modules/v4/campaign/campaign.controller.js +1 -1
- package/dist/src/modules/v4/campaign/campaign.test.controller.d.ts +3 -3
- package/dist/src/modules/v4/campaign/campaign.test.controller.js +12 -8
- package/dist/src/modules/v4/dynamicData/dynamicData.service.d.ts +14 -5
- package/dist/src/modules/v4/dynamicData/dynamicData.service.js +187 -83
- package/dist/src/modules/v4/programPayload/programPayload.repository.d.ts +3 -1
- package/dist/src/modules/v4/programPayload/programPayload.repository.js +30 -0
- package/dist/src/modules/v4/reward/reward.service.d.ts +4 -1
- package/dist/src/modules/v4/reward/reward.service.js +7 -5
- package/dist/src/modules/v4/router.d.ts +6 -4
- package/dist/src/modules/v4/token/token.service.d.ts +1 -1
- package/dist/src/modules/v4/token/token.service.js +3 -9
- package/dist/src/modules/v4/tvl/tvl.service.d.ts +3 -0
- package/dist/src/modules/v4/tvl/tvl.service.js +3 -0
- package/dist/src/modules/v4/user/user.controller.d.ts +2 -0
- package/dist/src/modules/v4/user/user.controller.js +2 -2
- package/dist/src/modules/v4/user/user.model.d.ts +2 -0
- package/dist/src/modules/v4/user/user.model.js +2 -0
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/backgroundJobs/index.d.ts +0 -1
- package/dist/src/backgroundJobs/index.js +0 -55
- package/dist/src/backgroundJobs/jobs/health.d.ts +0 -41
- package/dist/src/backgroundJobs/jobs/health.js +0 -15
- package/dist/src/backgroundJobs/jobs/opportunityUpdater.d.ts +0 -39
- 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 {
|
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:
|
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(
|
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
|
-
|
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
|
-
|
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.
|
251
|
+
DynamicDataService.fetchWithRecursiveErrorHandling(fn, campaigns.slice(0, mid), chainId),
|
74
252
|
// Process second half with error propagation
|
75
|
-
DynamicDataService.
|
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(
|
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(
|
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(
|
210
|
-
const
|
211
|
-
|
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(),
|