@merkl/api 0.16.28 → 0.16.30

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.
@@ -37,9 +37,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
37
37
  };
38
38
  };
39
39
  };
40
- };
41
- } & {
42
- tokens: {
40
+ } & {
43
41
  ":id": {
44
42
  allowance: {
45
43
  ":owner": {
@@ -73,9 +71,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
73
71
  };
74
72
  };
75
73
  };
76
- };
77
- } & {
78
- tokens: {
74
+ } & {
79
75
  reward: {
80
76
  ":chainId": {
81
77
  get: {
@@ -104,9 +100,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
104
100
  };
105
101
  };
106
102
  };
107
- };
108
- } & {
109
- tokens: {
103
+ } & {
110
104
  balances: {
111
105
  get: {
112
106
  body: unknown;
@@ -138,9 +132,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
138
132
  };
139
133
  };
140
134
  };
141
- };
142
- } & {
143
- tokens: {
135
+ } & {
144
136
  index: {
145
137
  get: {
146
138
  body: unknown;
@@ -172,9 +164,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
172
164
  };
173
165
  };
174
166
  };
175
- };
176
- } & {
177
- tokens: {
167
+ } & {
178
168
  count: {
179
169
  get: {
180
170
  body: unknown;
@@ -194,9 +184,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
194
184
  };
195
185
  };
196
186
  };
197
- };
198
- } & {
199
- tokens: {
187
+ } & {
200
188
  index: {
201
189
  post: {
202
190
  body: {
@@ -227,12 +215,11 @@ export declare const TokenController: Elysia<"/tokens", false, {
227
215
  };
228
216
  };
229
217
  };
230
- };
231
- } & {
232
- tokens: {
218
+ } & {
233
219
  ":id": {
234
220
  patch: {
235
221
  body: {
222
+ name?: string | undefined;
236
223
  icon?: string | undefined;
237
224
  displaySymbol?: string | undefined;
238
225
  isTest?: boolean | undefined;
@@ -261,9 +248,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
261
248
  };
262
249
  };
263
250
  };
264
- };
265
- } & {
266
- tokens: {
251
+ } & {
267
252
  sync: {
268
253
  get: {
269
254
  body: unknown;
@@ -275,6 +260,52 @@ export declare const TokenController: Elysia<"/tokens", false, {
275
260
  };
276
261
  };
277
262
  };
263
+ } & {
264
+ webhooks: {
265
+ notion: {
266
+ post: {
267
+ body: {
268
+ data: {
269
+ properties: {
270
+ Symbol: {
271
+ rich_text: {
272
+ plain_text: string;
273
+ }[];
274
+ };
275
+ Name: {
276
+ rich_text: {
277
+ plain_text: string;
278
+ }[];
279
+ };
280
+ Icon: {
281
+ files: {
282
+ file: {
283
+ url: string;
284
+ };
285
+ }[];
286
+ };
287
+ Address: {
288
+ rich_text: {
289
+ plain_text: string;
290
+ }[];
291
+ };
292
+ "Chain ID": {
293
+ number: number;
294
+ };
295
+ };
296
+ };
297
+ };
298
+ params: {};
299
+ query: unknown;
300
+ headers: {
301
+ authorization: string;
302
+ };
303
+ response: {
304
+ 200: void;
305
+ };
306
+ };
307
+ };
308
+ };
278
309
  };
279
310
  }, {
280
311
  derive: {};
@@ -4,7 +4,7 @@ import { getUserAllowance } from "../../../libs/tokens/allowances";
4
4
  import { throwOnInvalidRequiredAddress, throwOnUnsupportedChainId } from "../../../utils/throw";
5
5
  import Elysia from "elysia";
6
6
  import { ChainDto } from "../accounting";
7
- import { CreateTokenDto, FindUniqueTokenAllowanceDto, FindUniqueTokenDto, GetTokenBalanceDto, GetTokenQueryDto, TokenIdDto, UpdateTokenDto, } from "./token.model";
7
+ import { CreateTokenDto, FindUniqueTokenAllowanceDto, FindUniqueTokenDto, GetTokenBalanceDto, GetTokenQueryDto, NotionWebhookDto, TokenIdDto, UpdateTokenDto, } from "./token.model";
8
8
  import { TokenService } from "./token.service";
9
9
  // ─── Tokens Controller ───────────────────────────────────────────────────────
10
10
  export const TokenController = new Elysia({ prefix: "/tokens", detail: { tags: ["Tokens"], hide: true } })
@@ -67,4 +67,12 @@ export const TokenController = new Elysia({ prefix: "/tokens", detail: { tags: [
67
67
  beforeHandle: BackOfficeGuard,
68
68
  detail: { hide: true },
69
69
  })
70
- .get("/sync", async () => await TokenService.fillTokenAndIconsFromTokenList(), { detail: { hide: true } });
70
+ .get("/sync", async () => await TokenService.fillTokenAndIconsFromTokenList(), { detail: { hide: true } })
71
+ .group("/webhooks", app => {
72
+ return app.post("/notion", async ({ body }) => TokenService.notionWebhook(body), {
73
+ body: NotionWebhookDto,
74
+ headers: AuthorizationHeadersDto,
75
+ beforeHandle: BackOfficeGuard,
76
+ detail: { hide: true },
77
+ });
78
+ });
@@ -58,6 +58,7 @@ export declare const UpdateTokenDto: import("@sinclair/typebox").TObject<{
58
58
  isTest: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
59
59
  icon: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
60
60
  displaySymbol: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
61
+ name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
61
62
  }>;
62
63
  export declare const CreateTokenDto: import("@sinclair/typebox").TObject<{
63
64
  chainId: import("@sinclair/typebox").TNumber;
@@ -66,7 +67,39 @@ export declare const CreateTokenDto: import("@sinclair/typebox").TObject<{
66
67
  verified: import("@sinclair/typebox").TBoolean;
67
68
  isTest: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
68
69
  }>;
70
+ export declare const NotionWebhookDto: import("@sinclair/typebox").TObject<{
71
+ data: import("@sinclair/typebox").TObject<{
72
+ properties: import("@sinclair/typebox").TObject<{
73
+ Icon: import("@sinclair/typebox").TObject<{
74
+ files: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
75
+ file: import("@sinclair/typebox").TObject<{
76
+ url: import("@sinclair/typebox").TString;
77
+ }>;
78
+ }>>;
79
+ }>;
80
+ Address: import("@sinclair/typebox").TObject<{
81
+ rich_text: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
82
+ plain_text: import("@sinclair/typebox").TString;
83
+ }>>;
84
+ }>;
85
+ "Chain ID": import("@sinclair/typebox").TObject<{
86
+ number: import("@sinclair/typebox").TNumber;
87
+ }>;
88
+ Symbol: import("@sinclair/typebox").TObject<{
89
+ rich_text: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
90
+ plain_text: import("@sinclair/typebox").TString;
91
+ }>>;
92
+ }>;
93
+ Name: import("@sinclair/typebox").TObject<{
94
+ rich_text: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
95
+ plain_text: import("@sinclair/typebox").TString;
96
+ }>>;
97
+ }>;
98
+ }>;
99
+ }>;
100
+ }>;
69
101
  export type TokenModel = typeof TokenDto.static;
70
102
  export type GetTokenQueryModel = typeof GetTokenQueryDto.static;
71
103
  export type UpdateTokenModel = typeof UpdateTokenDto.static;
72
104
  export type CreateTokenModel = typeof CreateTokenDto.static;
105
+ export type NotionWebhookModel = typeof NotionWebhookDto.static;
@@ -46,6 +46,7 @@ export const UpdateTokenDto = t.Object({
46
46
  icon: t.Optional(t.String({ format: "uri" })),
47
47
  // iconFile: t.Optional(t.File()),
48
48
  displaySymbol: t.Optional(t.String()),
49
+ name: t.Optional(t.String()),
49
50
  });
50
51
  export const CreateTokenDto = t.Object({
51
52
  chainId: t.Numeric(),
@@ -54,3 +55,14 @@ export const CreateTokenDto = t.Object({
54
55
  verified: t.Boolean(),
55
56
  isTest: t.Optional(t.Boolean()),
56
57
  });
58
+ export const NotionWebhookDto = t.Object({
59
+ data: t.Object({
60
+ properties: t.Object({
61
+ Icon: t.Object({ files: t.Array(t.Object({ file: t.Object({ url: t.String() }) })) }),
62
+ Address: t.Object({ rich_text: t.Array(t.Object({ plain_text: t.String() })) }),
63
+ "Chain ID": t.Object({ number: t.Numeric() }),
64
+ Symbol: t.Object({ rich_text: t.Array(t.Object({ plain_text: t.String() })) }),
65
+ Name: t.Object({ rich_text: t.Array(t.Object({ plain_text: t.String() })) }),
66
+ }),
67
+ }),
68
+ }, { additionalProperties: true });
@@ -1,5 +1,6 @@
1
1
  import type { Pricer } from "../../../utils/pricer";
2
2
  import { type ChainId } from "@sdk";
3
+ import type { NotionWebhookModel } from "./token.model";
3
4
  import type { CreateTokenModel, GetTokenQueryModel, Token, TokenModel, TokenUnique, UpdateTokenModel } from "./token.model";
4
5
  import { TokenRepository } from "./token.repository";
5
6
  export declare abstract class TokenService {
@@ -227,4 +228,5 @@ export declare abstract class TokenService {
227
228
  isTest: boolean;
228
229
  price: number | null;
229
230
  }>;
231
+ static notionWebhook(body: NotionWebhookModel): Promise<void>;
230
232
  }
@@ -5,6 +5,7 @@ import { apiDbClient } from "../../../utils/prisma";
5
5
  import { Prisma } from "../../../../database/api/.generated";
6
6
  import { DistributionCreatorService, NETWORK_LABELS, bigIntToNumber } from "@sdk";
7
7
  import { getAddress, parseUnits } from "viem";
8
+ import { BucketService } from "../bucket/bucket.service";
8
9
  import { CacheService } from "../cache";
9
10
  import { TTLPresets } from "../cache/cache.model";
10
11
  import { TokenRepository } from "./token.repository";
@@ -295,4 +296,34 @@ export class TokenService {
295
296
  // }
296
297
  return await TokenRepository.update(id, data);
297
298
  }
299
+ static async notionWebhook(body) {
300
+ const bucket = new BucketService(`merkl-${process.env.ENV}-tokens`, `angle-${process.env.ENV}-1`);
301
+ const properties = body.data.properties;
302
+ const icon = properties["Icon"].files?.[0]?.file.url;
303
+ const iconFile = await fetch(icon);
304
+ const mimeType = iconFile.headers.get("content-type");
305
+ const extension = mimeType.split("/")[1];
306
+ const address = properties["Address"].rich_text[0].plain_text;
307
+ const chainId = properties["Chain ID"].number;
308
+ const symbol = properties["Symbol"].rich_text[0]?.plain_text;
309
+ const name = properties["Name"].rich_text[0]?.plain_text;
310
+ const byteArray = await iconFile.bytes();
311
+ const [token] = await TokenService.getManyOrCreate([
312
+ {
313
+ chainId,
314
+ address,
315
+ },
316
+ ]);
317
+ if (token) {
318
+ await TokenService.update(token.id, {
319
+ icon: icon && icon !== "" ? icon : token.icon,
320
+ displaySymbol: symbol && symbol !== "" ? symbol : token.symbol,
321
+ name: name && name !== "" ? name : token.name ? token.name : "Unknown",
322
+ });
323
+ await bucket.pushRaw(`${chainId}/${address}.${extension}`, byteArray, {
324
+ type: mimeType,
325
+ isPublic: true,
326
+ });
327
+ }
328
+ }
298
329
  }