@merkl/api 0.20.53 → 0.20.55

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.
@@ -1,27 +1,43 @@
1
1
  import { AuthorizationHeadersDto, BackOfficeGuard } from "@/guards/BackOffice.guard";
2
2
  import { UnsupportedNetwork } from "@/utils/error";
3
- import { throwOnInvalidAddress, throwOnInvalidRequiredAddress } from "@/utils/throw";
3
+ import { throwOnInvalidAddress, throwOnInvalidRequiredAddress, throwOnUnsupportedChainId } from "@/utils/throw";
4
4
  import Elysia, { t } from "elysia";
5
5
  import { CacheService } from "../cache";
6
6
  import { TTLPresets } from "../cache/cache.model";
7
7
  import { ChainUniqueDto } from "../chain/chain.model";
8
- import { Bytes32Dto, RewardsPerV3PositionDto, UniV4ChainIdArray, UniswapV4PoolDto, } from "./uniswap.model";
8
+ import { Bytes32Dto, RewardsPerPositionDto, UniV4ChainIdArray, UniswapV4PoolDto, } from "./uniswap.model";
9
9
  import { UniswapService } from "./uniswap.service";
10
10
  export const UniswapController = new Elysia({
11
11
  prefix: "uniswap",
12
12
  detail: { tags: ["Uniswap"] },
13
13
  })
