@merkl/api 0.19.32 → 0.19.34

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.
@@ -36,7 +36,8 @@ export class CampaignService {
36
36
  try {
37
37
  await OpportunityService.createFromCampaign(campaign);
38
38
  }
39
- catch {
39
+ catch (e) {
40
+ console.error(e);
40
41
  throw new CannotParseOpportunity(campaign.campaignId, campaign.chainId, campaign.type);
41
42
  }
42
43
  return await CampaignRepository.upsert({ id, ...campaign });
@@ -46,17 +47,22 @@ export class CampaignService {
46
47
  * @dev deprecated should be replaced with a manual override
47
48
  */
48
49
  static async updateMetaData(campaign) {
49
- const id = CampaignService.hashId({
50
+ const campaignUnique = {
50
51
  distributionChain: campaign.distributionChain,
51
52
  campaignId: campaign.campaignId,
52
- });
53
- const existingCampaign = await CampaignService.findUniqueOrThrow(campaign);
53
+ };
54
+ const id = CampaignService.hashId(campaignUnique);
55
+ let existingCampaign = await CampaignService.findUnique(campaign);
56
+ if (!existingCampaign) {
57
+ await CampaignService.fill([campaignUnique]);
58
+ existingCampaign = await CampaignService.findUniqueOrThrow(campaign);
59
+ }
54
60
  const params = existingCampaign.params;
55
61
  if ("url" in params) {
56
62
  params.url = campaign.url;
57
63
  }
58
64
  const updatedParams = JSON.stringify(params);
59
- return await CampaignRepository.updateMetaData(id, updatedParams);
65
+ return await CampaignRepository.updateParams(id, updatedParams);
60
66
  }
61
67
  /**
62
68
  * @dev back-office function for manual overrides
@@ -181,8 +187,8 @@ export class CampaignService {
181
187
  }
182
188
  static async findCampaignsToProcess(distributionChainId) {
183
189
  return (await CampaignRepository.findCampaignsToProcess(distributionChainId))
184
- .filter(campaign => campaign.endTimestamp > (campaign?.CampaignStatus?.[0]?.computedUntil ?? 0n))
185
- ?.sort((a, b) => Number((a.CampaignStatus?.[0]?.processingStarted ?? 0n) - (b.CampaignStatus?.[0]?.processingStarted ?? 0n)));
190
+ .filter(campaign => campaign.endTimestamp > (campaign?.CampaignStatus?.[0]?.computedUntil ?? 0n)) // Filter out campaigns that have already been processed
191
+ ?.sort((a, b) => Number((a.CampaignStatus?.[0]?.processingStarted ?? 0n) - (b.CampaignStatus?.[0]?.processingStarted ?? 0n))); // Sort by processingStarted, that is to say campaigns that were processed the most long ago
186
192
  }
187
193
  static async findNextCampaignToProcess(chainId) {
188
194
  const campaigns = await CampaignService.findCampaignsToProcess(chainId);
@@ -202,31 +208,37 @@ export class CampaignService {
202
208
  }
203
209
  throw new Error(`No campaign to process found on ${NETWORK_LABELS[chainId]}`);
204
210
  }
205
- static async fill(campaigns) {
211
+ static async findEngineCampaigns(campaigns) {
206
212
  const campaignsFromEngine = await CampaignRepository.getFromEngineDbWithId(campaigns);
207
213
  for (const campaign of campaigns) {
208
214
  if (campaignsFromEngine.findIndex(c => c.campaignId === campaign.campaignId) === -1) {
209
215
  log.warn(`Campaign ${campaign.campaignId} on ${NETWORK_LABELS[campaign.distributionChain]} not found in engine db`);
210
216
  }
211
217
  }
218
+ return campaignsFromEngine.map(campaign => {
219
+ return {
220
+ amount: campaign.amount,
221
+ chainId: campaign.chainId,
222
+ computeChainId: campaign.computeChainId,
223
+ creator: campaign.creator,
224
+ endTimestamp: campaign.endTimestamp.toString(),
225
+ campaignId: campaign.campaignId,
226
+ opportunityIdentifier: campaign.mainParameter,
227
+ params: JSON.stringify(campaign.campaignParameters),
228
+ rewardTokenAddress: campaign.rewardToken,
229
+ startTimestamp: campaign.startTimestamp.toString(),
230
+ type: campaign.campaignType,
231
+ subType: campaign.campaignSubType,
232
+ };
233
+ });
234
+ }
235
+ static async fill(campaigns) {
236
+ const campaignsFromEngine = await CampaignService.findEngineCampaigns(campaigns);
212
237
  let success = 0;
213
238
  let fail = 0;
214
239
  for (const engineCampaign of campaignsFromEngine) {
215
240
  try {
216
- await CampaignService.create({
217
- amount: engineCampaign.amount,
218
- chainId: engineCampaign.chainId,
219
- computeChainId: engineCampaign.computeChainId,
220
- creator: engineCampaign.creator,
221
- endTimestamp: engineCampaign.endTimestamp.toString(),
222
- campaignId: engineCampaign.campaignId,
223
- opportunityIdentifier: engineCampaign.mainParameter,
224
- params: JSON.stringify(engineCampaign.campaignParameters),
225
- rewardTokenAddress: engineCampaign.rewardToken,
226
- startTimestamp: engineCampaign.startTimestamp.toString(),
227
- type: engineCampaign.campaignType,
228
- subType: engineCampaign.campaignSubType,
229
- });
241
+ await CampaignService.create(engineCampaign);
230
242
  success++;
231
243
  }
232
244
  catch {
@@ -209,7 +209,9 @@ export class OpportunityRepository {
209
209
  Chain: { connect: { id: newOpp.chainId } },
210
210
  MainProtocol: !!newOpp.mainProtocol
211
211
  ? { connect: { id: newOpp.mainProtocol } }
212
- : { disconnect: { id: toDisconnect.mainProtocol } },
212
+ : !!previousOpportunity?.mainProtocolId
213
+ ? { disconnect: { id: previousOpportunity?.mainProtocolId } }
214
+ : undefined,
213
215
  Protocols: {
214
216
  connect: (newOpp.protocols ?? []).map((protocol) => {
215
217
  return { id: protocol };
@@ -26,34 +26,12 @@ export declare abstract class OpportunityService {
26
26
  apr: number;
27
27
  dailyRewards: number;
28
28
  } | null>;
29
- static fakeCreateFromCampaign(campaign: Omit<CreateCampaignModel, "id">): Promise<{
30
- id: string;
31
- chainId: number;
32
- type: string;
33
- identifier: string;
34
- name: string;
35
- status: "PAST" | "LIVE" | "SOON";
36
- action: OpportunityAction;
37
- tokens: ({
38
- symbol: string;
39
- id: string;
40
- name: string | null;
41
- icon: string;
42
- address: string;
43
- chainId: number;
44
- decimals: number;
45
- verified: boolean;
46
- isTest: boolean;
47
- isPoint: boolean;
48
- isNative: boolean;
49
- } & {
50
- price?: number | null | undefined;
51
- })[][];
52
- mainProtocol: "splice" | "morpho" | "euler" | "ambient" | "uniswap" | "arthswap" | "base-swap" | "camelot" | "crust" | "fenix" | "horiza" | "izumi" | "kim" | "pancake-swap" | "quick-swap" | "ramses" | "retro" | "stryke" | "sushiswap" | "swapr" | "thruster" | "voltage" | "zero" | "koi" | "supswap" | "zk-swap" | "thirdtrade" | "swap-x" | "velodrome" | "aerodrome" | "balancer" | "curve" | "cross_curve" | "curveNPool" | "aura" | "akron" | "beefy" | "dragonswap" | "poolside" | "syncswap" | "neptune" | "zkSwapThreePool" | "rfx" | "ra" | "maverick" | "trader-joe" | "hanji" | "radiant" | "aave" | "fraxlend" | "ironclad" | "gearbox" | "compound" | "sturdy" | "frax" | "ionic" | "moonwell" | "fluid" | "silo" | "dolomite" | "badger" | "ajna" | "layerbank" | "ion" | "venus" | "woofi" | "reactor_fusion" | "eigenlayer" | "vest" | "zerolend" | "hyperdrive" | "gamma" | "oku" | "hourglass" | "veda" | "kyo" | "sonex" | "lendle" | "tako-tako" | "equalizer" | "spectra" | "beraborrow" | "superlend" | "avalon" | "angles" | "enzyme" | "toros" | "vicuna" | "bunni" | "beratrax" | "concrete" | "cian" | "pendle" | "yei" | "filament" | "gammaswap" | "maha" | "tempest" | "uranium" | "holdstation" | "katana" | "satlayer" | undefined;
53
- depositUrl: any;
54
- tags: string[];
55
- }>;
56
- static createFromCampaign(campaign: Omit<CreateCampaignModel, "id">, upsert?: boolean): Promise<{
29
+ /**
30
+ * @param upsert whether to update the opportunity if it already exists in database
31
+ * @param dryRun whether to skip the opportunity table interaction and just return the computed opportunity
32
+ * @returns the opportunity entity computed
33
+ */
34
+ static createFromCampaign(campaign: Omit<CreateCampaignModel, "id">, upsert?: boolean, dryRun?: boolean): Promise<{
57
35
  id: string;
58
36
  chainId: number;
59
37
  type: string;
@@ -36,50 +36,12 @@ export class OpportunityService {
36
36
  const distributionChainId = campaign.chainId;
37
37
  return await metadataBuilderFactory(campaignType).build(computeChainId, campaignParams, campaign.subType, campaign.rewardTokenAddress, distributionChainId, campaign.campaignId, campaign.creator);
38
38
  }
39
- static async fakeCreateFromCampaign(campaign) {
40
- const campaignType = CampaignService.getTypeFromV3(campaign.type);
41
- const metadata = await OpportunityService.#getMetadata(campaign);
42
- const tags = [...((await UserService.findUnique(campaign.creator))?.tags ?? []), ...(campaign?.tags ?? [])];
43
- const opportunityId = OpportunityService.hashId({
44
- chainId: campaign.computeChainId,
45
- identifier: campaign.opportunityIdentifier,
46
- type: campaignType,
47
- });
48
- const protocol = (await ProtocolService.findMany({ id: metadata.mainProtocol }))?.[0];
49
- const tokens = await Promise.all(metadata.tokens.map(async (t) => {
50
- return await TokenService.findMany(t);
51
- }));
52
- const params = JSON.parse(campaign.params);
53
- const now = moment().unix();
54
- const opportunity = {
55
- id: opportunityId,
56
- chainId: campaign.computeChainId,
57
- type: campaignType,
58
- identifier: campaign.opportunityIdentifier, // mainParameter
59
- name: metadata.name,
60
- status: now >= +campaign.startTimestamp && now < +campaign.endTimestamp
61
- ? Status.LIVE
62
- : now > +campaign.endTimestamp
63
- ? Status.PAST
64
- : Status.SOON,
65
- action: metadata.action,
66
- tokens,
67
- mainProtocol: metadata.mainProtocol,
68
- // If creator has specified a deposit URL, use it
69
- // Else if we have the specific logic to handle the deposit URL, use it
70
- // Else if the protocol has a deposit URL, use it
71
- depositUrl: !!params.url
72
- ? params.url
73
- : !!metadata.depositUrl
74
- ? metadata.depositUrl
75
- : !!metadata.mainProtocol && !!protocol.url
76
- ? protocol.url
77
- : undefined,
78
- tags,
79
- };
80
- return opportunity;
81
- }
82
- static async createFromCampaign(campaign, upsert = false) {
39
+ /**
40
+ * @param upsert whether to update the opportunity if it already exists in database
41
+ * @param dryRun whether to skip the opportunity table interaction and just return the computed opportunity
42
+ * @returns the opportunity entity computed
43
+ */
44
+ static async createFromCampaign(campaign, upsert = false, dryRun = false) {
83
45
  const campaignType = CampaignService.getTypeFromV3(campaign.type);
84
46
  const metadata = await OpportunityService.#getMetadata(campaign);
85
47
  const tags = [...((await UserService.findUnique(campaign.creator))?.tags ?? []), ...(campaign?.tags ?? [])];
@@ -118,7 +80,8 @@ export class OpportunityService {
118
80
  : undefined,
119
81
  tags,
120
82
  };
121
- await OpportunityRepository.create(opportunity, upsert);
83
+ if (!dryRun)
84
+ await OpportunityRepository.create(opportunity, upsert);
122
85
  return opportunity;
123
86
  }
124
87
  static async updateStatus(opportunityId, status) {
@@ -5,27 +5,6 @@ import { ProtocolRepository } from "./protocol.repository";
5
5
  // ─── Protocols Services ──────────────────────────────────────────────────────
6
6
  export class ProtocolService {
7
7
  static async findMany(query) {
8
- if (process.env.CLEAN_PROTOCOLS === "true") {
9
- await ProtocolRepository.changeId("uniswap-v3", "uniswap");
10
- await ProtocolRepository.changeId("balancerpool", "balancer");
11
- await ProtocolRepository.changeId("syncswap-v3", "syncswap");
12
- await ProtocolRepository.changeId("akronv2", "akron");
13
- await ProtocolRepository.changeId("aerodromev2", "aerodrome");
14
- await ProtocolRepository.changeId("velodromev2", "velodrome");
15
- await ProtocolRepository.changeId("fenixv2", "fenix");
16
- await ProtocolRepository.changeId("supswap-v3", "supswap");
17
- await ProtocolRepository.changeId("stryke-sushi", "stryke");
18
- await ProtocolRepository.changeId("stryke-pcs", "stryke");
19
- await ProtocolRepository.changeId("quickswap-algebra-v1_2", "quickswap");
20
- await ProtocolRepository.changeId("quickswap-uni", "quickswap");
21
- await ProtocolRepository.changeId("quickswap-algebra", "quickswap");
22
- await ProtocolRepository.changeId("quickswap-2", "quickswap");
23
- await ProtocolRepository.changeId("pancake-swap-v3", "pancakeswap");
24
- await ProtocolRepository.changeId("baseswapv2", "base-swap");
25
- await ProtocolRepository.changeId("baseswap", "base-swap");
26
- await ProtocolRepository.changeId("sushi-swap-v3", "sushi-swap");
27
- await ProtocolRepository.changeId("sushiswap", "sushi-swap");
28
- }
29
8
  const protocols = await ProtocolRepository.findMany(query);
30
9
  const enrichedProtocols = protocols.map(({ MainOpportunities, ...protocol }) => ({
31
10
  ...protocol,
@@ -1144,13 +1144,16 @@ export declare const v4: Elysia<"/v4", false, {
1144
1144
  } & {
1145
1145
  "dry-run": {
1146
1146
  ":campaignId": {
1147
- "meta-data": {
1147
+ metadata: {
1148
1148
  get: {
1149
1149
  body: unknown;
1150
1150
  params: {
1151
1151
  campaignId: string;
1152
1152
  };
1153
- query: unknown;
1153
+ query: {
1154
+ campaignId: string;
1155
+ distributionChain: number;
1156
+ };
1154
1157
  headers: {
1155
1158
  authorization: string;
1156
1159
  };
@@ -1177,7 +1180,7 @@ export declare const v4: Elysia<"/v4", false, {
1177
1180
  isNative: boolean;
1178
1181
  } & {
1179
1182
  price?: number | null | undefined;
1180
- })[][];
1183
+ })[];
1181
1184
  mainProtocol: "splice" | "morpho" | "euler" | "ambient" | "uniswap" | "arthswap" | "base-swap" | "camelot" | "crust" | "fenix" | "horiza" | "izumi" | "kim" | "pancake-swap" | "quick-swap" | "ramses" | "retro" | "stryke" | "sushiswap" | "swapr" | "thruster" | "voltage" | "zero" | "koi" | "supswap" | "zk-swap" | "thirdtrade" | "swap-x" | "velodrome" | "aerodrome" | "balancer" | "curve" | "cross_curve" | "curveNPool" | "aura" | "akron" | "beefy" | "dragonswap" | "poolside" | "syncswap" | "neptune" | "zkSwapThreePool" | "rfx" | "ra" | "maverick" | "trader-joe" | "hanji" | "radiant" | "aave" | "fraxlend" | "ironclad" | "gearbox" | "compound" | "sturdy" | "frax" | "ionic" | "moonwell" | "fluid" | "silo" | "dolomite" | "badger" | "ajna" | "layerbank" | "ion" | "venus" | "woofi" | "reactor_fusion" | "eigenlayer" | "vest" | "zerolend" | "hyperdrive" | "gamma" | "oku" | "hourglass" | "veda" | "kyo" | "sonex" | "lendle" | "tako-tako" | "equalizer" | "spectra" | "beraborrow" | "superlend" | "avalon" | "angles" | "enzyme" | "toros" | "vicuna" | "bunni" | "beratrax" | "concrete" | "cian" | "pendle" | "yei" | "filament" | "gammaswap" | "maha" | "tempest" | "uranium" | "holdstation" | "katana" | "satlayer" | undefined;
1182
1185
  depositUrl: any;
1183
1186
  tags: string[];