@merkl/api 0.20.41 → 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.
Files changed (25) hide show
  1. package/dist/src/eden/index.d.ts +15 -63
  2. package/dist/src/engine/dynamicData/implementations/Erc20.js +5 -2
  3. package/dist/src/engine/dynamicData/implementations/EventBased.js +9 -9
  4. package/dist/src/index.d.ts +3 -15
  5. package/dist/src/modules/v4/campaign/campaign.service.js +5 -1
  6. package/dist/src/modules/v4/coingecko/coingecko.model.d.ts +0 -0
  7. package/dist/src/modules/v4/coingecko/coingecko.model.js +1 -0
  8. package/dist/src/modules/v4/coingecko/coingecko.repository.d.ts +14 -0
  9. package/dist/src/modules/v4/coingecko/coingecko.repository.js +9 -0
  10. package/dist/src/modules/v4/coingecko/coingecko.service.d.ts +16 -0
  11. package/dist/src/modules/v4/coingecko/coingecko.service.js +65 -0
  12. package/dist/src/modules/v4/programPayload/programPayload.service.d.ts +2 -1
  13. package/dist/src/modules/v4/programPayload/programPayload.service.js +21 -7
  14. package/dist/src/modules/v4/router.d.ts +3 -15
  15. package/dist/src/modules/v4/token/token.controller.d.ts +3 -15
  16. package/dist/src/modules/v4/token/token.controller.js +0 -5
  17. package/dist/src/modules/v4/token/token.model.d.ts +1 -0
  18. package/dist/src/modules/v4/token/token.model.js +1 -0
  19. package/dist/src/modules/v4/token/token.repository.js +1 -0
  20. package/dist/src/modules/v4/token/token.service.d.ts +1 -2
  21. package/dist/src/modules/v4/token/token.service.js +21 -37
  22. package/dist/src/scripts/fill-coingecko-data.d.ts +1 -0
  23. package/dist/src/scripts/fill-coingecko-data.js +3 -0
  24. package/dist/tsconfig.package.tsbuildinfo +1 -1
  25. package/package.json +1 -1
@@ -1837,6 +1837,7 @@ declare const eden: {
1837
1837
  isNative?: boolean | undefined;
1838
1838
  test?: boolean | undefined;
1839
1839
  missingIcons?: boolean | undefined;
1840
+ missingPrice?: boolean | undefined;
1840
1841
  };
1841
1842
  fetch?: RequestInit | undefined;
1842
1843
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
@@ -1883,7 +1884,7 @@ declare const eden: {
1883
1884
  isPoint: boolean;
1884
1885
  isNative: boolean;
1885
1886
  price: number | null;
1886
- } | undefined;
1887
+ };
1887
1888
  }>>;
1888
1889
  };
1889
1890
  count: {
@@ -1900,23 +1901,13 @@ declare const eden: {
1900
1901
  isNative?: boolean | undefined;
1901
1902
  test?: boolean | undefined;
1902
1903
  missingIcons?: boolean | undefined;
1904
+ missingPrice?: boolean | undefined;
1903
1905
  };
1904
1906
  fetch?: RequestInit | undefined;
1905
1907
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
1906
1908
  200: number;
1907
1909
  }>>;
1908
1910
  };
