@merkl/api 0.10.252 → 0.10.253

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 (23) hide show
  1. package/dist/src/eden/index.d.ts +123 -9
  2. package/dist/src/index.d.ts +47 -3
  3. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/RfxProcessor.js +1 -0
  4. package/dist/src/modules/v4/campaign/campaign.controller.d.ts +1 -1
  5. package/dist/src/modules/v4/campaign/campaign.model.d.ts +18 -1
  6. package/dist/src/modules/v4/campaign/campaign.model.js +1 -1
  7. package/dist/src/modules/v4/campaign/campaign.repository.d.ts +2 -3
  8. package/dist/src/modules/v4/campaign/campaign.repository.js +9 -70
  9. package/dist/src/modules/v4/campaign/campaign.service.js +6 -23
  10. package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +46 -2
  11. package/dist/src/modules/v4/opportunity/opportunity.controller.js +23 -0
  12. package/dist/src/modules/v4/opportunity/opportunity.model.d.ts +4 -2
  13. package/dist/src/modules/v4/opportunity/opportunity.model.js +4 -2
  14. package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +148 -1
  15. package/dist/src/modules/v4/opportunity/opportunity.repository.js +45 -25
  16. package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +54 -1
  17. package/dist/src/modules/v4/opportunity/opportunity.service.js +55 -4
  18. package/dist/src/modules/v4/opportunity/subservices/getClammMetadata.service.js +1 -1
  19. package/dist/src/modules/v4/opportunity/subservices/getErc20Metadata.service.js +31 -50
  20. package/dist/src/modules/v4/router.d.ts +47 -3
  21. package/dist/src/utils/generateCardName.js +1 -1
  22. package/dist/tsconfig.package.tsbuildinfo +1 -1
  23. package/package.json +1 -1
@@ -19,6 +19,29 @@ export const OpportunityController = new Elysia({
19
19
  headers: AuthorizationHeadersDto,
20
20
  body: CreateOpportunityDto,
21
21
  detail: { hide: true },
22
+ })
23
+ // ─── Tries to reparse An Opportunity ─────────────────────────────────
24
+ .post("/:id", async ({ params }) => {
25
+ try {
26
+ if (!params.id.includes("-"))
27
+ return await OpportunityService.recreate(params.id);
28
+ const [chainId, type, identifier] = params.id.split("-");
29
+ return await OpportunityService.recreate({
30
+ chainId: +chainId,
31
+ type: type,
32
+ identifier,
33
+ });
34
+ }
35
+ catch (err) {
36
+ if (err.code && err.code === "P2025")
37
+ throw new NotFoundError();
38
+ throw err;
39
+ }
40
+ }, {
41
+ beforeHandle: BackOfficeGuard,
42
+ headers: AuthorizationHeadersDto,
43
+ params: OpportunityUniqueDto,
44
+ detail: { hide: true },
22
45
  })
23
46
  // ─── Get All Opportunities ───────────────────────────────────────────
