@merkl/api 0.15.40 → 0.15.42
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 +117 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/jobs/etl/reward-breakdowns.js +79 -71
- package/dist/src/modules/v4/bucket/bucket.service.d.ts +1 -1
- package/dist/src/modules/v4/chain/chain.controller.d.ts +29 -0
- package/dist/src/modules/v4/chain/chain.controller.js +7 -1
- package/dist/src/modules/v4/chain/chain.model.d.ts +11 -0
- package/dist/src/modules/v4/chain/chain.model.js +8 -0
- package/dist/src/modules/v4/chain/chain.repository.d.ts +16 -5
- package/dist/src/modules/v4/chain/chain.repository.js +21 -2
- package/dist/src/modules/v4/chain/chain.service.d.ts +9 -1
- package/dist/src/modules/v4/chain/chain.service.js +3 -4
- package/dist/src/modules/v4/protocol/protocol.repository.d.ts +34 -2
- package/dist/src/modules/v4/protocol/protocol.repository.js +13 -0
- package/dist/src/modules/v4/protocol/protocol.service.js +7 -1
- package/dist/src/modules/v4/router.d.ts +29 -0
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/src/eden/index.d.ts
CHANGED
@@ -1739,6 +1739,28 @@ declare const eden: {
|
|
1739
1739
|
}[];
|
1740
1740
|
}[];
|
1741
1741
|
}>>;
|
1742
|
+
post: (body: {
|
1743
|
+
name: string;
|
1744
|
+
id: number;
|
1745
|
+
icon: string;
|
1746
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
1747
|
+
explorerUrl: string;
|
1748
|
+
}, options: {
|
1749
|
+
headers: {
|
1750
|
+
authorization: string;
|
1751
|
+
};
|
1752
|
+
query?: Record<string, unknown> | undefined;
|
1753
|
+
fetch?: RequestInit | undefined;
|
1754
|
+
}) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
1755
|
+
200: {
|
1756
|
+
Explorer: {
|
1757
|
+
type: import("../../database/api/.generated").$Enums.ExplorerType;
|
1758
|
+
url: string;
|
1759
|
+
id: string;
|
1760
|
+
chainId: number;
|
1761
|
+
}[];
|
1762
|
+
};
|
1763
|
+
}>>;
|
1742
1764
|
};
|
1743
1765
|
count: {
|
1744
1766
|
get: (options: {
|
@@ -4839,6 +4861,28 @@ declare const eden: {
|
|
4839
4861
|
}[];
|
4840
4862
|
}[];
|
4841
4863
|
}>>;
|
4864
|
+
post: (body: {
|
4865
|
+
name: string;
|
4866
|
+
id: number;
|
4867
|
+
icon: string;
|
4868
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
4869
|
+
explorerUrl: string;
|
4870
|
+
}, options: {
|
4871
|
+
headers: {
|
4872
|
+
authorization: string;
|
4873
|
+
};
|
4874
|
+
query?: Record<string, unknown> | undefined;
|
4875
|
+
fetch?: RequestInit | undefined;
|
4876
|
+
}) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
4877
|
+
200: {
|
4878
|
+
Explorer: {
|
4879
|
+
type: import("../../database/api/.generated").$Enums.ExplorerType;
|
4880
|
+
url: string;
|
4881
|
+
id: string;
|
4882
|
+
chainId: number;
|
4883
|
+
}[];
|
4884
|
+
};
|
4885
|
+
}>>;
|
4842
4886
|
};
|
4843
4887
|
count: {
|
4844
4888
|
get: (options: {
|
@@ -9006,6 +9050,35 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
|
|
9006
9050
|
};
|
9007
9051
|
};
|
9008
9052
|
};
|
9053
|
+
} & {
|
9054
|
+
chains: {
|
9055
|
+
index: {
|
9056
|
+
post: {
|
9057
|
+
body: {
|
9058
|
+
name: string;
|
9059
|
+
id: number;
|
9060
|
+
icon: string;
|
9061
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
9062
|
+
explorerUrl: string;
|
9063
|
+
};
|
9064
|
+
params: {};
|
9065
|
+
query: unknown;
|
9066
|
+
headers: {
|
9067
|
+
authorization: string;
|
9068
|
+
};
|
9069
|
+
response: {
|
9070
|
+
200: {
|
9071
|
+
Explorer: {
|
9072
|
+
type: import("../../database/api/.generated").$Enums.ExplorerType;
|
9073
|
+
url: string;
|
9074
|
+
id: string;
|
9075
|
+
chainId: number;
|
9076
|
+
}[];
|
9077
|
+
};
|
9078
|
+
};
|
9079
|
+
};
|
9080
|
+
};
|
9081
|
+
};
|
9009
9082
|
};
|
9010
9083
|
} & {
|
9011
9084
|
v4: {
|
@@ -13388,6 +13461,28 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
|
|
13388
13461
|
}[];
|
13389
13462
|
}[];
|
13390
13463
|
}>>;
|
13464
|
+
post: (body: {
|
13465
|
+
name: string;
|
13466
|
+
id: number;
|
13467
|
+
icon: string;
|
13468
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
13469
|
+
explorerUrl: string;
|
13470
|
+
}, options: {
|
13471
|
+
headers: {
|
13472
|
+
authorization: string;
|
13473
|
+
};
|
13474
|
+
query?: Record<string, unknown> | undefined;
|
13475
|
+
fetch?: RequestInit | undefined;
|
13476
|
+
}) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
13477
|
+
200: {
|
13478
|
+
Explorer: {
|
13479
|
+
type: import("../../database/api/.generated").$Enums.ExplorerType;
|
13480
|
+
url: string;
|
13481
|
+
id: string;
|
13482
|
+
chainId: number;
|
13483
|
+
}[];
|
13484
|
+
};
|
13485
|
+
}>>;
|
13391
13486
|
};
|
13392
13487
|
count: {
|
13393
13488
|
get: (options: {
|
@@ -16488,6 +16583,28 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
|
|
16488
16583
|
}[];
|
16489
16584
|
}[];
|
16490
16585
|
}>>;
|
16586
|
+
post: (body: {
|
16587
|
+
name: string;
|
16588
|
+
id: number;
|
16589
|
+
icon: string;
|
16590
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
16591
|
+
explorerUrl: string;
|
16592
|
+
}, options: {
|
16593
|
+
headers: {
|
16594
|
+
authorization: string;
|
16595
|
+
};
|
16596
|
+
query?: Record<string, unknown> | undefined;
|
16597
|
+
fetch?: RequestInit | undefined;
|
16598
|
+
}) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
16599
|
+
200: {
|
16600
|
+
Explorer: {
|
16601
|
+
type: import("../../database/api/.generated").$Enums.ExplorerType;
|
16602
|
+
url: string;
|
16603
|
+
id: string;
|
16604
|
+
chainId: number;
|
16605
|
+
}[];
|
16606
|
+
};
|
16607
|
+
}>>;
|
16491
16608
|
};
|
16492
16609
|
count: {
|
16493
16610
|
get: (options: {
|
package/dist/src/index.d.ts
CHANGED
@@ -2108,6 +2108,35 @@ declare const app: Elysia<"", false, {
|
|
2108
2108
|
};
|
2109
2109
|
};
|
2110
2110
|
};
|
2111
|
+
} & {
|
2112
|
+
chains: {
|
2113
|
+
index: {
|
2114
|
+
post: {
|
2115
|
+
body: {
|
2116
|
+
name: string;
|
2117
|
+
id: number;
|
2118
|
+
icon: string;
|
2119
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
2120
|
+
explorerUrl: string;
|
2121
|
+
};
|
2122
|
+
params: {};
|
2123
|
+
query: unknown;
|
2124
|
+
headers: {
|
2125
|
+
authorization: string;
|
2126
|
+
};
|
2127
|
+
response: {
|
2128
|
+
200: {
|
2129
|
+
Explorer: {
|
2130
|
+
type: import("../database/api/.generated").$Enums.ExplorerType;
|
2131
|
+
url: string;
|
2132
|
+
id: string;
|
2133
|
+
chainId: number;
|
2134
|
+
}[];
|
2135
|
+
};
|
2136
|
+
};
|
2137
|
+
};
|
2138
|
+
};
|
2139
|
+
};
|
2111
2140
|
};
|
2112
2141
|
} & {
|
2113
2142
|
v4: {
|
@@ -1,9 +1,9 @@
|
|
1
1
|
// ─── Reward Breakdowns ETL ───────────────────────────────────────────────────
|
2
|
-
if (!process.env.ENV || !process.env.CHAIN_ID || !process.env.ROOT)
|
2
|
+
if (!process.env.DATABASE_API_URL || !process.env.ENV || !process.env.CHAIN_ID || !process.env.ROOT)
|
3
3
|
throw new Error("[ENV]: missing variable");
|
4
|
+
import { BucketService } from "../../modules/v4/bucket/bucket.service";
|
4
5
|
import { log } from "../../utils/logger";
|
5
6
|
import { apiDbClient } from "../../utils/prisma";
|
6
|
-
import { withRetry } from "@sdk";
|
7
7
|
import { S3Client } from "bun";
|
8
8
|
import moment from "moment";
|
9
9
|
// ─── Global Variables ────────────────────────────────────────
|
@@ -13,10 +13,9 @@ const gcsClient = new S3Client({
|
|
13
13
|
endpoint: process.env.GCS_ENDPOINT,
|
14
14
|
bucket: `merkl-rewards-lake-${process.env.ENV}`,
|
15
15
|
});
|
16
|
-
|
17
|
-
if (!(await file.exists()))
|
18
|
-
file = gcsClient.file(`breakdowns/${process.env.CHAIN_ID}-${process.env.ROOT}.gz`);
|
16
|
+
const file = gcsClient.file(`breakdowns/${process.env.CHAIN_ID}-${process.env.ROOT}`);
|
19
17
|
const failedBatches = [];
|
18
|
+
const missingCampaigns = [];
|
20
19
|
// ─── Extract ─────────────────────────────────────────────────────────────────
|
21
20
|
const extract = async () => {
|
22
21
|
if (!file.exists())
|
@@ -30,106 +29,115 @@ const extract = async () => {
|
|
30
29
|
buffer += textDecoder.decode(chunk);
|
31
30
|
const lines = buffer.split("\n");
|
32
31
|
buffer = lines.pop() || "";
|
33
|
-
for (const line of lines)
|
34
|
-
if (line.trim())
|
32
|
+
for (const line of lines) {
|
33
|
+
if (line.trim())
|
35
34
|
data.push(...(await transform(JSON.parse(line))));
|
36
|
-
|
35
|
+
}
|
37
36
|
if (data.length < 30_000)
|
38
37
|
continue;
|
39
38
|
try {
|
40
|
-
count += await
|
39
|
+
count += await load(data);
|
41
40
|
data = [];
|
42
41
|
}
|
43
42
|
catch (err) {
|
44
|
-
|
43
|
+
// log.error("Failed to insert a batch, adding it to the fail queue.", err);
|
45
44
|
failedBatches.push(data);
|
46
45
|
data = [];
|
47
46
|
}
|
48
47
|
}
|
49
48
|
try {
|
50
|
-
count += await
|
49
|
+
count += await load(data);
|
51
50
|
data = [];
|
52
51
|
}
|
53
52
|
catch (err) {
|
54
|
-
|
53
|
+
// log.error("Failed to insert a batch, adding it to the fail queue.", err);
|
55
54
|
failedBatches.push(data);
|
56
55
|
data = [];
|
57
56
|
}
|
58
57
|
return count;
|
59
58
|
};
|
60
59
|
// ─── Transform ───────────────────────────────────────────────────────────────
|
61
|
-
const transform =
|
62
|
-
const
|
63
|
-
|
64
|
-
|
65
|
-
const
|
66
|
-
|
60
|
+
const transform = (rewardBreakdowns) => {
|
61
|
+
const transformedBreakdowns = Array(rewardBreakdowns.length);
|
62
|
+
let i = 0;
|
63
|
+
for (i; i < rewardBreakdowns.length; i++) {
|
64
|
+
const rewardTokenId = Bun.hash(`${process.env.CHAIN_ID}${rewardBreakdowns[i].token}`).toString();
|
65
|
+
const rewardId = Bun.hash(`${process.env.ROOT}${rewardBreakdowns[i].recipient}${rewardTokenId}`).toString();
|
66
|
+
const campaignId = Bun.hash(`${process.env.CHAIN_ID}${rewardBreakdowns[i].campaignId}`).toString();
|
67
|
+
transformedBreakdowns[i] = {
|
67
68
|
rewardId,
|
68
|
-
protocolId:
|
69
|
-
campaignId
|
70
|
-
reason:
|
71
|
-
amount:
|
72
|
-
claimed:
|
73
|
-
pending:
|
74
|
-
|
69
|
+
protocolId: rewardBreakdowns[i].protocolId ? rewardBreakdowns[i].protocolId : undefined,
|
70
|
+
campaignId,
|
71
|
+
reason: rewardBreakdowns[i].reason ? rewardBreakdowns[i].reason : "",
|
72
|
+
amount: rewardBreakdowns[i].amount,
|
73
|
+
claimed: rewardBreakdowns[i].claimed,
|
74
|
+
pending: rewardBreakdowns[i].pending,
|
75
|
+
merklCampaignId: rewardBreakdowns[i].campaignId,
|
76
|
+
};
|
75
77
|
}
|
76
|
-
return
|
78
|
+
return transformedBreakdowns;
|
77
79
|
};
|
78
80
|
// ─── Load ────────────────────────────────────────────────────────────────────
|
79
81
|
const load = async (rewardBreakdowns) => {
|
80
|
-
|
81
|
-
|
82
|
-
// const foundCampaigns: string[] = [];
|
83
|
-
// const campaigns = rewardBreakdowns.reduce(
|
84
|
-
// (acc, x) => {
|
85
|
-
// const campaignUnique = {
|
86
|
-
// campaignId: x.campaignId,
|
87
|
-
// distributionChain: Number.parseInt(process.env.CHAIN_ID!),
|
88
|
-
// };
|
89
|
-
// if (!Object.keys(acc).includes(CampaignService.hashId(campaignUnique)))
|
90
|
-
// acc[CampaignService.hashId(campaignUnique)] = {
|
91
|
-
// campaignId: x.campaignId,
|
92
|
-
// distributionChain: Number.parseInt(process.env.CHAIN_ID!),
|
93
|
-
// };
|
94
|
-
// return acc;
|
95
|
-
// },
|
96
|
-
// {} as Record<string, CampaignUnique>
|
97
|
-
// );
|
98
|
-
// for (const campaign of Object.values(campaigns)) {
|
99
|
-
// const campaignExists = await CampaignService.checkIfExist(campaign);
|
100
|
-
// if (!campaignExists) {
|
101
|
-
// const { success, fail } = await CampaignService.fill([campaign]);
|
102
|
-
// if (fail === 1 || success !== 1) {
|
103
|
-
// missingCampaigns.push(CampaignService.hashId(campaign));
|
104
|
-
// log.warn(`createManyBreakdown - Missing campaign: ${campaign.campaignId}`);
|
105
|
-
// continue;
|
106
|
-
// }
|
107
|
-
// }
|
108
|
-
// foundCampaigns.push(CampaignService.hashId(campaign));
|
109
|
-
// }
|
110
|
-
// if (missingCampaigns.length !== 0) {
|
111
|
-
// log.warn(`createManyBreakdown - Missing ${missingCampaigns.length} campaigns: ${missingCampaigns.join(", ")}`);
|
112
|
-
// }
|
113
|
-
return (await apiDbClient.rewardBreakdown.createMany({
|
114
|
-
data: rewardBreakdowns.map(x => {
|
115
|
-
const campaignId = Bun.hash(`${process.env.CHAIN_ID}${x.campaignId}`).toString();
|
116
|
-
return { ...x, campaignId };
|
117
|
-
}),
|
118
|
-
// .filter(x => !missingCampaigns.includes(x.campaignId)),
|
82
|
+
const count = (await apiDbClient.rewardBreakdown.createMany({
|
83
|
+
data: rewardBreakdowns,
|
119
84
|
skipDuplicates: true,
|
120
85
|
})).count;
|
86
|
+
log.info(`Successfully inserted ${rewardBreakdowns.length} reward breakdown(s).`);
|
87
|
+
return count;
|
88
|
+
};
|
89
|
+
// ─── Retry ───────────────────────────────────────────────────────────────────
|
90
|
+
// Using binary search
|
91
|
+
const retry = async (batches) => {
|
92
|
+
log.info(`Retrying ${batches.length} batch(es)...`);
|
93
|
+
let inserted = 0;
|
94
|
+
while (batches.length > 0) {
|
95
|
+
const currentBatch = batches.shift();
|
96
|
+
const firstHalf = currentBatch.splice(0, Math.ceil(currentBatch.length / 2));
|
97
|
+
const secondHalf = currentBatch;
|
98
|
+
// Process first half
|
99
|
+
try {
|
100
|
+
inserted += await load(firstHalf);
|
101
|
+
}
|
102
|
+
catch (err) {
|
103
|
+
log.error(JSON.stringify(firstHalf), err);
|
104
|
+
if (firstHalf.length > 1)
|
105
|
+
batches.push(firstHalf);
|
106
|
+
else
|
107
|
+
missingCampaigns.push(firstHalf[0].merklCampaignId);
|
108
|
+
}
|
109
|
+
// Process second half
|
110
|
+
try {
|
111
|
+
inserted += await load(secondHalf);
|
112
|
+
}
|
113
|
+
catch (err) {
|
114
|
+
log.error(JSON.stringify(secondHalf), err);
|
115
|
+
if (secondHalf.length > 1)
|
116
|
+
batches.push(secondHalf);
|
117
|
+
else
|
118
|
+
missingCampaigns.push(secondHalf[0].merklCampaignId);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
return inserted;
|
121
122
|
};
|
122
123
|
// ─── Main ────────────────────────────────────────────────────────────────────
|
123
124
|
export const main = async () => {
|
124
125
|
const start = moment().unix();
|
125
126
|
const count = await extract();
|
126
|
-
log.info(`✅ Successfully created ${count} new
|
127
|
+
log.info(`✅ Successfully created ${count} new record(s) for ${process.env.CHAIN_ID}-${process.env.ROOT} in ${moment().unix() - start} sec.`);
|
127
128
|
if (failedBatches.length !== 0) {
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
const inserted = await retry(failedBatches);
|
130
|
+
log.info(`Successfully inserted ${inserted} record(s) after retrying.`);
|
131
|
+
if (inserted !== failedBatches.length) {
|
132
|
+
log.error("breakdowns", `${failedBatches.length} breakdown(s) failed to insert.`);
|
133
|
+
// ─── Push Missing Campaigns To Bucket ────────────────
|
134
|
+
const bucket = new BucketService(`merkl-campaigns-lake-${process.env.ENV}`, `merkl-data-${process.env.ENV}`);
|
135
|
+
await bucket.push(`${process.env.CHAIN_ID}_missing_campaigns`, JSON.stringify(missingCampaigns), {
|
136
|
+
type: "application/json",
|
137
|
+
});
|
138
|
+
process.exit(1);
|
139
|
+
}
|
133
140
|
}
|
141
|
+
// if (failedBatches.length === 0) await file.delete();
|
134
142
|
};
|
135
143
|
main();
|
@@ -4,7 +4,7 @@ export declare class BucketService {
|
|
4
4
|
defaultUploadOptions: UploadOptions<unknown>;
|
5
5
|
constructor(bucket: string, projectId: string);
|
6
6
|
pushArray<T>(filename: string, arr: T[], options?: UploadOptions<T>): Promise<number>;
|
7
|
-
push(filename: string, text: string, options?: Omit<UploadOptions<never>, "transform">): Promise<number>;
|
7
|
+
push(filename: string, text: string, options?: Omit<UploadOptions<never>, "transform" | "separator">): Promise<number>;
|
8
8
|
pullArray<T>(filename: string, callback: (elem: string) => T, separator?: string): Promise<T[]>;
|
9
9
|
pull(filename: string): Promise<string>;
|
10
10
|
}
|
@@ -103,6 +103,35 @@ export declare const ChainController: Elysia<"/chains", false, {
|
|
103
103
|
};
|
104
104
|
};
|
105
105
|
};
|
106
|
+
} & {
|
107
|
+
chains: {
|
108
|
+
index: {
|
109
|
+
post: {
|
110
|
+
body: {
|
111
|
+
name: string;
|
112
|
+
id: number;
|
113
|
+
icon: string;
|
114
|
+
explorerType: "ETHERSCAN" | "BLOCKSCOUT";
|
115
|
+
explorerUrl: string;
|
116
|
+
};
|
117
|
+
params: {};
|
118
|
+
query: unknown;
|
119
|
+
headers: {
|
120
|
+
authorization: string;
|
121
|
+
};
|
122
|
+
response: {
|
123
|
+
200: {
|
124
|
+
Explorer: {
|
125
|
+
type: import("../../../../database/api/.generated").$Enums.ExplorerType;
|
126
|
+
url: string;
|
127
|
+
id: string;
|
128
|
+
chainId: number;
|
129
|
+
}[];
|
130
|
+
};
|
131
|
+
};
|
132
|
+
};
|
133
|
+
};
|
134
|
+
};
|
106
135
|
}, {
|
107
136
|
derive: {};
|
108
137
|
resolve: {};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { AuthorizationHeadersDto, BackOfficeGuard } from "../../../guards/BackOffice.guard";
|
2
2
|
import Elysia, { t } from "elysia";
|
3
|
-
import { ChainResourceDto, ChainUniqueDto, GetChainQueryDto, UpdateChainDto } from "./chain.model";
|
3
|
+
import { ChainResourceDto, ChainUniqueDto, CreateChainDto, GetChainQueryDto, UpdateChainDto, } from "./chain.model";
|
4
4
|
import { ChainService } from "./chain.service";
|
5
5
|
// ─── Chains Controller ───────────────────────────────────────────────────────
|
6
6
|
export const ChainController = new Elysia({ prefix: "/chains", detail: { tags: ["Chains"] } })
|
@@ -35,4 +35,10 @@ export const ChainController = new Elysia({ prefix: "/chains", detail: { tags: [
|
|
35
35
|
headers: AuthorizationHeadersDto,
|
36
36
|
beforeHandle: BackOfficeGuard,
|
37
37
|
detail: { hide: true },
|
38
|
+
})
|
39
|
+
.post("/", async ({ body }) => await ChainService.create(body), {
|
40
|
+
body: CreateChainDto,
|
41
|
+
headers: AuthorizationHeadersDto,
|
42
|
+
beforeHandle: BackOfficeGuard,
|
43
|
+
detail: { hide: true },
|
38
44
|
});
|
@@ -25,6 +25,17 @@ export declare const ChainArrayOptionalDto: import("@sinclair/typebox").TObject<
|
|
25
25
|
export declare const UpdateChainDto: import("@sinclair/typebox").TObject<{
|
26
26
|
icon: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
27
27
|
}>;
|
28
|
+
export declare const CreateChainDto: import("@sinclair/typebox").TObject<{
|
29
|
+
id: import("@sinclair/typebox").TNumber;
|
30
|
+
name: import("@sinclair/typebox").TString;
|
31
|
+
icon: import("@sinclair/typebox").TString;
|
32
|
+
explorerType: import("@sinclair/typebox").TEnum<{
|
33
|
+
ETHERSCAN: "ETHERSCAN";
|
34
|
+
BLOCKSCOUT: "BLOCKSCOUT";
|
35
|
+
}>;
|
36
|
+
explorerUrl: import("@sinclair/typebox").TString;
|
37
|
+
}>;
|
28
38
|
export type ChainUniqueModel = typeof ChainUniqueDto.static;
|
29
39
|
export type ChainSearchDto = typeof GetChainQueryDto.static;
|
30
40
|
export type UpdateChainModel = typeof UpdateChainDto.static;
|
41
|
+
export type CreateChainModel = typeof CreateChainDto.static;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { ExplorerType } from "../../../../database/api/.generated";
|
1
2
|
import { t } from "elysia";
|
2
3
|
// ─── DTOs ────────────────────────────────────────────────────────────────────
|
3
4
|
export const ChainResourceDto = t.Object({
|
@@ -26,3 +27,10 @@ export const UpdateChainDto = t.Object({
|
|
26
27
|
icon: t.Optional(t.String({ format: "uri" })),
|
27
28
|
// iconFile: t.Optional(t.File()),
|
28
29
|
});
|
30
|
+
export const CreateChainDto = t.Object({
|
31
|
+
id: t.Numeric(),
|
32
|
+
name: t.String(),
|
33
|
+
icon: t.String({ format: "uri" }),
|
34
|
+
explorerType: t.Enum(ExplorerType),
|
35
|
+
explorerUrl: t.String({ format: "uri" }),
|
36
|
+
});
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { Campaign, CampaignParameters } from "@sdk";
|
2
|
-
import type {
|
2
|
+
import type { ChainSearchDto, CreateChainModel, UpdateChainModel } from "./chain.model";
|
3
3
|
export declare abstract class ChainRepository {
|
4
4
|
#private;
|
5
5
|
/**
|
@@ -45,16 +45,27 @@ export declare abstract class ChainRepository {
|
|
45
45
|
* @param template for chain creation
|
46
46
|
* @returns
|
47
47
|
*/
|
48
|
-
static create(
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
static create(data: CreateChainModel): Promise<{
|
49
|
+
Explorer: {
|
50
|
+
type: import("../../../../database/api/.generated").$Enums.ExplorerType;
|
51
|
+
url: string;
|
52
|
+
id: string;
|
53
|
+
chainId: number;
|
54
|
+
}[];
|
52
55
|
}>;
|
53
56
|
static findMany(): Promise<{
|
54
57
|
name: string;
|
55
58
|
id: number;
|
56
59
|
icon: string;
|
57
60
|
}[]>;
|
61
|
+
static findUniqueOrThrow(id: number): Promise<{
|
62
|
+
Explorer: {
|
63
|
+
type: import("../../../../database/api/.generated").$Enums.ExplorerType;
|
64
|
+
url: string;
|
65
|
+
id: string;
|
66
|
+
chainId: number;
|
67
|
+
}[];
|
68
|
+
}>;
|
58
69
|
/**
|
59
70
|
* Fetches the campaign dynamic data for a v3 campaign onchain
|
60
71
|
* @param chainId
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { campaignsDynamicData } from "../../../libs/campaigns/campaignsDynamicData";
|
2
2
|
import { executeSimple } from "../../../utils/execute";
|
3
3
|
import { apiDbClient } from "../../../utils/prisma";
|
4
|
+
import { ExplorerRepository } from "../explorer/explorer.repository";
|
4
5
|
export class ChainRepository {
|
5
6
|
/**
|
6
7
|
* @param id ChainId
|
@@ -44,12 +45,30 @@ export class ChainRepository {
|
|
44
45
|
* @param template for chain creation
|
45
46
|
* @returns
|
46
47
|
*/
|
47
|
-
static async create(
|
48
|
-
|
48
|
+
static async create(data) {
|
49
|
+
await apiDbClient.chain.create({
|
50
|
+
data: {
|
51
|
+
id: data.id,
|
52
|
+
icon: data.icon,
|
53
|
+
name: data.name,
|
54
|
+
},
|
55
|
+
});
|
56
|
+
await ExplorerRepository.create(data.id, data.explorerType, data.explorerUrl);
|
57
|
+
return await ChainRepository.findUniqueOrThrow(data.id);
|
49
58
|
}
|
50
59
|
static async findMany() {
|
51
60
|
return apiDbClient.chain.findMany();
|
52
61
|
}
|
62
|
+
static async findUniqueOrThrow(id) {
|
63
|
+
return apiDbClient.chain.findUniqueOrThrow({
|
64
|
+
where: {
|
65
|
+
id,
|
66
|
+
},
|
67
|
+
select: {
|
68
|
+
Explorer: true,
|
69
|
+
},
|
70
|
+
});
|
71
|
+
}
|
53
72
|
/**
|
54
73
|
* Fetches the campaign dynamic data for a v3 campaign onchain
|
55
74
|
* @param chainId
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { ChainSearchDto, UpdateChainModel } from "./chain.model";
|
1
|
+
import type { ChainSearchDto, CreateChainModel, UpdateChainModel } from "./chain.model";
|
2
2
|
export declare abstract class ChainService {
|
3
3
|
static get(chainId: number): Promise<({
|
4
4
|
Explorer: {
|
@@ -39,6 +39,14 @@ export declare abstract class ChainService {
|
|
39
39
|
* @returns an array of chainId
|
40
40
|
*/
|
41
41
|
static getSupportedIds(): Promise<number[]>;
|
42
|
+
static create(chain: CreateChainModel): Promise<{
|
43
|
+
Explorer: {
|
44
|
+
type: import("../../../../database/api/.generated").$Enums.ExplorerType;
|
45
|
+
url: string;
|
46
|
+
id: string;
|
47
|
+
chainId: number;
|
48
|
+
}[];
|
49
|
+
}>;
|
42
50
|
static update(id: number, data: UpdateChainModel): Promise<{
|
43
51
|
name: string;
|
44
52
|
id: number;
|
@@ -39,11 +39,10 @@ export class ChainService {
|
|
39
39
|
}
|
40
40
|
return supportedIds;
|
41
41
|
}
|
42
|
+
static async create(chain) {
|
43
|
+
return await ChainRepository.create(chain);
|
44
|
+
}
|
42
45
|
static async update(id, data) {
|
43
|
-
// let iconUri = data.icon;
|
44
|
-
// if (data.iconFile) {
|
45
|
-
// iconUri = await BucketService.upload("merkl-assets", `/chains/${id}`, data.iconFile.stream(), true);
|
46
|
-
// }
|
47
46
|
return await ChainRepository.update(id, data);
|
48
47
|
}
|
49
48
|
}
|
@@ -25,14 +25,46 @@ export declare abstract class ProtocolRepository {
|
|
25
25
|
tags: string[];
|
26
26
|
icon: string;
|
27
27
|
}[]>;
|
28
|
-
static findMany(query: GetProtocolsQueryModel): Promise<{
|
28
|
+
static findMany(query: GetProtocolsQueryModel): Promise<({
|
29
|
+
MainOpportunities: ({
|
30
|
+
Campaigns: {
|
31
|
+
type: import("../../../../database/api/.generated").$Enums.CampaignType;
|
32
|
+
id: string;
|
33
|
+
params: import("database/api/.generated/runtime/library").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
|
+
} & {
|
46
|
+
name: string;
|
47
|
+
type: import("../../../../database/api/.generated").$Enums.CampaignType;
|
48
|
+
id: string;
|
49
|
+
status: import("../../../../database/api/.generated").$Enums.Status;
|
50
|
+
tags: string[];
|
51
|
+
identifier: string;
|
52
|
+
chainId: number;
|
53
|
+
action: import("../../../../database/api/.generated").$Enums.OpportunityAction;
|
54
|
+
depositUrl: string | null;
|
55
|
+
mainProtocolId: string | null;
|
56
|
+
tvl: number;
|
57
|
+
apr: number;
|
58
|
+
dailyRewards: number;
|
59
|
+
})[];
|
60
|
+
} & {
|
29
61
|
name: string;
|
30
62
|
url: string;
|
31
63
|
description: string;
|
32
64
|
id: string;
|
33
65
|
tags: string[];
|
34
66
|
icon: string;
|
35
|
-
}[]>;
|
67
|
+
})[]>;
|
36
68
|
static countMany(query: GetProtocolsQueryModel): Promise<number>;
|
37
69
|
static update(id: string, data: UpdateProtocolModel): Promise<{
|
38
70
|
name: string;
|