1909
- icons: {
1910
- post: (body: unknown, options: {
1911
- headers: {
1912
- authorization: string;
1913
- };
1914
- query?: Record<string, unknown> | undefined;
1915
- fetch?: RequestInit | undefined;
1916
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
1917
- 200: void;
1918
- }>>;
1919
- };
1920
1911
  webhooks: {
1921
1912
  notion: {
1922
1913
  post: (body: {
@@ -5656,6 +5647,7 @@ declare const eden: {
5656
5647
  isNative?: boolean | undefined;
5657
5648
  test?: boolean | undefined;
5658
5649
  missingIcons?: boolean | undefined;
5650
+ missingPrice?: boolean | undefined;
5659
5651
  };
5660
5652
  fetch?: RequestInit | undefined;
5661
5653
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
@@ -5702,7 +5694,7 @@ declare const eden: {
5702
5694
  isPoint: boolean;
5703
5695
  isNative: boolean;
5704
5696
  price: number | null;
5705
- } | undefined;
5697
+ };
5706
5698
  }>>;
5707
5699
  };
5708
5700
  count: {
@@ -5719,23 +5711,13 @@ declare const eden: {
5719
5711
  isNative?: boolean | undefined;
5720
5712
  test?: boolean | undefined;
5721
5713
  missingIcons?: boolean | undefined;
5714
+ missingPrice?: boolean | undefined;
5722
5715
  };
5723
5716
  fetch?: RequestInit | undefined;
5724
5717
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
5725
5718
  200: number;
5726
5719
  }>>;
5727
5720
  };
5728
- icons: {
5729
- post: (body: unknown, options: {
5730
- headers: {
5731
- authorization: string;
5732
- };
5733
- query?: Record<string, unknown> | undefined;
5734
- fetch?: RequestInit | undefined;
5735
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
5736
- 200: void;
5737
- }>>;
5738
- };
5739
5721
  webhooks: {
5740
5722
  notion: {
5741
5723
  post: (body: {
@@ -10544,6 +10526,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
10544
10526
  isNative?: boolean | undefined;
10545
10527
  test?: boolean | undefined;
10546
10528
  missingIcons?: boolean | undefined;
10529
+ missingPrice?: boolean | undefined;
10547
10530
  };
10548
10531
  headers: unknown;
10549
10532
  response: {
@@ -10581,6 +10564,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
10581
10564
  isNative?: boolean | undefined;
10582
10565
  test?: boolean | undefined;
10583
10566
  missingIcons?: boolean | undefined;
10567
+ missingPrice?: boolean | undefined;
10584
10568
  };
10585
10569
  headers: unknown;
10586
10570
  response: {
@@ -10618,7 +10602,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
10618
10602
  isPoint: boolean;
10619
10603
  isNative: boolean;
10620
10604
  price: number | null;
10621
- } | undefined;
10605
+ };
10622
10606
  };
10623
10607
  };
10624
10608
  };
@@ -10659,20 +10643,6 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
10659
10643
  };
10660
10644
  };
10661
10645
  };
10662
- } & {
10663
- icons: {
10664
- post: {
10665
- body: unknown;
10666
- params: {};
10667
- query: unknown;
10668
- headers: {
10669
- authorization: string;
10670
- };
10671
- response: {
10672
- 200: void;
10673
- };
10674
- };
10675
- };
10676
10646
  } & {
10677
10647
  webhooks: {
10678
10648
  notion: {
@@ -15881,6 +15851,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
15881
15851
  isNative?: boolean | undefined;
15882
15852
  test?: boolean | undefined;
15883
15853
  missingIcons?: boolean | undefined;
15854
+ missingPrice?: boolean | undefined;
15884
15855
  };
15885
15856
  fetch?: RequestInit | undefined;
15886
15857
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
@@ -15927,7 +15898,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
15927
15898
  isPoint: boolean;
15928
15899
  isNative: boolean;
15929
15900
  price: number | null;
15930
- } | undefined;
15901
+ };
15931
15902
  }>>;
15932
15903
  };
15933
15904
  count: {
@@ -15944,23 +15915,13 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
15944
15915
  isNative?: boolean | undefined;
15945
15916
  test?: boolean | undefined;
15946
15917
  missingIcons?: boolean | undefined;
15918
+ missingPrice?: boolean | undefined;
15947
15919
  };
15948
15920
  fetch?: RequestInit | undefined;
15949
15921
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
15950
15922
  200: number;
15951
15923
  }>>;
15952
15924
  };