24
47
  .get("/", async ({ query }) => await OpportunityService.getMany(query), {
@@ -331,8 +331,10 @@ export declare const CreateOpportunityDto: import("@sinclair/typebox").TObject<{
331
331
  address: import("@sinclair/typebox").TString;
332
332
  chainId: import("@sinclair/typebox").TNumber;
333
333
  }>>;
334
- protocols: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
335
- mainProtocol: import("@sinclair/typebox").TString;
334
+ protocols: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
335
+ mainProtocol: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
336
+ depositUrl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
337
+ tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
336
338
  }>;
337
339
  export declare const OpportunityAggregateFieldDto: import("@sinclair/typebox").TObject<{
338
340
  field: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"dailyRewards">[]>;
@@ -78,8 +78,10 @@ export const CreateOpportunityDto = t.Object({
78
78
  status: t.Enum(Status),
79
79
  action: t.Enum(OpportunityAction),
80
80
  tokens: t.Array(TokenDto),
81
- protocols: t.Array(t.String()),
82
- mainProtocol: t.String(),
81
+ protocols: t.Optional(t.Array(t.String())),
82
+ mainProtocol: t.Optional(t.String()),
83
+ depositUrl: t.Optional(t.String()),
84
+ tags: t.Optional(t.Array(t.String())),
83
85
  });
84
86
  export const OpportunityAggregateFieldDto = t.Object({
85
87
  field: t.Union(["dailyRewards"].map(v => t.Literal(v))),
@@ -6,7 +6,154 @@ import { type TvlRecord } from "../tvl";
6
6
  import type { CreateOpportunityModel, GetOpportunitiesQueryModel, UpdateOpportunityModel } from "./opportunity.model";
7
7
  export declare abstract class OpportunityRepository {
8
8
  #private;
9
- static create(newOpp: CreateOpportunityModel): Promise<void>;
9
+ static upsert(newOpp: CreateOpportunityModel): Promise<void>;
10
+ static findUnique(id: string): Promise<({
11
+ Chain: {
12
+ name: string;
13
+ id: number;
14
+ icon: string;
15
+ };
16
+ Campaigns: ({
17
+ RewardToken: {
18
+ symbol: string;
19
+ name: string | null;
20
+ id: string;
21
+ icon: string;
22
+ chainId: number;
23
+ address: string;
24
+ decimals: number;
25
+ displaySymbol: string;
26
+ verified: boolean;
27
+ isTest: boolean;
28
+ price: number | null;
29
+ };
30
+ } & {
31
+ type: import("../../../../database/api/.generated").$Enums.CampaignType;
32
+ id: string;
33
+ params: Prisma.JsonValue;
34
+ subType: number | null;
35
+ startTimestamp: bigint;
36
+ endTimestamp: bigint;
37
+ computeChainId: number;
38
+ distributionChainId: number;
39
+ campaignId: string;
40
+ rewardTokenId: string;
41
+ amount: string;
42
+ opportunityId: string;
43
+ creatorAddress: string;
44
+ })[];
45
+ Tokens: {
46
+ symbol: string;
47
+ name: string | null;
48
+ id: string;
49
+ icon: string;
50
+ chainId: number;
51
+ address: string;
52
+ decimals: number;
53
+ displaySymbol: string;
54
+ verified: boolean;
55
+ isTest: boolean;
56
+ price: number | null;
57
+ }[];
58
+ Protocols: {
59
+ name: string;
60
+ url: string;
61
+ description: string;
62
+ id: string;
63
+ tags: string[];
64
+ icon: string;
65
+ }[];
66
+ MainProtocol: {
67
+ name: string;
68
+ url: string;
69
+ description: string;
70
+ id: string;
71
+ tags: string[];
72
+ icon: string;
73
+ } | null;
74
+ TvlRecords: ({
75
+ TvlBreakdown: {
76
+ type: import("../../../../database/api/.generated").$Enums.TvlType;
77
+ id: number;
78
+ identifier: string;
79
+ value: number;
80
+ tvlRecordId: string;
81
+ }[];
82
+ } & {
83
+ total: number;
84
+ id: string;
85
+ timestamp: bigint;
86
+ opportunityId: string;
87
+ })[];
88
+ AprRecords: ({
89
+ AprBreakdown: {
90
+ type: import("../../../../database/api/.generated").$Enums.AprType;
91
+ id: number;
92
+ identifier: string;
93
+ value: number;
94
+ aprRecordId: string;
95
+ }[];
96
+ } & {
97
+ id: string;
98
+ timestamp: bigint;
99
+ opportunityId: string;
100
+ cumulated: number;
101
+ })[];
102
+ DailyRewardsRecords: ({
103
+ DailyRewardsBreakdown: ({
104
+ Campaign: {
105
+ startTimestamp: bigint;
106
+ endTimestamp: bigint;
107
+ amount: string;
108
+ RewardToken: {
109
+ symbol: string;
110
+ name: string | null;
111
+ id: string;
112
+ icon: string;
113
+ chainId: number;
114
+ address: string;
115
+ decimals: number;
116
+ displaySymbol: string;
117
+ verified: boolean;
118
+ isTest: boolean;
119
+ price: number | null;
120
+ };
121
+ CampaignStatus: {
122
+ error: string;
123
+ details: Prisma.JsonValue;
124
+ status: import("../../../../database/api/.generated").$Enums.RunStatus;
125
+ campaignId: string;
126
+ computedUntil: bigint;
127
+ processingStarted: bigint;
128
+ }[];
129
+ };
130
+ } & {
131
+ id: number;
132
+ value: number;
133
+ campaignId: string;
134
+ dailyRewardsRecordId: string;
135
+ })[];
136
+ } & {
137
+ total: number;
138
+ id: string;
139
+ timestamp: bigint;
140
+ opportunityId: string;
141
+ })[];
142
+ } & {
143
+ name: string;
144
+ type: import("../../../../database/api/.generated").$Enums.CampaignType;
145
+ id: string;
146
+ status: import("../../../../database/api/.generated").$Enums.Status;
147
+ tags: string[];
148
+ identifier: string;
149
+ chainId: number;
150
+ action: import("../../../../database/api/.generated").$Enums.OpportunityAction;
151
+ depositUrl: string | null;
152
+ mainProtocolId: string | null;
153
+ tvl: number;
154
+ apr: number;
155
+ dailyRewards: number;
156
+ }) | null>;
10
157
  static findUniqueOrThrow(id: string): Promise<{
11
158
  Chain: {
12
159
  name: string;
@@ -38,33 +38,53 @@ export class OpportunityRepository {
38
38
  },
39
39
  };
40
40
  }
41
- static async create(newOpp) {
42
- await apiDbClient.opportunity.create({
43
- data: {
44
- id: newOpp.id,
45
- action: newOpp.action,
46
- identifier: newOpp.identifier,
47
- name: newOpp.name,
48
- status: newOpp.status,
49
- type: newOpp.type,
50
- Chain: { connect: { id: newOpp.chainId } },
51
- MainProtocol: { connect: { id: newOpp.mainProtocol } },
52
- Protocols: {
53
- connect: newOpp.protocols.map((protocol) => {
54
- return { id: protocol };
55
- }),
56
- },
57
- Tokens: {
58
- connect: newOpp.tokens.map((token) => {
59
- return {
60
- chainId_address: {
61
- chainId: token.chainId,
62
- address: token.address,
63
- },
64
- };
65
- }),
41
+ static async upsert(newOpp) {
42
+ const data = {
43
+ id: newOpp.id,
44
+ action: newOpp.action,
45
+ identifier: newOpp.identifier,
46
+ name: newOpp.name,
47
+ status: newOpp.status,
48
+ type: newOpp.type,
49
+ Chain: { connect: { id: newOpp.chainId } },
50
+ MainProtocol: { connect: { id: newOpp.mainProtocol } },
51
+ Protocols: {
52
+ connect: (newOpp.protocols ?? []).map((protocol) => {
53
+ return { id: protocol };
54
+ }),
55
+ },
56
+ Tokens: {
57
+ connect: newOpp.tokens.map((token) => {
58
+ return {
59
+ chainId_address: {
60
+ chainId: token.chainId,
61
+ address: token.address,
62
+ },
63
+ };
64
+ }),
65
+ },
66
+ };
67
+ await apiDbClient.opportunity.upsert({
68
+ where: { id: newOpp.id },
69
+ create: data,
70
+ update: data,
71
+ });
72
+ }
73
+ static async findUnique(id) {
74
+ return await apiDbClient.opportunity.findUnique({
75
+ include: {
76
+ ...OpportunityRepository.#getRecordInclusion(),
77
+ Chain: true,
78
+ Campaigns: {
79
+ include: {
80
+ RewardToken: true,
81
+ },
66
82
  },
83
+ MainProtocol: true,
84
+ Protocols: true,
85
+ Tokens: true,
67
86
  },
87
+ where: { id },
68
88
  });
69
89
  }
70
90
  static async findUniqueOrThrow(id) {
@@ -11,12 +11,65 @@ export declare abstract class OpportunityService {
11
11
  * @returns {Promise<Opportunity|undefined>}
12
12
  */
13
13
  static create(newOpp: Omit<CreateOpportunityModel, "id">): Promise<void>;
14
+ static createFromCampaign(campaign: Omit<CreateCampaignModel, "id">): Promise<{
15
+ id: string;
16
+ chainId: number;
17
+ type: "INVALID" | "ERC20" | "CLAMM" | "ERC20_SNAPSHOT" | "JSON_AIRDROP" | "SILO" | "RADIANT" | "MORPHO" | "DOLOMITE" | "BADGER" | "COMPOUND" | "AJNA" | "EULER" | "UNISWAP_V4" | "ION" | "EIGENLAYER";
18
+ identifier: string;
19
+ name: string;
20
+ status: "PAST" | "LIVE" | "SOON";
21
+ action: import("../../../../database/api/.generated").$Enums.OpportunityAction;
22
+ tokens: ({
23
+ symbol: string;
24
+ name: string | null;
25
+ id: string;
26
+ icon: string;
27
+ chainId: number;
28
+ address: string;
29
+ decimals: number;
30
+ verified: boolean;
31
+ isTest: boolean;
32
+ } & {
33
+ price?: number | null | undefined;
34
+ })[];
35
+ mainProtocol: "morpho" | "aura" | "poolside" | "gearbox" | "fluid" | "compound" | "ionic" | "layerbank" | "moonwell" | "fenix" | "syncswap" | "beefy" | "aerodrome" | "velodrome" | "curve" | "akron" | "dragonswap" | "koi" | "baseswap" | "zkswap" | "rfx" | "woofi" | "zkSwapThreePool" | "venus" | "reactor_fusion" | "balancer" | "aave" | "arthswap" | "camelot" | "crust" | "horiza" | "izumi" | "kim" | "pancakeswap-v3" | "quickswap-algebra" | "quickswap-uni" | "ramses" | "retro" | "stryke" | "stryke-pcs" | "stryke-sushi" | "sushiswap-v3" | "swapr" | "thruster" | "uniswap-v3" | "voltage" | "zero" | "supswap-v3" | "thirdtrade" | "uniswap-v2" | "syncswap-v3" | "neptune" | "radiant" | "euler" | "sturdy" | "frax" | "silo" | "coumpound" | "dolomite" | "badger" | "ajna" | "ion" | "eigenlayer" | undefined;
36
+ depositUrl: any;
37
+ tags: string[];
38
+ }>;
39
+ /**
40
+ * deletes and recreates an opportunity with fresh data
41
+ */
42
+ static recreate(opportunityId: string | OpportunityUnique): Promise<{
43
+ id: string;
44
+ chainId: number;
45
+ type: "INVALID" | "ERC20" | "CLAMM" | "ERC20_SNAPSHOT" | "JSON_AIRDROP" | "SILO" | "RADIANT" | "MORPHO" | "DOLOMITE" | "BADGER" | "COMPOUND" | "AJNA" | "EULER" | "UNISWAP_V4" | "ION" | "EIGENLAYER";
46
+ identifier: string;
47
+ name: string;
48
+ status: "PAST" | "LIVE" | "SOON";
49
+ action: import("../../../../database/api/.generated").$Enums.OpportunityAction;
50
+ tokens: ({
51
+ symbol: string;
52
+ name: string | null;
53
+ id: string;
54
+ icon: string;
55
+ chainId: number;
56
+ address: string;
57
+ decimals: number;
58
+ verified: boolean;
59
+ isTest: boolean;
60
+ } & {
61
+ price?: number | null | undefined;
62
+ })[];
63
+ mainProtocol: "morpho" | "aura" | "poolside" | "gearbox" | "fluid" | "compound" | "ionic" | "layerbank" | "moonwell" | "fenix" | "syncswap" | "beefy" | "aerodrome" | "velodrome" | "curve" | "akron" | "dragonswap" | "koi" | "baseswap" | "zkswap" | "rfx" | "woofi" | "zkSwapThreePool" | "venus" | "reactor_fusion" | "balancer" | "aave" | "arthswap" | "camelot" | "crust" | "horiza" | "izumi" | "kim" | "pancakeswap-v3" | "quickswap-algebra" | "quickswap-uni" | "ramses" | "retro" | "stryke" | "stryke-pcs" | "stryke-sushi" | "sushiswap-v3" | "swapr" | "thruster" | "uniswap-v3" | "voltage" | "zero" | "supswap-v3" | "thirdtrade" | "uniswap-v2" | "syncswap-v3" | "neptune" | "radiant" | "euler" | "sturdy" | "frax" | "silo" | "coumpound" | "dolomite" | "badger" | "ajna" | "ion" | "eigenlayer" | undefined;
64
+ depositUrl: any;
65
+ tags: string[];
66
+ }>;
14
67
  /**
15
68
  * build/fetch metadata of a campaign's opportunity
16
69
  * @param campaign
17
70
  * @returns {OpportunityMetadata}
18
71
  */
19
- static getMetadata(campaign: CreateCampaignModel): Promise<OpportunityMetadata>;
72
+ static getMetadata(campaign: Omit<CreateCampaignModel, "id">): Promise<OpportunityMetadata>;
20
73
  static updateMetadata(chain: ChainId): Promise<void>;
21
74
  static getUniqueWithCampaignsOrThrow(opportunityId: string | OpportunityUnique): Promise<OpportunityWithCampaignsResourceModel>;
22
75
  static getUniqueOrThrow(opportunityId: string | OpportunityUnique): Promise<OpportunityResourceModel>;
@@ -1,11 +1,12 @@
1
1
  import { NotFoundError } from "../../../errors";
2
2
  import { CampaignService } from "../campaign";
3
3
  import { log } from "../../../utils/logger";
4
- import { OpportunityAction, Prisma } from "../../../../database/api/.generated";
4
+ import { OpportunityAction, Prisma, Status } from "../../../../database/api/.generated";
5
5
  import { record } from "@elysiajs/opentelemetry";
6
6
  import { CacheService } from "../cache";
7
7
  import { TTLPresets } from "../cache/cache.model";
8
8
  import { TokenService } from "../token";
9
+ import { UserService } from "../user";
9
10
  import { OpportunityRepository } from "./opportunity.repository";
10
11
  import { getAjnaMetadata } from "./subservices/getAjnaMetadata.service";
11
12
  import { getBadgerMetadata } from "./subservices/getBadgerMetadata.service";
@@ -31,7 +32,58 @@ export class OpportunityService {
31
32
  */
32
33
  static async create(newOpp) {
33
34
  const id = OpportunityService.hashId(newOpp);
34
- return await OpportunityRepository.create({ ...newOpp, id });
35
+ return await OpportunityRepository.upsert({ ...newOpp, id });
36
+ }
37
+ static async createFromCampaign(campaign) {
38
+ const metadata = await OpportunityService.getMetadata(campaign);
39
+ metadata.tags = [...((await UserService.findUnique(campaign.creator))?.tags ?? []), ...(campaign?.tags ?? [])];
40
+ const opportunityId = OpportunityService.hashId({
41
+ chainId: campaign.computeChainId,
42
+ identifier: campaign.opportunityIdentifier,
43
+ type: campaign.type,
44
+ });
45
+ const tokens = (await TokenService.getManyOrCreate(metadata.tokens)).filter(t => t !== undefined);
46
+ const params = JSON.parse(campaign.params);
47
+ const opportunity = {
48
+ id: opportunityId,
49
+ chainId: campaign.computeChainId,
50
+ type: campaign.type,
51
+ identifier: campaign.opportunityIdentifier, // mainParameter
52
+ name: metadata.name,
53
+ status: +campaign.startTimestamp >= new Date().getTime() * 1000
54
+ ? Status.LIVE
55
+ : +campaign.endTimestamp < new Date().getTime() * 1000
56
+ ? Status.PAST
57
+ : Status.SOON,
58
+ action: metadata.action,
59
+ tokens,
60
+ mainProtocol: metadata.mainProtocol,
61
+ depositUrl: !!params.url ? params.url : undefined,
62
+ tags: metadata.tags,
63
+ };
64
+ await OpportunityRepository.upsert(opportunity);
65
+ return opportunity;
66
+ }
67
+ /**
68
+ * deletes and recreates an opportunity with fresh data
69
+ */
70
+ static async recreate(opportunityId) {
71
+ const id = typeof opportunityId === "string" ? opportunityId : OpportunityService.hashId(opportunityId);
72
+ const opportunity = await OpportunityRepository.findUnique(id);
73
+ if (!opportunity)
74
+ throw new NotFoundError();
75
+ const firstCampaign = opportunity?.Campaigns[0];
76
+ return await OpportunityService.createFromCampaign({
77
+ ...firstCampaign,
78
+ chainId: firstCampaign.distributionChainId,
79
+ creator: firstCampaign.creatorAddress,
80
+ rewardTokenAddress: firstCampaign.RewardToken.address,
81
+ opportunityIdentifier: opportunity.identifier,
82
+ subType: firstCampaign.subType ?? undefined,
83
+ params: JSON.stringify(firstCampaign.params),
84
+ startTimestamp: firstCampaign.startTimestamp.toString(),
85
+ endTimestamp: firstCampaign.endTimestamp.toString(),
86
+ });
35
87
  }
36
88
  /**
37
89
  * build/fetch metadata of a campaign's opportunity
@@ -39,10 +91,9 @@ export class OpportunityService {
39
91
  * @returns {OpportunityMetadata}
40
92
  */
41
93
  static async getMetadata(campaign) {
42
- const campaignType = CampaignService.getTypeFromV3(campaign.type);
43
94
  const campaignParams = JSON.parse(campaign.params);
44
95
  const chainId = campaign.computeChainId === 0 ? campaign.chainId : campaign.computeChainId;
45
- switch (campaignType) {
96
+ switch (campaign.type) {
46
97
  case "CLAMM":
47
98
  return getClammMetadata(chainId, campaignParams);
48
99
  case "ERC20":
@@ -22,7 +22,7 @@ export const getClammMetadata = (chainId, params) => {
22
22
  platform = "iZUMi";
23
23
  }
24
24
  return {
25
- name: `Provide liquidity to ${params.symbolToken0}-${params.symbolToken1}${params.poolFee ? ` ${params.poolFee}%` : ""}`,
25
+ name: `Provide liquidity to ${platform} ${params.symbolToken0}-${params.symbolToken1}${params.poolFee ? ` ${params.poolFee}%` : ""}`,
26
26
  action: OpportunityAction.POOL,
27
27
  tokens: [
28
28
  { chainId, address: params.token0 },
@@ -1,9 +1,12 @@
1
1
  import { log } from "../../../../utils/logger";
2
2
  import { CampaignType } from "../../../../../database/api/.generated";
3
3
  import { CampaignService } from "../../campaign";
4
+ import { ProtocolService } from "../../protocol";
4
5
  export const getErc20Metadata = async (chainId, campaignId, rewardToken, amount, params) => {
5
6
  let action = "HOLD";
6
7
  let name = `Hold ${params.symbolTargetToken}`;
8
+ let mainProtocolId = undefined;
9
+ const tokens = [{ chainId, address: params.targetToken }];
7
10
  try {
8
11
  const [dynamicData] = await CampaignService.fetchDynamicData(chainId, CampaignType.ERC20, [
9
12
  {
@@ -13,56 +16,33 @@ export const getErc20Metadata = async (chainId, campaignId, rewardToken, amount,
13
16
  campaignParameters: params,
14
17
  },
15
18
  ]);
16
- const map = {
17
- actions: {
18
- POOL: [
19
- "uniswapv2",
20
- "velodrome",
21
- "aerodrome",
22
- "balancerGauge",
23
- "balancerPool",
24
- "curve",
25
- "aura",
26
- "akron",
27
- "beefy",
28
- "dragonswap",
29
- "poolside",
30
- "koi",
31
- "pancakeswap",
32
- "tempest",
33
- "cross_curve",
34
- "zkswap",
35
- "maverickBoostedPosition",
36
- "zkSwapThreePool",
37
- "syncswap",
38
- "rfx",
39
- ],
40
- BORROW: ["radiant_borrow", "aave_borrowing", "euler_borrow", "zerolend_borrowing"],
41
- LEND: [
42
- "gearbox",
43
- "compound",
44
- "radiant_lend",
45
- "aave_lending",
46
- "sturdy_aggregator",
47
- "sturdy_silo",
48
- "fraxlend",
49
- "moonwell",
50
- "ionic",
51
- "fluid",
52
- "silostaking",
53
- "euler_lend",
54
- "layerbank",
55
- "zerolend_lending",
56
- "venus",
57
- "woofi",
58
- "reactor_fusion",
59
- ],
60
- },
61
- };
62
- action =
63
- Object.entries(map.actions).find(([_action, _types]) => _types.includes(dynamicData?.type ?? ""))?.[0] ?? "HOLD";
64
- //TODO: /!\ fix this "as any"
19
+ action = "HOLD"; // In case the protocol doesn't exist, initialize it to HOLD
65
20
  name = dynamicData?.typeInfo?.cardName;
21
+ mainProtocolId = dynamicData?.typeInfo?.protocol?.toLowerCase().replace(" ", "");
22
+ const protocol = (await ProtocolService.findMany({ id: mainProtocolId }))?.[0];
23
+ if (!protocol) {
24
+ mainProtocolId = undefined;
25
+ }
26
+ mainProtocolId = protocol?.id;
27
+ // Case of lending protocols and receipt tokens
28
+ if (!!dynamicData && !!dynamicData.typeInfo?.underlying) {
29
+ tokens.push({ chainId, address: dynamicData.typeInfo.underlying });
30
+ }
31
+ // Case of perps protocols
32
+ if (!!dynamicData && !!dynamicData.typeInfo?.shortToken && !!dynamicData.typeInfo?.longToken) {
33
+ tokens.push({ chainId, address: dynamicData.typeInfo.shortToken });
34
+ tokens.push({ chainId, address: dynamicData.typeInfo.longToken });
35
+ }
36
+ // Case of weird AMMs
37
+ if (!!dynamicData && !!dynamicData.typeInfo?.tokenA && !!dynamicData.typeInfo?.tokenB) {
38
+ tokens.push({ chainId, address: dynamicData.typeInfo.tokenA });
39
+ tokens.push({ chainId, address: dynamicData.typeInfo.tokenB });
40
+ }
41
+ // Case of AMMs
42
+ if (!!dynamicData && !!dynamicData.typeInfo?.token0 && !!dynamicData.typeInfo?.token1) {
43
+ tokens.push({ chainId, address: dynamicData.typeInfo.token0 });
44
+ tokens.push({ chainId, address: dynamicData.typeInfo.token1 });
45
+ }
66
46
  }
67
47
  catch {
68
48
  log.warn(`failed to fetch dynamic data for ERC20 campaign ${campaignId}`);
@@ -70,6 +50,7 @@ export const getErc20Metadata = async (chainId, campaignId, rewardToken, amount,
70
50
  return {
71
51
  action,
72
52
  name,
73
- tokens: [{ chainId, address: params.targetToken }],
53
+ tokens,
54
+ mainProtocol: mainProtocolId,
74
55
  };
75
56
  };
@@ -26,6 +26,10 @@ export declare const v4: Elysia<"/v4", false, {
26
26
  post: {
27
27
  body: {
28
28
  name?: string | undefined;
29
+ tags?: string[] | undefined;
30
+ depositUrl?: string | undefined;
31
+ protocols?: string[] | undefined;
32
+ mainProtocol?: string | undefined;
29
33
  type: "INVALID" | "ERC20" | "CLAMM" | "ERC20_SNAPSHOT" | "JSON_AIRDROP" | "SILO" | "RADIANT" | "MORPHO" | "DOLOMITE" | "BADGER" | "COMPOUND" | "AJNA" | "EULER" | "UNISWAP_V4" | "ION" | "EIGENLAYER";
30
34
  tokens: {
31
35
  chainId: number;
@@ -35,8 +39,6 @@ export declare const v4: Elysia<"/v4", false, {
35
39
  identifier: string;
36
40
  chainId: number;
37
41
  action: "INVALID" | "POOL" | "HOLD" | "DROP" | "LEND" | "BORROW";
38
- protocols: string[];
39
- mainProtocol: string;
40
42
  };
41
43
  params: {};
42
44
  query: unknown;
@@ -49,6 +51,48 @@ export declare const v4: Elysia<"/v4", false, {
49
51
  };
50
52
  };
51
53
  };
54
+ } & {
55
+ opportunities: {
56
+ ":id": {
57
+ post: {
58
+ body: unknown;
59
+ params: {
60
+ id: string;
61
+ };
62
+ query: unknown;
63
+ headers: {
64
+ authorization: string;
65
+ };
66
+ response: {
67
+ 200: {
68
+ id: string;
69
+ chainId: number;
70
+ type: "INVALID" | "ERC20" | "CLAMM" | "ERC20_SNAPSHOT" | "JSON_AIRDROP" | "SILO" | "RADIANT" | "MORPHO" | "DOLOMITE" | "BADGER" | "COMPOUND" | "AJNA" | "EULER" | "UNISWAP_V4" | "ION" | "EIGENLAYER";
71
+ identifier: string;
72
+ name: string;
73
+ status: "PAST" | "LIVE" | "SOON";
74
+ action: import("../../../database/api/.generated").$Enums.OpportunityAction;
75
+ tokens: ({
76
+ symbol: string;
77
+ name: string | null;
78
+ id: string;
79
+ icon: string;
80
+ chainId: number;
81
+ address: string;
82
+ decimals: number;
83
+ verified: boolean;
84
+ isTest: boolean;
85
+ } & {
86
+ price?: number | null | undefined;
87
+ })[];
88
+ mainProtocol: "morpho" | "aura" | "poolside" | "gearbox" | "fluid" | "compound" | "ionic" | "layerbank" | "moonwell" | "fenix" | "syncswap" | "beefy" | "aerodrome" | "velodrome" | "curve" | "akron" | "dragonswap" | "koi" | "baseswap" | "zkswap" | "rfx" | "woofi" | "zkSwapThreePool" | "venus" | "reactor_fusion" | "balancer" | "aave" | "arthswap" | "camelot" | "crust" | "horiza" | "izumi" | "kim" | "pancakeswap-v3" | "quickswap-algebra" | "quickswap-uni" | "ramses" | "retro" | "stryke" | "stryke-pcs" | "stryke-sushi" | "sushiswap-v3" | "swapr" | "thruster" | "uniswap-v3" | "voltage" | "zero" | "supswap-v3" | "thirdtrade" | "uniswap-v2" | "syncswap-v3" | "neptune" | "radiant" | "euler" | "sturdy" | "frax" | "silo" | "coumpound" | "dolomite" | "badger" | "ajna" | "ion" | "eigenlayer" | undefined;
89
+ depositUrl: any;
90
+ tags: string[];
91
+ };
92
+ };
93
+ };
94
+ };
95
+ };
52
96
  } & {
53
97
  opportunities: {
54
98
  index: {
@@ -539,7 +583,7 @@ export declare const v4: Elysia<"/v4", false, {
539
583
  tags?: string[] | undefined;
540
584
  identifier?: string | undefined;
541
585
  subType?: number | undefined;
542
- type: number;
586
+ type: "INVALID" | "ERC20" | "CLAMM" | "ERC20_SNAPSHOT" | "JSON_AIRDROP" | "SILO" | "RADIANT" | "MORPHO" | "DOLOMITE" | "BADGER" | "COMPOUND" | "AJNA" | "EULER" | "UNISWAP_V4" | "ION" | "EIGENLAYER";
543
587
  params: string;
544
588
  creator: string;
545
589
  chainId: number;
@@ -42,7 +42,7 @@ export function generateCardName(type, typeInfo, campaign, symbols = [""]) {
42
42
  return `Lend ${cardToken} on ${typeInfo.protocol}`;
43
43
  }
44
44
  case tokenType.rfx:
45
- return `${typeInfo.protocol} ${typeInfo.symbolShortToken}/${typeInfo.symbolLongToken}`;
45
+ return `Supply ${typeInfo.symbolShortToken}/${typeInfo.symbolLongToken} on ${typeInfo.protocol}`;
46
46
  case tokenType.radiant_borrow:
47
47
  case tokenType.aave_borrowing:
48
48
  case tokenType.yei_borrowing: