@merkl/api 0.10.252 → 0.10.253
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/src/eden/index.d.ts +123 -9
- package/dist/src/index.d.ts +47 -3
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/RfxProcessor.js +1 -0
- package/dist/src/modules/v4/campaign/campaign.controller.d.ts +1 -1
- package/dist/src/modules/v4/campaign/campaign.model.d.ts +18 -1
- package/dist/src/modules/v4/campaign/campaign.model.js +1 -1
- package/dist/src/modules/v4/campaign/campaign.repository.d.ts +2 -3
- package/dist/src/modules/v4/campaign/campaign.repository.js +9 -70
- package/dist/src/modules/v4/campaign/campaign.service.js +6 -23
- package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +46 -2
- package/dist/src/modules/v4/opportunity/opportunity.controller.js +23 -0
- package/dist/src/modules/v4/opportunity/opportunity.model.d.ts +4 -2
- package/dist/src/modules/v4/opportunity/opportunity.model.js +4 -2
- package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +148 -1
- package/dist/src/modules/v4/opportunity/opportunity.repository.js +45 -25
- package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +54 -1
- package/dist/src/modules/v4/opportunity/opportunity.service.js +55 -4
- package/dist/src/modules/v4/opportunity/subservices/getClammMetadata.service.js +1 -1
- package/dist/src/modules/v4/opportunity/subservices/getErc20Metadata.service.js +31 -50
- package/dist/src/modules/v4/router.d.ts +47 -3
- package/dist/src/utils/generateCardName.js +1 -1
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- 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
|
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
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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.
|
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 (
|
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
|
-
|
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
|
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:
|
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
|
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:
|