15953
- icons: {
15954
- post: (body: unknown, options: {
15955
- headers: {
15956
- authorization: string;
15957
- };
15958
- query?: Record<string, unknown> | undefined;
15959
- fetch?: RequestInit | undefined;
15960
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
15961
- 200: void;
15962
- }>>;
15963
- };
15964
15925
  webhooks: {
15965
15926
  notion: {
15966
15927
  post: (body: {
@@ -19700,6 +19661,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
19700
19661
  isNative?: boolean | undefined;
19701
19662
  test?: boolean | undefined;
19702
19663
  missingIcons?: boolean | undefined;
19664
+ missingPrice?: boolean | undefined;
19703
19665
  };
19704
19666
  fetch?: RequestInit | undefined;
19705
19667
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
@@ -19746,7 +19708,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
19746
19708
  isPoint: boolean;
19747
19709
  isNative: boolean;
19748
19710
  price: number | null;
19749
- } | undefined;
19711
+ };
19750
19712
  }>>;
19751
19713
  };
19752
19714
  count: {
@@ -19763,23 +19725,13 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
19763
19725
  isNative?: boolean | undefined;
19764
19726
  test?: boolean | undefined;
19765
19727
  missingIcons?: boolean | undefined;
19728
+ missingPrice?: boolean | undefined;
19766
19729
  };
19767
19730
  fetch?: RequestInit | undefined;
19768
19731
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
19769
19732
  200: number;
19770
19733
  }>>;
19771
19734
  };
19772
- icons: {
19773
- post: (body: unknown, options: {
19774
- headers: {
19775
- authorization: string;
19776
- };
19777
- query?: Record<string, unknown> | undefined;
19778
- fetch?: RequestInit | undefined;
19779
- }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
19780
- 200: void;
19781
- }>>;
19782
- };
19783
19735
  webhooks: {
19784
19736
  notion: {
19785
19737
  post: (body: {
@@ -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
  }
@@ -2195,6 +2195,7 @@ declare const app: Elysia<"", false, {
2195
2195
  isNative?: boolean | undefined;
2196
2196
  test?: boolean | undefined;
2197
2197
  missingIcons?: boolean | undefined;
2198
+ missingPrice?: boolean | undefined;
2198
2199
  };
2199
2200
  headers: unknown;
2200
2201
  response: {
@@ -2232,6 +2233,7 @@ declare const app: Elysia<"", false, {
2232
2233
  isNative?: boolean | undefined;
2233
2234
  test?: boolean | undefined;
2234
2235
  missingIcons?: boolean | undefined;
2236
+ missingPrice?: boolean | undefined;
2235
2237
  };
2236
2238
  headers: unknown;
2237
2239
  response: {
@@ -2269,7 +2271,7 @@ declare const app: Elysia<"", false, {
2269
2271
  isPoint: boolean;
2270
2272
  isNative: boolean;
2271
2273
  price: number | null;
2272
- } | undefined;
2274
+ };
2273
2275
  };
2274
2276
  };
2275
2277
  };
@@ -2310,20 +2312,6 @@ declare const app: Elysia<"", false, {
2310
2312
  };
2311
2313
  };
2312
2314
  };
2313
- } & {
2314
- icons: {
2315
- post: {
2316
- body: unknown;
2317
- params: {};
2318
- query: unknown;
2319
- headers: {
2320
- authorization: string;
2321
- };
2322
- response: {
2323
- 200: void;
2324
- };
2325
- };
2326
- };
2327
2315
  } & {
2328
2316
  webhooks: {
2329
2317
  notion: {
@@ -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),
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,14 @@
1
+ export declare class CoingeckoRepository {
2
+ static findList(): Promise<{
3
+ id: string;
4
+ symbol: string;
5
+ name: string;
6
+ }[]>;
7
+ static findMarkets(ids?: string[]): Promise<{
8
+ id: string;
9
+ symbol: string;
10
+ name: string;
11
+ image: string;
12
+ current_price: number;
13
+ }[]>;
14
+ }
@@ -0,0 +1,9 @@
1
+ import axios from "axios";
2
+ export class CoingeckoRepository {
3
+ static async findList() {
4
+ return (await axios.get("https://api.coingecko.com/api/v3/coins/list")).data;
5
+ }
6
+ static async findMarkets(ids) {
7
+ return (await axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=250${!!ids ? `&ids=${ids.join(",")}` : ""}`)).data;
8
+ }
9
+ }
@@ -0,0 +1,16 @@
1
+ export declare class CoingeckoService {
2
+ static findList(): Promise<{
3
+ id: string;
4
+ symbol: string;
5
+ name: string;
6
+ }[]>;
7
+ static findMarkets(ids?: string[]): Promise<{
8
+ id: string;
9
+ symbol: string;
10
+ name: string;
11
+ image: string;
12
+ current_price: number;
13
+ }[]>;
14
+ static createPriceSourceForSymbolIfMissing(symbol: string, ticker: string): Promise<void>;
15
+ static fillTokensWithCoingeckoData(): Promise<void>;
16
+ }
@@ -0,0 +1,65 @@
1
+ import { CacheService } from "@/modules/v4/cache";
2
+ import { PriceService } from "@/modules/v4/price/price.service";
3
+ import { log } from "@/utils/logger";
4
+ import { NETWORK_LABELS } from "@sdk";
5
+ import { TTLPresets } from "../cache/cache.model";
6
+ import { TokenService } from "../token/token.service";
7
+ import { CoingeckoRepository } from "./coingecko.repository";
8
+ export class CoingeckoService {
9
+ static findList() {
10
+ return CacheService.wrap(TTLPresets.HOUR_1, CoingeckoRepository.findList);
11
+ }
12
+ static findMarkets(ids) {
13
+ return CacheService.wrap(TTLPresets.MIN_5, CoingeckoRepository.findMarkets, ids);
14
+ }
15
+ static async createPriceSourceForSymbolIfMissing(symbol, ticker) {
16
+ try {
17
+ await PriceService.getPriceSourceBySymbol(symbol);
18
+ }
19
+ catch {
20
+ await PriceService.createPriceSource({
21
+ symbol: symbol,
22
+ method: "COINGECKO",
23
+ args: {
24
+ ticker,
25
+ },
26
+ });
27
+ log.info(`added coingecko price source for token ${symbol}: ${ticker}`);
28
+ }
29
+ }
30
+ static async fillTokensWithCoingeckoData() {
31
+ // 1 - Get coingecko token list
32
+ const coingeckoData = CoingeckoService.findList();
33
+ // 2 - Find tokens with missing prices and try to add the sources
34
+ const missingPriceTokens = await TokenService.findMany({ missingPrice: true });
35
+ for (const token of missingPriceTokens) {
36
+ const coingeckoToken = (await coingeckoData).find(t => t.symbol === token.symbol && t.name === token.name);
37
+ if (!!coingeckoToken) {
38
+ await CoingeckoService.createPriceSourceForSymbolIfMissing(token.symbol, coingeckoToken.id);
39
+ }
40
+ }
41
+ // 3 - Find tokens with missing logos and gather their ids
42
+ const missingIconsTokens = await TokenService.findMany({ missingIcons: true });
43
+ const coingeckoIds = [];
44
+ for (const token of missingIconsTokens) {
45
+ const coingeckoToken = (await coingeckoData).find(t => t.symbol === token.symbol && t.name === token.name);
46
+ if (!!coingeckoToken) {
47
+ coingeckoIds.push(coingeckoToken.id);
48
+ }
49
+ }
50
+ // Avoid rate limits, wait 2 min
51
+ await new Promise(resolve => setTimeout(resolve, 120_000));
52
+ // 4 - Get images and fill them
53
+ const coingeckoMarkets = await CoingeckoService.findMarkets(coingeckoIds);
54
+ for (const market of coingeckoMarkets) {
55
+ for (const token of missingIconsTokens) {
56
+ if (token.symbol === market.symbol && token.name === market.name) {
57
+ await TokenService.update(token.id, {
58
+ icon: market.image,
59
+ });
60
+ log.info(`updated token ${token.symbol} on ${NETWORK_LABELS[token.chainId]} with icon ${market.image}`);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
@@ -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 {
@@ -2065,6 +2065,7 @@ export declare const v4: Elysia<"/v4", false, {
2065
2065
  isNative?: boolean | undefined;
2066
2066
  test?: boolean | undefined;
2067
2067
  missingIcons?: boolean | undefined;
2068
+ missingPrice?: boolean | undefined;
2068
2069
  };
2069
2070
  headers: unknown;
2070
2071
  response: {
@@ -2102,6 +2103,7 @@ export declare const v4: Elysia<"/v4", false, {
2102
2103
  isNative?: boolean | undefined;
2103
2104
  test?: boolean | undefined;
2104
2105
  missingIcons?: boolean | undefined;
2106
+ missingPrice?: boolean | undefined;
2105
2107
  };
2106
2108
  headers: unknown;
2107
2109
  response: {
@@ -2139,7 +2141,7 @@ export declare const v4: Elysia<"/v4", false, {
2139
2141
  isPoint: boolean;
2140
2142
  isNative: boolean;
2141
2143
  price: number | null;
2142
- } | undefined;
2144
+ };
2143
2145
  };
2144
2146
  };
2145
2147
  };
@@ -2180,20 +2182,6 @@ export declare const v4: Elysia<"/v4", false, {
2180
2182
  };
2181
2183
  };
2182
2184
  };
2183
- } & {
2184
- icons: {
2185
- post: {
2186
- body: unknown;
2187
- params: {};
2188
- query: unknown;
2189
- headers: {
2190
- authorization: string;
2191
- };
2192
- response: {
2193
- 200: void;
2194
- };
2195
- };
2196
- };
2197
2185
  } & {
2198
2186
  webhooks: {
2199
2187
  notion: {
@@ -156,6 +156,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
156
156
  isNative?: boolean | undefined;
157
157
  test?: boolean | undefined;
158
158
  missingIcons?: boolean | undefined;
159
+ missingPrice?: boolean | undefined;
159
160
  };
160
161
  headers: unknown;
161
162
  response: {
@@ -193,6 +194,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
193
194
  isNative?: boolean | undefined;
194
195
  test?: boolean | undefined;
195
196
  missingIcons?: boolean | undefined;
197
+ missingPrice?: boolean | undefined;
196
198
  };
197
199
  headers: unknown;
198
200
  response: {
@@ -230,7 +232,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
230
232
  isPoint: boolean;
231
233
  isNative: boolean;
232
234
  price: number | null;
233
- } | undefined;
235
+ };
234
236
  };
235
237
  };
236
238
  };
@@ -271,20 +273,6 @@ export declare const TokenController: Elysia<"/tokens", false, {
271
273
  };
272
274
  };
273
275
  };
274
- } & {
275
- icons: {
276
- post: {
277
- body: unknown;
278
- params: {};
279
- query: unknown;
280
- headers: {
281
- authorization: string;
282
- };
283
- response: {
284
- 200: void;
285
- };
286
- };
287
- };
288
276
  } & {
289
277
  webhooks: {
290
278
  notion: {
@@ -92,11 +92,6 @@ export const TokenController = new Elysia({ prefix: "/tokens", detail: { tags: [
92
92
  headers: AuthorizationHeadersDto,
93
93
  beforeHandle: BackOfficeGuard,
94
94
  detail: { hide: true },
95
- })
96
- .post("/icons", async () => await TokenService.tryToFillWithCoingeckoIcons(), {
97
- headers: AuthorizationHeadersDto,
98
- beforeHandle: BackOfficeGuard,
99
- detail: { hide: true },
100
95
  })
101
96
  .group("/webhooks", app => {
102
97
  return app.post("/notion", async ({ body }) => TokenService.notionWebhook(body), {
@@ -42,6 +42,7 @@ export declare const GetTokenQueryDto: import("@sinclair/typebox").TObject<{
42
42
  verified: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
43
43
  test: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
44
44
  missingIcons: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
45
+ missingPrice: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
45
46
  }>;
46
47
  export declare const TokenDto: import("@sinclair/typebox").TObject<{
47
48
  address: import("@sinclair/typebox").TString;