@merkl/api 0.10.251 → 0.10.253

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) 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/modules/v4/token/token.repository.js +1 -3
  22. package/dist/src/utils/generateCardName.js +1 -1
  23. package/dist/tsconfig.package.tsbuildinfo +1 -1
  24. 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,9 +42,7 @@ export class TokenRepository {
42
42
  : query.displaySymbol
43
43
  ? [
44
44
  {
45
- displaySymbol: {
46
- not: "",
47
- },
45
+ displaySymbol: "",
48
46
  symbol: { equals: query.displaySymbol, mode: "insensitive" },
49
47
  },
50
48
  { displaySymbol: { equals: query.displaySymbol, mode: "insensitive" } },
@@ -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: