@merkl/api 0.20.42 → 0.20.43

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.
@@ -9,7 +9,7 @@ import { TokenRepository } from "@/modules/v4/token/token.repository";
9
9
  import { TokenService } from "@/modules/v4/token/token.service";
10
10
  import { log } from "@/utils/logger";
11
11
  import { Pricer } from "@/utils/pricer";
12
- import { BN2Number, BalancerPoolInterface, BalancerV3StablePoolInterface, ChainInteractionService, ERC20Interface, EnzymeInterface, FactoryInterface, LayerBankERC20Interface, MetamorphoInterface, YEAR, getEnv, } from "@sdk";
12
+ import { BN2Number, BalancerPoolInterface, BalancerV3StablePoolInterface, ChainId, ChainInteractionService, ERC20Interface, EnzymeInterface, FactoryInterface, LayerBankERC20Interface, MetamorphoInterface, YEAR, getEnv, } from "@sdk";
13
13
  export class Erc20DynamicData {
14
14
  async build(chainId, campaigns) {
15
15
  const pricer = await Pricer.load();
@@ -126,7 +126,7 @@ export class Erc20DynamicData {
126
126
  log.warn(`failed to update target token price: ${e}`);
127
127
  }
128
128
  i += callsForCampaign;
129
- const whitelistedSupplyTargetToken = tokenTypesByCampaign[campaign.campaignId].typeInfo.whitelistedSupplyTargetToken;
129
+ let whitelistedSupplyTargetToken = tokenTypesByCampaign[campaign.campaignId].typeInfo.whitelistedSupplyTargetToken;
130
130
  const totalSupplyTargetToken = whitelistedSupplyTargetToken;
131
131
  const priceTargetToken = tokenTypesByCampaign[campaign.campaignId].typeInfo.priceTargetToken;
132
132
  let lastEligibilityRatio = 1;
@@ -158,6 +158,9 @@ export class Erc20DynamicData {
158
158
  ]);
159
159
  const rewardToken = rewardTokens[0];
160
160
  apr = rewardToken.isPoint ? apr / 365 / 100 : apr;
161
+ if (campaign.chainId === ChainId.ETHERLINK) {
162
+ whitelistedSupplyTargetToken = tokenTypesByCampaign[campaign.campaignId].typeInfo.totalSupply;
163
+ }
161
164
  dynamicData.push({
162
165
  ...campaign,
163
166
  apr,
@@ -10,7 +10,7 @@ import { Pricer } from "../../../utils/pricer";
10
10
  * @dev important: using the most recent state save with current prices
11
11
  * it's only an estimate
12
12
  */
13
- async function computeEventBasedPoolTVLFromMostRecentStateSave(chainId, campaignID, priceCurrency, decimalsCurrency) {
13
+ async function computeEventBasedPoolRewardsFromMostRecentStateSave(chainId, campaignID, priceCurrency, decimalsCurrency) {
14
14
  let stateSave;
15
15
  let blockNumber;
16
16
  let states = {};
@@ -37,23 +37,22 @@ async function computeEventBasedPoolTVLFromMostRecentStateSave(chainId, campaign
37
37
  }
38
38
  const { fileName, bucketName } = states;
39
39
  // Bucket service
40
- let tvl = 0;
40
+ let distributedRewards = 0;
41
41
  if (!fileName || !bucketName) {
42
- return { tvl, blockNumber: blockNumber };
42
+ return { distributedRewards, blockNumber: blockNumber };
43
43
  }
44
44
  try {
45
45
  const bucket = new BucketService("merkl-production-states", "merkl-production");
46
46
  const storedStates = JSON.parse(await bucket.pull(fileName));
47
47
  for (const [_, { value, params: _params }] of Object.entries(storedStates)) {
48
- tvl += BN2Number(value.allTimeValue, 18 - decimalsCurrency);
48
+ distributedRewards += BN2Number(value.allTimeValue, 18);
49
49
  }
50
- tvl = tvl * priceCurrency;
51
- tvl = Math.max(tvl, 1);
50
+ distributedRewards = Math.max(distributedRewards, 1);
52
51
  }
53
52
  catch {
54
53
  log.warn(`merklDynamic data - failed to decode state of event based on ${NETWORK_LABELS[chainId]}`);
55
54
  }
56
- return { tvl, blockNumber: blockNumber };
55
+ return { distributedRewards, blockNumber: blockNumber };
57
56
  }
58
57
  export class EventBasedDynamicData {
59
58
  async build(chainId, campaigns) {
@@ -67,7 +66,7 @@ export class EventBasedDynamicData {
67
66
  chainId: chainId,
68
67
  symbol: symbolCurrency0,
69
68
  }));
70
- const { tvl } = await computeEventBasedPoolTVLFromMostRecentStateSave(chainId, campaign.campaignId, priceToken, decimalsCurrency0);
69
+ const { distributedRewards } = await computeEventBasedPoolRewardsFromMostRecentStateSave(chainId, campaign.campaignId, priceToken, decimalsCurrency0);
71
70
  const c = campaign;
72
71
  const amount = BN2Number(c.amount, c.campaignParameters.decimalsRewardToken);
73
72
  const multiplier = BN2Number(c.campaignParameters.topicToData[0].multiplier, 12 + 9);
@@ -76,11 +75,12 @@ export class EventBasedDynamicData {
76
75
  const isLive = moment().unix() > startTimestamp && moment().unix() < endTimestamp;
77
76
  let distributionMeanAPR = 0;
78
77
  const priceRewardToken = await TokenService.getRewardTokenPrice(campaign);
78
+ const tvl = distributedRewards / multiplier;
79
79
  if (isLive) {
80
80
  /** Yearly rewards in $ */
81
81
  const fixRewardRate = multiplier * priceRewardToken;
82
82
  distributionMeanAPR = fixRewardRate;
83
- if (tvl * fixRewardRate > (amount * (moment().unix() - startTimestamp)) / (endTimestamp - startTimestamp)) {
83
+ if (distributedRewards > (amount * (moment().unix() - startTimestamp)) / (endTimestamp - startTimestamp)) {
84
84
  distributionMeanAPR =
85
85
  (fixRewardRate * amount * (moment().unix() - startTimestamp)) / (endTimestamp - startTimestamp) / tvl;
86
86
  }
@@ -279,9 +279,13 @@ export class CampaignService {
279
279
  */
280
280
  static format(campaign) {
281
281
  const { DistributionChain, ComputeChain, Creator, RewardToken, params, CampaignStatus, createdAt, manualOverrides: _, ...c } = campaign;
282
+ const updatedParams = params;
283
+ if (ComputeChain.name === "Etherlink") {
284
+ updatedParams.blacklist = [];
285
+ }
282
286
  return {
283
287
  ...c,
284
- params: params,
288
+ params: updatedParams,
285
289
  chain: ComputeChain,
286
290
  endTimestamp: Number(c.endTimestamp),
287
291
  startTimestamp: Number(c.startTimestamp),
@@ -16,7 +16,8 @@ export declare class ProgramPayloadService {
16
16
  safePayload: safePayload;
17
17
  nonEncodedConfig: any;
18
18
  }>;
19
- static checkMinimumAmount(rewardToken: string, args: CampaignParametersStruct, distributionChainId: ChainId): Promise<boolean>;
19
+ static checkMinimumAmount(rewardToken: string, tokenAmount: bigint, numberOfHours: bigint, distributionChainId: ChainId): Promise<boolean>;
20
+ static getMinimumAmount(rewardToken: string, distributionChainId: ChainId): Promise<bigint>;
20
21
  static createSafePayloadForCampaign(args: CampaignParametersStruct, distributionChainId: ChainId, rewardToken: string, distributionCreator: string, withApproval?: boolean): Promise<[approvalTransaction, createCampaignTransaction] | [createCampaignTransaction]>;
21
22
  static initiateSafePayload(distributionChainId: ChainId, distributionCreator: string, rewardToken: string, approvalAmount?: string): safePayload;
22
23
  static buildPayload(query: CampaignPayloadInputModel, initialCampaignPayload?: safePayload | null, totalAmount?: string): Promise<safePayload>;
@@ -71,6 +71,11 @@ export class ProgramPayloadService {
71
71
  // Small hack to avoid the need to parse the config again
72
72
  const args = buildCampaignPayload(parsedConfig, distributionChainId).args;
73
73
  const safePayload = ProgramPayloadService.initiateSafePayload(distributionChainId, distributionCreator, rewardToken);
74
+ const tokenAmount = BigInt(args?.amount.toString());
75
+ const numberOfHours = BigInt(args?.duration.toString()) / 3600n;
76
+ if (!(await ProgramPayloadService.checkMinimumAmount(rewardToken, tokenAmount, numberOfHours, distributionChainId))) {
77
+ throw new Error("Amount is less than minimum");
78
+ }
74
79
  const transactions = await ProgramPayloadService.createSafePayloadForCampaign(args, distributionChainId, rewardToken, distributionCreator ?? NULL_ADDRESS);
75
80
  safePayload.transactions.push(...transactions);
76
81
  if (debug) {
@@ -78,23 +83,25 @@ export class ProgramPayloadService {
78
83
  }
79
84
  return safePayload;
80
85
  }
81
- static async checkMinimumAmount(rewardToken, args, distributionChainId) {
86
+ static async checkMinimumAmount(rewardToken, tokenAmount, numberOfHours, distributionChainId) {
87
+ const minimumAmountPerHour = await ProgramPayloadService.getMinimumAmount(rewardToken, distributionChainId);
88
+ if (!minimumAmountPerHour) {
89
+ throw new Error("Token not found");
90
+ }
91
+ return tokenAmount / numberOfHours >= BigInt(minimumAmountPerHour);
92
+ }
93
+ static async getMinimumAmount(rewardToken, distributionChainId) {
82
94
  const tokenList = await TokenService.getValidRewardTokens(distributionChainId);
83
95
  const minimumAmountPerHour = tokenList.find(token => token.address.toLowerCase() === rewardToken.toLowerCase())?.minimumAmountPerHour;
84
96
  if (!minimumAmountPerHour) {
85
97
  throw new Error("Token not found");
86
98
  }
87
- const tokenAmount = BigInt(args?.amount.toString());
88
- const numberOfHours = BigInt(args?.duration.toString()) / 3600n;
89
- return tokenAmount / numberOfHours >= BigInt(minimumAmountPerHour);
99
+ return BigInt(minimumAmountPerHour);
90
100
  }
91
101
  static async createSafePayloadForCampaign(args, distributionChainId, rewardToken, distributionCreator, withApproval = true) {
92
102
  const safePayload = _.cloneDeep(safeTemplate);
93
103
  safePayload.chainId = distributionChainId.toString();
94
104
  safePayload.createdAt = Math.floor(Date.now() / 1000);
95
- if (!(await ProgramPayloadService.checkMinimumAmount(rewardToken, args, distributionChainId))) {
96
- throw new Error("Amount is less than minimum");
97
- }
98
105
  safePayload.transactions[0].to = rewardToken;
99
106
  safePayload.transactions[0].contractInputsValues = {
100
107
  amount: args?.amount.toString(),
@@ -180,9 +187,16 @@ export class ProgramPayloadService {
180
187
  static async buildProgramPayloadWithAmounts(query, body) {
181
188
  let campaignPayloads = null;
182
189
  const totalAmount = Object.values(body).reduce((sum, amount) => sum + BigInt(amount), 0n);
190
+ const minimumAmountPerHour = await ProgramPayloadService.getMinimumAmount(query.rewardToken, query.distributionChainId);
191
+ const numberOfHours = (BigInt(query.endTimestamp) - BigInt(query.startTimestamp)) / 3600n;
192
+ if (!minimumAmountPerHour) {
193
+ throw new Error("Token not found");
194
+ }
183
195
  for (const [campaign, amount] of Object.entries(body)) {
184
196
  if (Object.keys(MerklInterfaceCampaigns[query.program]).includes(campaign)) {
185
197
  const queryCampaign = { ...query, campaign, amount: amount };
198
+ if (BigInt(amount) / numberOfHours < BigInt(minimumAmountPerHour))
199
+ throw new Error("Amount is less than minimum");
186
200
  campaignPayloads = await ProgramPayloadService.buildPayload(queryCampaign, campaignPayloads, totalAmount.toString());
187
201
  }
188
202
  else {