@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.
- package/dist/src/eden/index.d.ts +15 -63
- package/dist/src/engine/dynamicData/implementations/Erc20.js +5 -2
- package/dist/src/engine/dynamicData/implementations/EventBased.js +9 -9
- package/dist/src/index.d.ts +3 -15
- package/dist/src/modules/v4/campaign/campaign.service.js +5 -1
- package/dist/src/modules/v4/coingecko/coingecko.model.d.ts +0 -0
- package/dist/src/modules/v4/coingecko/coingecko.model.js +1 -0
- package/dist/src/modules/v4/coingecko/coingecko.repository.d.ts +14 -0
- package/dist/src/modules/v4/coingecko/coingecko.repository.js +9 -0
- package/dist/src/modules/v4/coingecko/coingecko.service.d.ts +16 -0
- package/dist/src/modules/v4/coingecko/coingecko.service.js +65 -0
- package/dist/src/modules/v4/programPayload/programPayload.service.d.ts +2 -1
- package/dist/src/modules/v4/programPayload/programPayload.service.js +21 -7
- package/dist/src/modules/v4/router.d.ts +3 -15
- package/dist/src/modules/v4/token/token.controller.d.ts +3 -15
- package/dist/src/modules/v4/token/token.controller.js +0 -5
- package/dist/src/modules/v4/token/token.model.d.ts +1 -0
- package/dist/src/modules/v4/token/token.model.js +1 -0
- package/dist/src/modules/v4/token/token.repository.js +1 -0
- package/dist/src/modules/v4/token/token.service.d.ts +1 -2
- package/dist/src/modules/v4/token/token.service.js +21 -37
- package/dist/src/scripts/fill-coingecko-data.d.ts +1 -0
- package/dist/src/scripts/fill-coingecko-data.js +3 -0
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/src/eden/index.d.ts
CHANGED
@@ -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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
-
}
|
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
|
-
|
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
|
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
|
40
|
+
let distributedRewards = 0;
|
41
41
|
if (!fileName || !bucketName) {
|
42
|
-
return {
|
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
|
-
|
48
|
+
distributedRewards += BN2Number(value.allTimeValue, 18);
|
49
49
|
}
|
50
|
-
|
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 {
|
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 {
|
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 (
|
83
|
+
if (distributedRewards > (amount * (moment().unix() - startTimestamp)) / (endTimestamp - startTimestamp)) {
|
84
84
|
distributionMeanAPR =
|
85
85
|
(fixRewardRate * amount * (moment().unix() - startTimestamp)) / (endTimestamp - startTimestamp) / tvl;
|
86
86
|
}
|
package/dist/src/index.d.ts
CHANGED
@@ -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
|
-
}
|
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:
|
288
|
+
params: updatedParams,
|
285
289
|
chain: ComputeChain,
|
286
290
|
endTimestamp: Number(c.endTimestamp),
|
287
291
|
startTimestamp: Number(c.startTimestamp),
|
File without changes
|
@@ -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,
|
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,
|
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
|
-
|
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
|
-
}
|
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
|
-
}
|
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;
|