14
14
  .group("/reward", app => {
15
- return app.get("", async ({ query }) => {
16
- return await UniswapService.findV3RewardsPerPosition(query);
15
+ return app
16
+ .get("/3", async ({ query }) => {
17
+ return await UniswapService.findRewardsPerPosition("v3", query);
18
+ }, {
19
+ query: RewardsPerPositionDto,
20
+ beforeHandle: ({ query }) => {
21
+ query.address = throwOnInvalidRequiredAddress(query.address);
22
+ if (!!query.chainId)
23
+ throwOnUnsupportedChainId(query.chainId);
24
+ query.pool = throwOnInvalidAddress(query.pool);
25
+ },
26
+ detail: {
27
+ description: "Get rewards earned grouped by pool and Uniswap V3 position. Warning: this endpoint is still in beta.",
28
+ },
29
+ })
30
+ .get("/4", async ({ query }) => {
31
+ return await UniswapService.findRewardsPerPosition("v4", query);
17
32
  }, {
18
- query: RewardsPerV3PositionDto,
33
+ query: RewardsPerPositionDto,
19
34
  beforeHandle: ({ query }) => {
20
35
  query.address = throwOnInvalidRequiredAddress(query.address);
21
- query.poolAddress = throwOnInvalidAddress(query.poolAddress);
36
+ if (!!query.chainId)
37
+ throwOnUnsupportedChainId(query.chainId);
22
38
  },
23
39
  detail: {
24
- description: "Get rewards earned grouped by pool and uniswap v3 position",
40
+ description: "Get rewards earned grouped by pool and Uniswap V4 position. Warning: this endpoint is still in beta.",
25
41
  },
26
42
  });
27
43
  })
@@ -30,9 +30,10 @@ export declare const UniswapV4PoolDto: import("@sinclair/typebox").TObject<{
30
30
  export declare const Bytes32Dto: import("@sinclair/typebox").TObject<{
31
31
  poolId: import("@sinclair/typebox").TRegExp;
32
32
  }>;
33
- export declare const RewardsPerV3PositionDto: import("@sinclair/typebox").TObject<{
33
+ export declare const RewardsPerPositionDto: import("@sinclair/typebox").TObject<{
34
34
  address: import("@sinclair/typebox").TString;
35
- poolAddress: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
35
+ chainId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
36
+ pool: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
36
37
  positionId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
37
38
  }>;
38
39
  export declare const UniswapV4PoolsDto: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
@@ -49,4 +50,4 @@ export declare const UniswapV4PoolsDto: import("@sinclair/typebox").TRecord<impo
49
50
  symbolCurrency1: import("@sinclair/typebox").TString;
50
51
  tickSpacing: import("@sinclair/typebox").TNumber;
51
52
  }>>;
52
- export type RewardsPerPositionModel = typeof RewardsPerV3PositionDto.static;
53
+ export type RewardsPerPositionModel = typeof RewardsPerPositionDto.static;
@@ -33,10 +33,11 @@ export const Bytes32Dto = t.Object({
33
33
  description: "A 32-byte hexadecimal string (bytes32)",
34
34
  }),
35
35
  });
36
- export const RewardsPerV3PositionDto = t.Object({
36
+ export const RewardsPerPositionDto = t.Object({
37
37
  address: t.String({ description: "Address of the rewarded user" }),
38
- poolAddress: t.Optional(t.String({
39
- description: "Address of the Uniswap pool. Default: returns rewards for all pools.",
38
+ chainId: t.Optional(t.Numeric({ description: "Filter on Uniswap pool on a given chain. Default: returns rewards for all chains." })),
39
+ pool: t.Optional(t.String({
40
+ description: "Address Or Id of the Uniswap pool. Default: returns rewards for all pools.",
40
41
  })),
41
42
  positionId: t.Optional(t.String({
42
43
  description: "PositionId of the Uniswap position. Can be a tokenId if the position is an NFT or a bytes32 id if held directly on the pool. Default: returns rewards for all positions on the pools.",
@@ -1,7 +1,17 @@
1
1
  import { type MerklChainId } from "@sdk";
2
+ import { type Opportunity } from "../opportunity";
3
+ import type { Token } from "../token/token.model";
2
4
  import { type RewardsPerPositionModel, type UniV4ChainId, type UniswapV4PoolsReturnType } from "./uniswap.model";
3
5
  export declare abstract class UniswapService {
4
- static findV3RewardsPerPosition(query: RewardsPerPositionModel): Promise<Record<string, Record<string, any[]>>>;
6
+ static findRewardsPerPosition(version: "v3" | "v4", query: RewardsPerPositionModel): Promise<Record<number, Record<string, Record<string, {
7
+ campaignId: string;
8
+ amount: string;
9
+ claimed: string;
10
+ pending: string;
11
+ reason: string;
12
+ rewardToken: Token["model"];
13
+ opportunity: Opportunity["model"];
14
+ }[]>>>>;
5
15
  static getUniswapV4Pools(chainId?: UniV4ChainId): Promise<UniswapV4PoolsReturnType>;
6
16
  /** @deprecated */
7
17
  static getUniswapV4PoolsForChain(chainId: MerklChainId): Promise<UniswapV4PoolsReturnType>;
@@ -11,10 +11,10 @@ import { RewardService } from "../reward";
11
11
  import { LoggedEntityType, UniV4ChainIdArray, } from "./uniswap.model";
12
12
  import { UniswapRepository } from "./uniswap.repository";
13
13
  export class UniswapService {
14
- static async findV3RewardsPerPosition(query) {
14
+ static async findRewardsPerPosition(version, query) {
15
15
  /** Check if the user is blacklisted */
16
16
  const isBlacklisted = !!(await BlacklistService.isBlacklisted(query.address));
17
- // pool address => NFT or Position ID => [rewards]
17
+ // chainId => pool => NFT or Position ID => [rewards]
18
18
  const res = {};
19
19
  if (isBlacklisted)
20
20
  return res;
@@ -25,26 +25,37 @@ export class UniswapService {
25
25
  const merkleRoots = merkleRootsPromises
26
26
  .filter(({ status }) => status === "fulfilled")
27
27
  .map(x => x.value);
28
- const rewards = RewardService.format(await RewardService.getByRecipient(query.address, merkleRoots.map(({ live }) => live), true, false, null, "UniswapV3"));
28
+ /** Get breakdowns of rewards containing the correct version */
29
+ const rewards = RewardService.format(await RewardService.getByRecipient(query.address, merkleRoots.map(({ live }) => live), true, false, null, version === "v3" ? "UniswapV3" : "UniswapV4"));
29
30
  for (const reward of rewards) {
30
31
  const rewardToken = reward.token;
31
32
  for (const breakdown of reward.breakdowns) {
32
- let poolAddress = breakdown.opportunity.explorerAddress;
33
- if (!!poolAddress) {
34
- poolAddress = getAddress(poolAddress);
35
- res[poolAddress] = res[poolAddress] ?? {};
36
- if (!query.poolAddress || poolAddress === query.poolAddress) {
37
- const positionId = breakdown.reason.split("_")[1];
38
- if (!res[poolAddress][positionId])
39
- res[poolAddress][positionId] = [];
40
- const { opportunity, ...props } = breakdown;
41
- res[poolAddress][positionId].push({
42
- ...props,
43
- opportunity: OpportunityService.formatResponseBase(opportunity),
44
- rewardToken: rewardToken,
45
- });
33
+ if (version === "v3" && !breakdown.opportunity.explorerAddress)
34
+ continue;
35
+ const pool = version === "v3" ? getAddress(breakdown.opportunity.explorerAddress) : breakdown.reason.split("_")[1];
36
+ const { opportunity: rawOpportunity, ...props } = breakdown;
37
+ const opportunity = OpportunityService.formatResponseBase(rawOpportunity);
38
+ const chainId = opportunity.chainId;
39
+ try {
40
+ if (!!chainId && (!query.chainId || chainId === query.chainId)) {
41
+ res[chainId] = res[chainId] ?? {};
42
+ if (!!pool && (!query.pool || pool === query.pool)) {
43
+ res[chainId][pool] = res[chainId][pool] ?? {};
44
+ const positionId = breakdown.reason.split("_")[version === "v3" ? 1 : 2];
45
+ if (!!positionId && (!query.positionId || positionId === query.positionId)) {
46
+ res[chainId][pool][positionId] = res[chainId][pool][positionId] ?? [];
47
+ res[chainId][pool][positionId].push({
48
+ ...props,
49
+ opportunity,
50
+ rewardToken,
51
+ });
52
+ }
53
+ }
46
54
  }
47
55
  }
56
+ catch {
57
+ log.warn(`failed to parse positionId for reason for pool ${pool} and version ${version}`);
58
+ }
48
59
  }
49
60
  }
50
61
  return res;