@merkl/api 0.21.32 → 0.21.33
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 +80 -0
- package/dist/src/engine/deprecated/erc20SubTypeProcessors/helpers/factoryFinder.js +1 -1
- package/dist/src/engine/deprecated/erc20SubTypeProcessors/implementations/curveProcessor.js +6 -2
- package/dist/src/engine/deprecated/erc20SubTypeProcessors/implementations/processorMapping.js +1 -0
- package/dist/src/engine/deprecated/erc20SubTypeProcessors/tokenTypeToProtocolAndAction.js +4 -0
- package/dist/src/engine/implementations/Erc20/subTypes/implementations/euler/tvl.js +5 -1
- package/dist/src/engine/implementations/Erc20/subTypes/index.d.ts +2 -1
- package/dist/src/engine/implementations/Erc20/subTypes/index.js +1 -0
- package/dist/src/guards/BackOffice.guard.d.ts +1 -1
- package/dist/src/guards/BackOffice.guard.js +1 -1
- package/dist/src/guards/Engine.guard.d.ts +1 -1
- package/dist/src/guards/Engine.guard.js +2 -3
- package/dist/src/guards/VerifyJwt.guard.d.ts +10 -0
- package/dist/src/guards/VerifyJwt.guard.js +13 -0
- package/dist/src/index.d.ts +32 -0
- package/dist/src/index.js +2 -0
- package/dist/src/modules/v4/accounting/accounting.controller.js +7 -13
- package/dist/src/modules/v4/authentication/authentication.controller.d.ts +52 -0
- package/dist/src/modules/v4/authentication/authentication.controller.js +23 -0
- package/dist/src/modules/v4/authentication/authentication.model.d.ts +10 -0
- package/dist/src/modules/v4/authentication/authentication.model.js +6 -0
- package/dist/src/modules/v4/authentication/authentication.service.d.ts +10 -0
- package/dist/src/modules/v4/authentication/authentication.service.js +31 -0
- package/dist/src/modules/v4/blacklist/blacklist.controller.js +2 -2
- package/dist/src/modules/v4/cache/cache.repository.d.ts +3 -0
- package/dist/src/modules/v4/cache/cache.repository.js +9 -0
- package/dist/src/modules/v4/cache/cache.service.d.ts +3 -0
- package/dist/src/modules/v4/cache/cache.service.js +9 -0
- package/dist/src/modules/v4/opportunity/opportunity.controller.js +1 -3
- package/dist/src/modules/v4/reward/reward.controller.js +13 -4
- package/dist/src/modules/v4/router.d.ts +32 -0
- package/dist/src/modules/v4/router.js +3 -1
- package/dist/src/modules/v4/token/token.controller.js +1 -1
- package/dist/src/modules/v4/uniswap/uniswap.controller.js +1 -1
- package/dist/src/utils/decodeCalls.js +15 -5
- package/dist/src/utils/generateCardName.js +1 -0
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +4 -3
package/dist/src/eden/index.d.ts
CHANGED
@@ -4951,6 +4951,30 @@ declare const eden: {
|
|
4951
4951
|
};
|
4952
4952
|
};
|
4953
4953
|
};
|
4954
|
+
authlogin: {
|
4955
|
+
post: (body: {
|
4956
|
+
message: string;
|
4957
|
+
address: string;
|
4958
|
+
signature: string;
|
4959
|
+
}, options?: {
|
4960
|
+
headers?: Record<string, unknown> | undefined;
|
4961
|
+
query?: Record<string, unknown> | undefined;
|
4962
|
+
fetch?: RequestInit | undefined;
|
4963
|
+
} | undefined) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
4964
|
+
200: void;
|
4965
|
+
}>>;
|
4966
|
+
};
|
4967
|
+
auth: {
|
4968
|
+
revoke: {
|
4969
|
+
delete: (body?: unknown, options?: {
|
4970
|
+
headers?: Record<string, unknown> | undefined;
|
4971
|
+
query?: Record<string, unknown> | undefined;
|
4972
|
+
fetch?: RequestInit | undefined;
|
4973
|
+
} | undefined) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
4974
|
+
200: void;
|
4975
|
+
}>>;
|
4976
|
+
};
|
4977
|
+
};
|
4954
4978
|
};
|
4955
4979
|
v3: {
|
4956
4980
|
app: {
|
@@ -11623,6 +11647,38 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
|
|
11623
11647
|
};
|
11624
11648
|
};
|
11625
11649
|
};
|
11650
|
+
} & {
|
11651
|
+
v4: {
|
11652
|
+
authlogin: {
|
11653
|
+
post: {
|
11654
|
+
body: {
|
11655
|
+
message: string;
|
11656
|
+
address: string;
|
11657
|
+
signature: string;
|
11658
|
+
};
|
11659
|
+
params: {};
|
11660
|
+
query: unknown;
|
11661
|
+
headers: unknown;
|
11662
|
+
response: {
|
11663
|
+
200: void;
|
11664
|
+
};
|
11665
|
+
};
|
11666
|
+
};
|
11667
|
+
} & {
|
11668
|
+
auth: {
|
11669
|
+
revoke: {
|
11670
|
+
delete: {
|
11671
|
+
body: unknown;
|
11672
|
+
params: {};
|
11673
|
+
query: unknown;
|
11674
|
+
headers: unknown;
|
11675
|
+
response: {
|
11676
|
+
200: void;
|
11677
|
+
};
|
11678
|
+
};
|
11679
|
+
};
|
11680
|
+
};
|
11681
|
+
};
|
11626
11682
|
} & {
|
11627
11683
|
v3: {
|
11628
11684
|
app: {
|
@@ -17387,6 +17443,30 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
|
|
17387
17443
|
};
|
17388
17444
|
};
|
17389
17445
|
};
|
17446
|
+
authlogin: {
|
17447
|
+
post: (body: {
|
17448
|
+
message: string;
|
17449
|
+
address: string;
|
17450
|
+
signature: string;
|
17451
|
+
}, options?: {
|
17452
|
+
headers?: Record<string, unknown> | undefined;
|
17453
|
+
query?: Record<string, unknown> | undefined;
|
17454
|
+
fetch?: RequestInit | undefined;
|
17455
|
+
} | undefined) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
17456
|
+
200: void;
|
17457
|
+
}>>;
|
17458
|
+
};
|
17459
|
+
auth: {
|
17460
|
+
revoke: {
|
17461
|
+
delete: (body?: unknown, options?: {
|
17462
|
+
headers?: Record<string, unknown> | undefined;
|
17463
|
+
query?: Record<string, unknown> | undefined;
|
17464
|
+
fetch?: RequestInit | undefined;
|
17465
|
+
} | undefined) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
|
17466
|
+
200: void;
|
17467
|
+
}>>;
|
17468
|
+
};
|
17469
|
+
};
|
17390
17470
|
};
|
17391
17471
|
v3: {
|
17392
17472
|
app: {
|
@@ -26,7 +26,7 @@ const factoryAddresses = {
|
|
26
26
|
"0x5Ea9DD3b6f042A34Df818C6c1324BC5A7c61427a": Erc20SubType.curve, // Curve Corn 2crypto Factory
|
27
27
|
"0xd7E72f3615aa65b92A4DBdC211E296a35512988B": Erc20SubType.curveNPool, // Curve Corn Stableswap Factory
|
28
28
|
"0x7Ca46A636b02D4aBC66883D7FF164bDE506DC66a": Erc20SubType.curveNPool, // Curve Corn 3crypto Factory
|
29
|
-
"0xbC0797015fcFc47d9C1856639CaE50D0e69FbEE8": Erc20SubType.
|
29
|
+
"0xbC0797015fcFc47d9C1856639CaE50D0e69FbEE8": Erc20SubType.curveTricrypto, // Curve Arbitrum 3crypto Factory
|
30
30
|
"0xA5961898870943c68037F6848d2D866Ed2016bcB": Erc20SubType.curveNPool, // Curve Base
|
31
31
|
"0xB255D6A720BB7c39fee173cE22113397119cB930": Erc20SubType.katana, // Katana Ronin
|
32
32
|
"0x29372c22459a4e373851798bFd6808e71EA34A71": Erc20SubType.punchswap, // Punchswap Flow EVM
|
@@ -2,6 +2,7 @@ import { Erc20SubType } from "@/engine/implementations/Erc20/subTypes";
|
|
2
2
|
import { generateCardName } from "@/utils/generateCardName";
|
3
3
|
import { BN2Number } from "@sdk";
|
4
4
|
import { GenericProcessor } from "../GenericProcessor";
|
5
|
+
import { getCrossCurveTokenPrice } from "../helpers/getCrossCurveTokenPrice";
|
5
6
|
export class CurveProcessor extends GenericProcessor {
|
6
7
|
rounds = {
|
7
8
|
round1: [
|
@@ -50,8 +51,11 @@ export class CurveProcessor extends GenericProcessor {
|
|
50
51
|
}
|
51
52
|
if (type === Erc20SubType.curve_2) {
|
52
53
|
const prices = [];
|
53
|
-
for (const symbol of Object.
|
54
|
-
|
54
|
+
for (const [address, symbol] of Object.entries(typeInfo.poolTokens)) {
|
55
|
+
let price = (await pricer.get({ symbol: symbol })) ?? 0;
|
56
|
+
if (price === 0) {
|
57
|
+
price = await getCrossCurveTokenPrice(address);
|
58
|
+
}
|
55
59
|
prices.push(price);
|
56
60
|
}
|
57
61
|
let minPrice = Math.min(...prices);
|
@@ -128,6 +128,10 @@ export const tokenTypeToProtocolAndAction = {
|
|
128
128
|
protocol: "curve",
|
129
129
|
action: OpportunityAction.POOL,
|
130
130
|
},
|
131
|
+
[Erc20SubType.curveTricrypto]: {
|
132
|
+
protocol: "curve",
|
133
|
+
action: OpportunityAction.POOL,
|
134
|
+
},
|
131
135
|
[Erc20SubType.sake_borrowing]: {
|
132
136
|
protocol: "sake",
|
133
137
|
action: OpportunityAction.BORROW,
|
@@ -37,7 +37,11 @@ export class EulerTVLBuilder {
|
|
37
37
|
if (!underlyingToken.decimals || !underlyingToken.price) {
|
38
38
|
throw new Error(`Missing decimals or price for token ${underlylingTokenAddress}`);
|
39
39
|
}
|
40
|
-
|
40
|
+
let tvl = bigIntToNumber(totalAssets, underlyingToken.decimals) * underlyingToken.price;
|
41
|
+
// If TVL is zero, set to 0.01
|
42
|
+
if (tvl === 0) {
|
43
|
+
tvl = 0.01;
|
44
|
+
}
|
41
45
|
tvls.push({
|
42
46
|
campaign,
|
43
47
|
tvl,
|
@@ -113,4 +113,5 @@ export var Erc20SubType;
|
|
113
113
|
Erc20SubType["lendle_vaults"] = "lendle_vaults";
|
114
114
|
Erc20SubType["termmax"] = "termmax";
|
115
115
|
Erc20SubType["hyperswap"] = "hyperswap";
|
116
|
+
Erc20SubType["curveTricrypto"] = "curveTricrypto";
|
116
117
|
})(Erc20SubType || (Erc20SubType = {}));
|
@@ -4,4 +4,4 @@ export declare const AuthorizationHeadersDto: import("@sinclair/typebox").TObjec
|
|
4
4
|
export type AuthorizationHeaders = typeof AuthorizationHeadersDto.static;
|
5
5
|
export declare function BackOfficeGuard({ headers }: {
|
6
6
|
headers: AuthorizationHeaders;
|
7
|
-
}):
|
7
|
+
}): void;
|
@@ -3,7 +3,7 @@ import { t } from "elysia";
|
|
3
3
|
export const AuthorizationHeadersDto = t.Object({
|
4
4
|
authorization: t.String(),
|
5
5
|
});
|
6
|
-
export
|
6
|
+
export function BackOfficeGuard({ headers }) {
|
7
7
|
if (headers.authorization !== `Bearer ${process.env.BACKOFFICE_SECRET}`) {
|
8
8
|
throw new UnauthorizedError();
|
9
9
|
}
|
@@ -4,4 +4,4 @@ export declare const AuthorizationHeadersDto: import("@sinclair/typebox").TObjec
|
|
4
4
|
export type AuthorizationHeaders = typeof AuthorizationHeadersDto.static;
|
5
5
|
export declare function EngineGuard({ headers }: {
|
6
6
|
headers: AuthorizationHeaders;
|
7
|
-
}):
|
7
|
+
}): void;
|
@@ -3,8 +3,7 @@ import { t } from "elysia";
|
|
3
3
|
export const AuthorizationHeadersDto = t.Object({
|
4
4
|
authorization: t.String(),
|
5
5
|
});
|
6
|
-
export
|
7
|
-
if (headers.authorization !== `Bearer ${process.env.ENGINE_SECRET}`)
|
6
|
+
export function EngineGuard({ headers }) {
|
7
|
+
if (headers.authorization !== `Bearer ${process.env.ENGINE_SECRET}`)
|
8
8
|
throw new UnauthorizedError();
|
9
|
-
}
|
10
9
|
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { UnauthorizedError } from "@/errors";
|
2
|
+
import { CacheService } from "@/modules/v4/cache/cache.service";
|
3
|
+
import { t } from "elysia";
|
4
|
+
import { jwtVerify } from "jose";
|
5
|
+
export const JwtCookieDto = t.Cookie({
|
6
|
+
jwt: t.String(),
|
7
|
+
});
|
8
|
+
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET);
|
9
|
+
export async function VerifyJwtGuard({ cookie }) {
|
10
|
+
const decodedJwt = await jwtVerify(cookie.jwt.value, JWT_SECRET);
|
11
|
+
if (!decodedJwt.payload.jti || (await CacheService.sIsMember("token:revoked", decodedJwt.payload.jti)))
|
12
|
+
throw new UnauthorizedError("Failed to verify JWT.");
|
13
|
+
}
|
package/dist/src/index.d.ts
CHANGED
@@ -6046,6 +6046,38 @@ declare const app: Elysia<"", false, {
|
|
6046
6046
|
};
|
6047
6047
|
};
|
6048
6048
|
};
|
6049
|
+
} & {
|
6050
|
+
v4: {
|
6051
|
+
authlogin: {
|
6052
|
+
post: {
|
6053
|
+
body: {
|
6054
|
+
message: string;
|
6055
|
+
address: string;
|
6056
|
+
signature: string;
|
6057
|
+
};
|
6058
|
+
params: {};
|
6059
|
+
query: unknown;
|
6060
|
+
headers: unknown;
|
6061
|
+
response: {
|
6062
|
+
200: void;
|
6063
|
+
};
|
6064
|
+
};
|
6065
|
+
};
|
6066
|
+
} & {
|
6067
|
+
auth: {
|
6068
|
+
revoke: {
|
6069
|
+
delete: {
|
6070
|
+
body: unknown;
|
6071
|
+
params: {};
|
6072
|
+
query: unknown;
|
6073
|
+
headers: unknown;
|
6074
|
+
response: {
|
6075
|
+
200: void;
|
6076
|
+
};
|
6077
|
+
};
|
6078
|
+
};
|
6079
|
+
};
|
6080
|
+
};
|
6049
6081
|
} & {
|
6050
6082
|
v3: {
|
6051
6083
|
app: {
|
package/dist/src/index.js
CHANGED
@@ -10,6 +10,8 @@ import { v2 } from "./routes/v2/router";
|
|
10
10
|
import { v3 } from "./routes/v3/router";
|
11
11
|
import { errorHandler } from "./utils/error";
|
12
12
|
import { log } from "./utils/logger";
|
13
|
+
if (!process.env.JWT_SECRET)
|
14
|
+
throw new Error("Environment variable JWT_SECRET is missing.");
|
13
15
|
// Axios with bun workaround
|
14
16
|
axios.defaults.headers.common["Accept-Encoding"] = "gzip";
|
15
17
|
const PORT = process.env.PORT || 3000;
|
@@ -7,30 +7,24 @@ export const AccountingController = new Elysia({ prefix: "/accounting", detail:
|
|
7
7
|
.get("/", async ({ query }) => await AccountingService.findMany(query), {
|
8
8
|
query: GetTransactionsQueryModel,
|
9
9
|
headers: AuthorizationHeadersDto,
|
10
|
-
beforeHandle:
|
11
|
-
await BackOfficeGuard({ headers });
|
12
|
-
},
|
10
|
+
beforeHandle: BackOfficeGuard,
|
13
11
|
})
|
14
12
|
.group("/revenues", app => {
|
15
13
|
return app
|
16
14
|
.get("", async () => await AccountingService.getAllRevenueBreakdownByChain(), {
|
17
15
|
headers: AuthorizationHeadersDto,
|
18
|
-
beforeHandle:
|
19
|
-
await BackOfficeGuard({ headers });
|
20
|
-
},
|
16
|
+
beforeHandle: BackOfficeGuard,
|
21
17
|
})
|
22
18
|
.get("/per-month/:year/:month", async ({ params }) => await AccountingService.getMonthlyRevenue(params.year, params.month), {
|
23
19
|
params: DateDto,
|
24
20
|
headers: AuthorizationHeadersDto,
|
25
|
-
beforeHandle:
|
26
|
-
await BackOfficeGuard({ headers });
|
27
|
-
},
|
21
|
+
beforeHandle: BackOfficeGuard,
|
28
22
|
})
|
29
23
|
.get("/chains/:chainId", async ({ params }) => await AccountingService.getRevenueByChain(params.chainId), {
|
30
24
|
params: ChainDto,
|
31
25
|
headers: AuthorizationHeadersDto,
|
32
26
|
beforeHandle: async ({ params, headers }) => {
|
33
|
-
|
27
|
+
BackOfficeGuard({ headers });
|
34
28
|
throwOnUnsupportedChainId(params.chainId);
|
35
29
|
},
|
36
30
|
})
|
@@ -38,7 +32,7 @@ export const AccountingController = new Elysia({ prefix: "/accounting", detail:
|
|
38
32
|
params: RevenuesDto,
|
39
33
|
headers: AuthorizationHeadersDto,
|
40
34
|
beforeHandle: async ({ params, headers }) => {
|
41
|
-
|
35
|
+
BackOfficeGuard({ headers });
|
42
36
|
throwOnUnsupportedChainId(params.chainId);
|
43
37
|
},
|
44
38
|
});
|
@@ -50,7 +44,7 @@ export const AccountingController = new Elysia({ prefix: "/accounting", detail:
|
|
50
44
|
params: TokensDto,
|
51
45
|
headers: AuthorizationHeadersDto,
|
52
46
|
beforeHandle: async ({ params, headers }) => {
|
53
|
-
|
47
|
+
BackOfficeGuard({ headers });
|
54
48
|
params.tokenAddress = throwOnInvalidRequiredAddress(params.tokenAddress);
|
55
49
|
throwOnUnsupportedChainId(params.chainId);
|
56
50
|
},
|
@@ -59,7 +53,7 @@ export const AccountingController = new Elysia({ prefix: "/accounting", detail:
|
|
59
53
|
params: TokensDateDto,
|
60
54
|
headers: AuthorizationHeadersDto,
|
61
55
|
beforeHandle: async ({ params, headers }) => {
|
62
|
-
|
56
|
+
BackOfficeGuard({ headers });
|
63
57
|
params.tokenAddress = throwOnInvalidRequiredAddress(params.tokenAddress);
|
64
58
|
throwOnUnsupportedChainId(params.chainId);
|
65
59
|
},
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import Elysia from "elysia";
|
2
|
+
export declare const AuthController: Elysia<"/auth", false, {
|
3
|
+
decorator: {};
|
4
|
+
store: {};
|
5
|
+
derive: {};
|
6
|
+
resolve: {};
|
7
|
+
}, {
|
8
|
+
type: {};
|
9
|
+
error: {};
|
10
|
+
}, {
|
11
|
+
schema: {};
|
12
|
+
macro: {};
|
13
|
+
macroFn: {};
|
14
|
+
}, {
|
15
|
+
authlogin: {
|
16
|
+
post: {
|
17
|
+
body: {
|
18
|
+
message: string;
|
19
|
+
address: string;
|
20
|
+
signature: string;
|
21
|
+
};
|
22
|
+
params: {};
|
23
|
+
query: unknown;
|
24
|
+
headers: unknown;
|
25
|
+
response: {
|
26
|
+
200: void;
|
27
|
+
};
|
28
|
+
};
|
29
|
+
};
|
30
|
+
} & {
|
31
|
+
auth: {
|
32
|
+
revoke: {
|
33
|
+
delete: {
|
34
|
+
body: unknown;
|
35
|
+
params: {};
|
36
|
+
query: unknown;
|
37
|
+
headers: unknown;
|
38
|
+
response: {
|
39
|
+
200: void;
|
40
|
+
};
|
41
|
+
};
|
42
|
+
};
|
43
|
+
};
|
44
|
+
}, {
|
45
|
+
derive: {};
|
46
|
+
resolve: {};
|
47
|
+
schema: {};
|
48
|
+
}, {
|
49
|
+
derive: {};
|
50
|
+
resolve: {};
|
51
|
+
schema: {};
|
52
|
+
}>;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { JwtCookieDto, VerifyJwtGuard } from "@/guards/VerifyJwt.guard";
|
2
|
+
import Elysia from "elysia";
|
3
|
+
import { LoginDto } from "./authentication.model";
|
4
|
+
import { AuthService } from "./authentication.service";
|
5
|
+
const authService = new AuthService();
|
6
|
+
export const AuthController = new Elysia({
|
7
|
+
prefix: "/auth",
|
8
|
+
detail: { tags: ["Authentication"] },
|
9
|
+
})
|
10
|
+
.post("login", async ({ body, cookie: { jwt } }) => {
|
11
|
+
jwt.value = await authService.login(body);
|
12
|
+
jwt.sameSite = true;
|
13
|
+
jwt.path = "/";
|
14
|
+
jwt.httpOnly = true;
|
15
|
+
jwt.secure = true;
|
16
|
+
jwt.maxAge = 2 * 60 * 60; // 2h
|
17
|
+
return;
|
18
|
+
}, { body: LoginDto })
|
19
|
+
.delete("/revoke", async ({ cookie: { jwt } }) => {
|
20
|
+
await authService.revokeToken(jwt.value);
|
21
|
+
jwt.remove();
|
22
|
+
return;
|
23
|
+
}, { beforeHandle: VerifyJwtGuard, cookie: JwtCookieDto });
|
@@ -0,0 +1,10 @@
|
|
1
|
+
export declare const LoginDto: import("@sinclair/typebox").TObject<{
|
2
|
+
address: import("@sinclair/typebox").TString;
|
3
|
+
message: import("@sinclair/typebox").TString;
|
4
|
+
signature: import("@sinclair/typebox").TString;
|
5
|
+
}>;
|
6
|
+
export type MessageModel = {
|
7
|
+
address: `0x${string}`;
|
8
|
+
message: string;
|
9
|
+
signature: `0x${string}`;
|
10
|
+
};
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import type { MessageModel } from "./authentication.model";
|
2
|
+
export declare class AuthService {
|
3
|
+
#private;
|
4
|
+
login(message: MessageModel): Promise<string>;
|
5
|
+
generateJwt(user: {
|
6
|
+
address: string;
|
7
|
+
creatorId: string | null;
|
8
|
+
}): Promise<string>;
|
9
|
+
revokeToken(jwt: string): Promise<void>;
|
10
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { UnauthorizedError } from "@/errors";
|
2
|
+
import { SignJWT, decodeJwt } from "jose";
|
3
|
+
import { http, createPublicClient } from "viem";
|
4
|
+
import { mainnet } from "viem/chains";
|
5
|
+
import { CacheService } from "../cache/cache.service";
|
6
|
+
import { UserService } from "../user/user.service";
|
7
|
+
export class AuthService {
|
8
|
+
#publicClient = createPublicClient({ chain: mainnet, transport: http() });
|
9
|
+
#encodedJwtSecret = new TextEncoder().encode(process.env.JWT_SECRET);
|
10
|
+
async login(message) {
|
11
|
+
if (!this.#publicClient.verifyMessage(message))
|
12
|
+
throw new UnauthorizedError("Failed to verify the message signature.");
|
13
|
+
let user = await UserService.findUnique(message.address);
|
14
|
+
if (!user)
|
15
|
+
user = await UserService.create({ address: message.address, tags: [] });
|
16
|
+
return await this.generateJwt(user);
|
17
|
+
}
|
18
|
+
async generateJwt(user) {
|
19
|
+
return await new SignJWT({ creator: user.creatorId })
|
20
|
+
.setProtectedHeader({ alg: "HS256" })
|
21
|
+
.setExpirationTime("2h")
|
22
|
+
.setAudience(user.address)
|
23
|
+
.setIssuer("merkl")
|
24
|
+
.setSubject("studio")
|
25
|
+
.setJti(Bun.randomUUIDv7())
|
26
|
+
.sign(this.#encodedJwtSecret);
|
27
|
+
}
|
28
|
+
async revokeToken(jwt) {
|
29
|
+
return await CacheService.sAdd("token:revoked", decodeJwt(jwt).jti);
|
30
|
+
}
|
31
|
+
}
|
@@ -23,7 +23,7 @@ export const BlacklistController = new Elysia({ prefix: "/blacklists", detail: {
|
|
23
23
|
body: AddBlacklistDto,
|
24
24
|
headers: AuthorizationHeadersDto,
|
25
25
|
beforeHandle: async ({ body, headers }) => {
|
26
|
-
|
26
|
+
BackOfficeGuard({ headers });
|
27
27
|
body.userAddress = throwOnInvalidRequiredAddress(body.userAddress);
|
28
28
|
body.poolAddress = throwOnInvalidRequiredAddress(body.poolAddress ?? NULL_ADDRESS);
|
29
29
|
throwOnUnsupportedChainId(body.chainId);
|
@@ -35,7 +35,7 @@ export const BlacklistController = new Elysia({ prefix: "/blacklists", detail: {
|
|
35
35
|
params: RemoveBlacklistDto,
|
36
36
|
headers: AuthorizationHeadersDto,
|
37
37
|
beforeHandle: async ({ headers, params }) => {
|
38
|
-
|
38
|
+
BackOfficeGuard({ headers });
|
39
39
|
params.address = throwOnInvalidRequiredAddress(params.address);
|
40
40
|
},
|
41
41
|
detail: { hide: true },
|
@@ -2,4 +2,7 @@ export declare abstract class CacheRepository {
|
|
2
2
|
static set(ttl: number, key: string, value: string): Promise<void>;
|
3
3
|
static ttl(key: string): Promise<number>;
|
4
4
|
static get(key: string): Promise<string | null>;
|
5
|
+
static sAdd(sName: string, sValue: string): Promise<void>;
|
6
|
+
static sRem(sName: string, sValue: string): Promise<void>;
|
7
|
+
static sIsMember(sName: string, sValue: string): Promise<any>;
|
5
8
|
}
|
@@ -10,4 +10,13 @@ export class CacheRepository {
|
|
10
10
|
static async get(key) {
|
11
11
|
return await withRetry(withTimeout, [redisClient.get(key), REDIS_READ_TIMEOUT], REDIS_RETRIES);
|
12
12
|
}
|
13
|
+
static async sAdd(sName, sValue) {
|
14
|
+
await withRetry(withTimeout, [redisClient.sAdd(sName, sValue), REDIS_WRITE_TIMEOUT], REDIS_RETRIES);
|
15
|
+
}
|
16
|
+
static async sRem(sName, sValue) {
|
17
|
+
await withRetry(withTimeout, [redisClient.sRem(sName, sValue), REDIS_WRITE_TIMEOUT], REDIS_RETRIES);
|
18
|
+
}
|
19
|
+
static async sIsMember(sName, sValue) {
|
20
|
+
return await withRetry(withTimeout, [redisClient.sIsMember(sName, sValue), REDIS_READ_TIMEOUT], REDIS_RETRIES);
|
21
|
+
}
|
13
22
|
}
|
@@ -6,4 +6,7 @@ export declare abstract class CacheService {
|
|
6
6
|
static keyAndTTL<Fn extends (...args: Args) => Promise<unknown>, Args extends unknown[]>(fn: Fn, ...args: Args): Promise<[string, number]>;
|
7
7
|
static set<Fn extends (...args: Args) => Promise<unknown>, Args extends unknown[], T extends Awaited<ReturnType<Fn>>>(ttl: number, fn: Fn, ...args: Args): Promise<T>;
|
8
8
|
static get<Fn extends (...args: Args) => Promise<unknown>, Args extends unknown[], T extends Awaited<ReturnType<Fn>>>(fn: Fn, args: Args): Promise<T | null>;
|
9
|
+
static sAdd(sName: string, sValue: string): Promise<void>;
|
10
|
+
static sRem(sName: string, sValue: string): Promise<void>;
|
11
|
+
static sIsMember(sName: string, sValue: string): Promise<any>;
|
9
12
|
}
|
@@ -47,4 +47,13 @@ export class CacheService {
|
|
47
47
|
return null;
|
48
48
|
return CacheService.#uncompress(cached);
|
49
49
|
}
|
50
|
+
static async sAdd(sName, sValue) {
|
51
|
+
return CacheRepository.sAdd(sName, sValue);
|
52
|
+
}
|
53
|
+
static async sRem(sName, sValue) {
|
54
|
+
return CacheRepository.sRem(sName, sValue);
|
55
|
+
}
|
56
|
+
static async sIsMember(sName, sValue) {
|
57
|
+
return await CacheRepository.sIsMember(sName, sValue);
|
58
|
+
}
|
50
59
|
}
|
@@ -13,9 +13,7 @@ import { validateId } from "./validate-id.pipe";
|
|
13
13
|
// ─── Opportunities Controller ────────────────────────────────────────────────
|
14
14
|
export const OpportunityController = new Elysia({
|
15
15
|
prefix: "/opportunities",
|
16
|
-
detail: {
|
17
|
-
tags: ["Opportunities"],
|
18
|
-
},
|
16
|
+
detail: { tags: ["Opportunities"] },
|
19
17
|
})
|
20
18
|
// ─── Get All Opportunities ───────────────────────────────────────────
|
21
19
|
.get("/", async ({ query }) => OpportunityService.findMany(query), {
|
@@ -68,11 +68,20 @@ export const RewardController = new Elysia({ prefix: "/rewards", detail: { tags:
|
|
68
68
|
query.campaignIds = query?.campaignIds?.flatMap(x => x.split(","));
|
69
69
|
throwOnUnsupportedChainId(query.chainId);
|
70
70
|
},
|
71
|
-
detail: { hide: true },
|
72
71
|
})
|
73
|
-
.get("/", async ({ query }) => await RewardService.breakdownForToken(query)
|
74
|
-
|
75
|
-
|
72
|
+
.get("/", async ({ query }) => await RewardService.breakdownForToken(query), {
|
73
|
+
detail: {
|
74
|
+
description: "Returns the all the address that received rewards of a given token, sorted by descending amounts",
|
75
|
+
},
|
76
|
+
})
|
77
|
+
.get("/total", async ({ query }) => await RewardService.totalForToken(query), {
|
78
|
+
detail: { description: "Returns the total amount distributed for a given token" },
|
79
|
+
})
|
80
|
+
.get("/count", async ({ query }) => await RewardService.countForToken(query), {
|
81
|
+
detail: {
|
82
|
+
description: "Returns the number of recipients that earned rewards in a given token",
|
83
|
+
},
|
84
|
+
});
|
76
85
|
})
|
77
86
|
.group("/engine", {
|
78
87
|
headers: AuthorizationHeadersDto,
|
@@ -5916,6 +5916,38 @@ export declare const v4: Elysia<"/v4", false, {
|
|
5916
5916
|
};
|
5917
5917
|
};
|
5918
5918
|
};
|
5919
|
+
} & {
|
5920
|
+
v4: {
|
5921
|
+
authlogin: {
|
5922
|
+
post: {
|
5923
|
+
body: {
|
5924
|
+
message: string;
|
5925
|
+
address: string;
|
5926
|
+
signature: string;
|
5927
|
+
};
|
5928
|
+
params: {};
|
5929
|
+
query: unknown;
|
5930
|
+
headers: unknown;
|
5931
|
+
response: {
|
5932
|
+
200: void;
|
5933
|
+
};
|
5934
|
+
};
|
5935
|
+
};
|
5936
|
+
} & {
|
5937
|
+
auth: {
|
5938
|
+
revoke: {
|
5939
|
+
delete: {
|
5940
|
+
body: unknown;
|
5941
|
+
params: {};
|
5942
|
+
query: unknown;
|
5943
|
+
headers: unknown;
|
5944
|
+
response: {
|
5945
|
+
200: void;
|
5946
|
+
};
|
5947
|
+
};
|
5948
|
+
};
|
5949
|
+
};
|
5950
|
+
};
|
5919
5951
|
}, {
|
5920
5952
|
derive: {};
|
5921
5953
|
resolve: {};
|
@@ -30,6 +30,7 @@ import { RedisInstrumentation } from "@opentelemetry/instrumentation-redis-4";
|
|
30
30
|
import { AlwaysOffSampler, BatchSpanProcessor } from "@opentelemetry/sdk-trace-node";
|
31
31
|
import { PrismaInstrumentation } from "@prisma/instrumentation";
|
32
32
|
import Elysia from "elysia";
|
33
|
+
import { AuthController } from "./authentication/authentication.controller";
|
33
34
|
import { ReferralController } from "./referral/referral.controller";
|
34
35
|
import { TurtleController } from "./turtle/turtle.controller";
|
35
36
|
// ─── V4 Router ───────────────────────────────────────────────────────────────
|
@@ -88,4 +89,5 @@ export const v4 = new Elysia({ tags: ["v4"], prefix: "/v4" })
|
|
88
89
|
.use(CreatorController)
|
89
90
|
.use(ReferralController)
|
90
91
|
.use(UniswapController)
|
91
|
-
.use(TurtleController)
|
92
|
+
.use(TurtleController)
|
93
|
+
.use(AuthController);
|