@merkl/api 0.10.251 → 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.
- 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/modules/v4/token/token.repository.js +1 -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,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
|
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:
|