@merkl/api 0.10.167 → 0.10.169

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.
Files changed (68) hide show
  1. package/dist/src/cache/declaration.d.ts +0 -30
  2. package/dist/src/cache/declaration.js +0 -30
  3. package/dist/src/eden/index.d.ts +59 -12
  4. package/dist/src/index.d.ts +23 -4
  5. package/dist/src/libs/campaigns/campaignTypes/CLAMMDynamicData.js +2 -2
  6. package/dist/src/libs/positions/clamm/index.d.ts +1 -2
  7. package/dist/src/libs/positions/clamm/index.js +322 -330
  8. package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.d.ts +1 -1
  9. package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.js +1 -1
  10. package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.d.ts +1 -1
  11. package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.js +2 -2
  12. package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.d.ts +1 -2
  13. package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.js +1 -4
  14. package/dist/src/libs/positions/euler/index.js +2 -3
  15. package/dist/src/libs/positions/index.js +1 -1
  16. package/dist/src/modules/v4/campaign/campaign.controller.d.ts +2 -2
  17. package/dist/src/modules/v4/campaign/campaign.service.d.ts +6 -5
  18. package/dist/src/modules/v4/campaign/campaign.service.js +10 -2
  19. package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +2 -2
  20. package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +159 -0
  21. package/dist/src/modules/v4/opportunity/opportunity.repository.js +29 -0
  22. package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +131 -3
  23. package/dist/src/modules/v4/opportunity/opportunity.service.js +11 -0
  24. package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.d.ts +6 -0
  25. package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.js +90 -0
  26. package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.d.ts +6 -0
  27. package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.js +69 -0
  28. package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.d.ts +6 -0
  29. package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.js +71 -0
  30. package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.d.ts +6 -0
  31. package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.js +45 -0
  32. package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.d.ts +6 -0
  33. package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.js +47 -0
  34. package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.d.ts +6 -0
  35. package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.js +40 -0
  36. package/dist/src/modules/v4/position/index.d.ts +2 -0
  37. package/dist/src/modules/v4/position/index.js +2 -0
  38. package/dist/src/modules/v4/position/position.controller.d.ts +39 -0
  39. package/dist/src/modules/v4/position/position.controller.js +16 -0
  40. package/dist/src/modules/v4/position/position.model.d.ts +25 -0
  41. package/dist/src/modules/v4/position/position.model.js +5 -0
  42. package/dist/src/modules/v4/position/position.repository.d.ts +14 -0
  43. package/dist/src/modules/v4/position/position.repository.js +6 -0
  44. package/dist/src/modules/v4/position/position.service.d.ts +5 -0
  45. package/dist/src/modules/v4/position/position.service.js +34 -0
  46. package/dist/src/modules/v4/reward/reward.service.d.ts +5 -0
  47. package/dist/src/modules/v4/reward/reward.service.js +5 -2
  48. package/dist/src/modules/v4/router.d.ts +23 -4
  49. package/dist/src/modules/v4/router.js +3 -1
  50. package/dist/src/routes/v1/prices.js +2 -4
  51. package/dist/src/routes/v3/blacklist.d.ts +23 -4
  52. package/dist/src/routes/v3/campaigns.d.ts +24 -5
  53. package/dist/src/routes/v3/campaignsInfo.d.ts +23 -4
  54. package/dist/src/routes/v3/multiChainPositions.d.ts +23 -4
  55. package/dist/src/routes/v3/opportunity.d.ts +23 -4
  56. package/dist/src/routes/v3/positions.d.ts +23 -4
  57. package/dist/src/routes/v3/recipients.d.ts +6 -2
  58. package/dist/src/routes/v3/recipients.js +14 -8
  59. package/dist/src/routes/v3/rewards.d.ts +23 -4
  60. package/dist/src/routes/v3/updates.d.ts +23 -4
  61. package/dist/src/routes/v3/userRewards.d.ts +23 -4
  62. package/dist/src/utils/prices/services/erc4626Service.js +4 -4
  63. package/dist/tsconfig.package.tsbuildinfo +1 -1
  64. package/package.json +1 -1
  65. package/dist/src/libs/reports/campaignReport.d.ts +0 -9
  66. package/dist/src/libs/reports/campaignReport.js +0 -37
  67. package/dist/src/libs/reports/mainParameterRewards.d.ts +0 -3
  68. package/dist/src/libs/reports/mainParameterRewards.js +0 -48
@@ -0,0 +1,90 @@
1
+ import { AjnaSubCampaignType, BN2Number, Campaign, ChainInteractionService, NETWORK_LABELS, POOL_INFO_UTILS, PoolInfoUtilsInterface, } from "@sdk";
2
+ import axios from "axios";
3
+ import { CampaignService } from "../../campaign";
4
+ const campaignType = Campaign.AJNA;
5
+ export class AjnaPositionFetcher {
6
+ fetchPositions = async (chainId, user, opportunities) => {
7
+ opportunities = opportunities.filter(o => o.type === Campaign[campaignType] && o.tokens?.length > 0 && o.chainId === chainId && !!o?.campaigns?.length);
8
+ const calls = [];
9
+ for (const opportunity of opportunities) {
10
+ const campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
11
+ // Call per opportunity
12
+ if (campaign.campaignSubType === AjnaSubCampaignType.lend) {
13
+ calls.push({
14
+ allowFailure: true,
15
+ callData: PoolInfoUtilsInterface.encodeFunctionData("poolPricesInfo", [campaign.campaignParameters.poolId]),
16
+ target: POOL_INFO_UTILS[chainId],
17
+ });
18
+ }
19
+ else {
20
+ calls.push({
21
+ allowFailure: true,
22
+ callData: PoolInfoUtilsInterface.encodeFunctionData("borrowerInfo", [
23
+ campaign.campaignParameters.poolId,
24
+ user,
25
+ ]),
26
+ target: POOL_INFO_UTILS[chainId],
27
+ });
28
+ }
29
+ }
30
+ const res = await ChainInteractionService(chainId).fetchState(calls);
31
+ const result = [];
32
+ for (const [index, opportunity] of opportunities.entries()) {
33
+ // Decoding per opportunity
34
+ const campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
35
+ let userSupply = 0;
36
+ // @Lamicham it's not scalable at all to do this.. We do 1 call per opportunity, sequentially, so it ends up
37
+ // slowing the whole position route
38
+ const resAjna = await axios.get(`https://ajna-api.blockanalitica.com/v4/${NETWORK_LABELS[chainId].toLowerCase()}/wallets/${user.toLowerCase()}/pools/${campaign.campaignParameters.poolId.toLowerCase()}/buckets/?p=1&p_size=50`);
39
+ if (resAjna.data.count === 0) {
40
+ continue;
41
+ }
42
+ if (campaign.campaignSubType === AjnaSubCampaignType.lend) {
43
+ const htpIndex = PoolInfoUtilsInterface.decodeFunctionResult("poolPricesInfo", res[index].returnData)[3];
44
+ const lupIndex = PoolInfoUtilsInterface.decodeFunctionResult("poolPricesInfo", res[index].returnData)[5];
45
+ const threshold = BN2Number(BN2Number(lupIndex, 0) === 0 ? lupIndex : lupIndex.gt(htpIndex) ? lupIndex.add(22) : htpIndex.add(22), 0);
46
+ let nextRoute = null;
47
+ if (!!resAjna.data.next) {
48
+ nextRoute = `https://${resAjna.data.next.slice(resAjna.data.next.indexOf(":") + 1)}`;
49
+ }
50
+ let position;
51
+ for (position of resAjna.data.results) {
52
+ if (position.bucket_index < threshold) {
53
+ userSupply += Number(position.deposit);
54
+ }
55
+ }
56
+ while (nextRoute !== null) {
57
+ const resNext = await axios.get(nextRoute);
58
+ let position;
59
+ for (position of resNext.data.results) {
60
+ if (position.bucket_index < threshold) {
61
+ userSupply += Number(position.deposit);
62
+ }
63
+ }
64
+ if (!!resNext.data.next) {
65
+ nextRoute = `https://${resNext.data.next.slice(resNext.data.next.indexOf(":") + 1)}`;
66
+ }
67
+ else {
68
+ nextRoute = null;
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ userSupply = BN2Number(PoolInfoUtilsInterface.decodeFunctionResult("borrowerInfo", res[index].returnData)[0], 18);
74
+ }
75
+ if (userSupply > 0) {
76
+ result.push({
77
+ flags: {},
78
+ opportunity,
79
+ tokens: [
80
+ {
81
+ token: opportunity.tokens.find(t => t.address === campaign.campaignParameters.quoteToken),
82
+ breakdown: [{ type: "balance", value: userSupply }],
83
+ },
84
+ ],
85
+ });
86
+ }
87
+ }
88
+ return result;
89
+ };
90
+ }
@@ -0,0 +1,6 @@
1
+ import { type MerklChainId } from "@sdk";
2
+ import type { Opportunity } from "../../opportunity";
3
+ import type { PositionFetcher, PositionT } from "../position.model";
4
+ export declare class BadgerPositionFetcher implements PositionFetcher {
5
+ fetchPositions: (chainId: MerklChainId, user: string, opportunities: Opportunity["model"][]) => Promise<PositionT[]>;
6
+ }
@@ -0,0 +1,69 @@
1
+ import { CDPMANAGER_ADDRESS, SORTEDCDPS_ADDRESS } from "../../../../constants";
2
+ import { BN2Number, Campaign, CdpManagerInterface, ChainInteractionService, ERC20Interface, SortedCdpsInterface, } from "@sdk";
3
+ import { CampaignService } from "../../campaign";
4
+ const campaignType = Campaign.BADGER;
5
+ export class BadgerPositionFetcher {
6
+ fetchPositions = async (chainId, user, opportunities) => {
7
+ opportunities = opportunities.filter(o => o.type === Campaign[campaignType] && o.tokens?.length > 0 && o.chainId === chainId);
8
+ const calls = [];
9
+ calls.push({
10
+ allowFailure: false,
11
+ callData: CdpManagerInterface.encodeFunctionData("getSystemDebt"),
12
+ target: CDPMANAGER_ADDRESS,
13
+ });
14
+ for (const opportunity of opportunities) {
15
+ // Call per opportunity
16
+ const _campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
17
+ calls.push({
18
+ allowFailure: false,
19
+ callData: SortedCdpsInterface.encodeFunctionData("getCdpsOf", [user]),
20
+ target: SORTEDCDPS_ADDRESS,
21
+ });
22
+ for (const _token of opportunity.tokens) {
23
+ // Call per token
24
+ }
25
+ }
26
+ const res = await ChainInteractionService(chainId).fetchState(calls);
27
+ const result = [];
28
+ let i = 0;
29
+ const totalSupplyEBTC = BN2Number(CdpManagerInterface.decodeFunctionResult("getSystemDebt", res[i++].returnData)[0], 18);
30
+ for (const [index, opportunity] of opportunities.entries()) {
31
+ // Decoding per opportunity
32
+ const campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
33
+ let userSupply = 0;
34
+ const cdps = SortedCdpsInterface.decodeFunctionResult("getCdpsOf", res[i++].returnData)[0];
35
+ if (cdps.length !== 0) {
36
+ const secondCalls = cdps.map((cdp) => {
37
+ return {
38
+ allowFailure: false,
39
+ callData: CdpManagerInterface.encodeFunctionData("Cdps", cdp),
40
+ target: CDPMANAGER_ADDRESS,
41
+ };
42
+ });
43
+ const secondRes = await ChainInteractionService(chainId).fetchState(secondCalls);
44
+ cdps.forEach((cdp, j) => {
45
+ const cdpSupply = CdpManagerInterface.decodeFunctionResult("Cdps", secondRes[j].returnData)[0];
46
+ userSupply += BN2Number(cdpSupply, campaign.campaignParameters.decimalsTargetToken);
47
+ });
48
+ }
49
+ for (const [subIndex, token] of opportunity.tokens.entries()) {
50
+ // Decoding per token
51
+ const balance = ERC20Interface.decodeFunctionResult("balanceOf", res[index + subIndex].returnData)[0].toString();
52
+ if (BigInt(balance) > 0n) {
53
+ const position = {
54
+ flags: {},
55
+ opportunity,
56
+ tokens: [
57
+ {
58
+ token,
59
+ breakdown: [{ type: "balance", value: BN2Number(balance, token.decimals) }],
60
+ },
61
+ ],
62
+ };
63
+ result.push(position);
64
+ }
65
+ }
66
+ }
67
+ return result;
68
+ };
69
+ }
@@ -0,0 +1,6 @@
1
+ import { type MerklChainId } from "@sdk";
2
+ import type { Opportunity } from "../../opportunity";
3
+ import type { PositionFetcher, PositionT } from "../position.model";
4
+ export declare class ClammPositionFetcher implements PositionFetcher {
5
+ fetchPositions: (chainId: MerklChainId, user: string, opportunities: Opportunity["model"][]) => Promise<PositionT[]>;
6
+ }
@@ -0,0 +1,71 @@
1
+ import { getClammUserPositions } from "../../../../libs/positions/clamm";
2
+ import { Campaign } from "@sdk";
3
+ import { CampaignService } from "../../campaign";
4
+ const campaignType = Campaign.CLAMM;
5
+ export class ClammPositionFetcher {
6
+ fetchPositions = async (chainId, user, opportunities) => {
7
+ opportunities = opportunities.filter(o => o.type === Campaign[campaignType] && o.tokens?.length > 0 && o.chainId === chainId);
8
+ // AMM => pool address => pool data
9
+ const poolsByAmm = {};
10
+ for (const opportunity of opportunities) {
11
+ // Call per opportunity
12
+ const campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
13
+ if (!poolsByAmm[campaign.campaignParameters.amm]) {
14
+ poolsByAmm[campaign.campaignParameters.amm] = {};
15
+ }
16
+ if (!poolsByAmm[campaign.campaignParameters.amm][campaign.campaignParameters.poolAddress]) {
17
+ poolsByAmm[campaign.campaignParameters.amm][campaign.campaignParameters.poolAddress] = {
18
+ token0: campaign.campaignParameters.token0,
19
+ token1: campaign.campaignParameters.token1,
20
+ symbolToken0: campaign.campaignParameters.symbolToken0,
21
+ symbolToken1: campaign.campaignParameters.symbolToken1,
22
+ decimalsToken0: campaign.campaignParameters.decimalsToken0,
23
+ decimalsToken1: campaign.campaignParameters.decimalsToken1,
24
+ mainParameter: campaign.mainParameter,
25
+ forwarders: {},
26
+ };
27
+ }
28
+ }
29
+ console.log(poolsByAmm);
30
+ const clammPositions = await getClammUserPositions(user, chainId, poolsByAmm, false);
31
+ console.log(clammPositions);
32
+ const result = [];
33
+ for (const opportunity of opportunities) {
34
+ const campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
35
+ const clammPosition = clammPositions[`2_${campaign.campaignParameters.poolAddress}`];
36
+ const poolData = poolsByAmm[campaign.campaignParameters.amm][campaign.campaignParameters.poolAddress];
37
+ if (!!clammPosition) {
38
+ const position = {
39
+ flags: {},
40
+ opportunity,
41
+ tokens: [
42
+ {
43
+ token: {
44
+ address: poolData.token0,
45
+ decimals: poolData.decimalsToken0,
46
+ symbol: poolData.symbolToken0,
47
+ },
48
+ breakdown: [
49
+ { type: "balance", value: clammPosition.userBalanceToken0 },
50
+ { type: "liquidity", value: clammPosition.userInRangeLiquidity },
51
+ ],
52
+ },
53
+ {
54
+ token: {
55
+ address: poolData.token1,
56
+ decimals: poolData.decimalsToken1,
57
+ symbol: poolData.symbolToken1,
58
+ },
59
+ breakdown: [
60
+ { type: "balance", value: clammPosition.userBalanceToken1 },
61
+ { type: "liquidity", value: clammPosition.userInRangeLiquidity },
62
+ ],
63
+ },
64
+ ],
65
+ };
66
+ result.push(position);
67
+ }
68
+ }
69
+ return result;
70
+ };
71
+ }
@@ -0,0 +1,6 @@
1
+ import { type MerklChainId } from "@sdk";
2
+ import type { Opportunity } from "../../opportunity";
3
+ import type { PositionFetcher } from "../position.model";
4
+ export declare class DolomitePositionFetcher implements PositionFetcher {
5
+ fetchPositions: (chainId: MerklChainId, user: string, opportunities: Opportunity["model"][]) => Promise<import("..").PositionT[]>;
6
+ }
@@ -0,0 +1,45 @@
1
+ import { Campaign } from "@sdk";
2
+ import { utils } from "ethers";
3
+ import { CampaignService } from "../../campaign";
4
+ import { PositionRepository } from "../position.repository";
5
+ const campaignType = Campaign.DOLOMITE;
6
+ export class DolomitePositionFetcher {
7
+ fetchPositions = async (chainId, user, opportunities) => {
8
+ opportunities = opportunities.filter(o => o.type === Campaign[campaignType] && o.tokens?.length > 0 && o.chainId === chainId);
9
+ const dolomitePositions = await PositionRepository.findManyDolomitePositions(chainId, user);
10
+ // Generic calls
11
+ for (const opportunity of opportunities) {
12
+ // Call per opportunity
13
+ const _campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
14
+ for (const _token of opportunity.tokens) {
15
+ // Call per token
16
+ }
17
+ }
18
+ const result = [];
19
+ // Decoding Generic calls
20
+ for (const [_index, opportunity] of opportunities.entries()) {
21
+ const campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
22
+ // Decoding per opportunity
23
+ const positionIndex = dolomitePositions.findIndex(y => utils.getAddress(y.token.id) === campaign.campaignParameters.targetToken);
24
+ const position = positionIndex >= 0 ? dolomitePositions[positionIndex] : undefined;
25
+ if (!position)
26
+ continue;
27
+ const borrowBalance = Number.parseFloat(position.token.borrowLiquidity);
28
+ const supplyBalance = Number.parseFloat(position.token.supplyLiquidity);
29
+ result.push({
30
+ flags: {},
31
+ opportunity,
32
+ tokens: [
33
+ {
34
+ token: opportunity.tokens.find(t => utils.getAddress(t.address) === campaign.campaignParameters.targetToken),
35
+ breakdown: [
36
+ { type: "borrowed", value: borrowBalance },
37
+ { type: "supplied", value: supplyBalance },
38
+ ],
39
+ },
40
+ ],
41
+ });
42
+ }
43
+ return result;
44
+ };
45
+ }
@@ -0,0 +1,6 @@
1
+ import { type MerklChainId } from "@sdk";
2
+ import type { Opportunity } from "../../opportunity";
3
+ import type { PositionFetcher, PositionT } from "../position.model";
4
+ export declare class ERC20PositionFetcher implements PositionFetcher {
5
+ fetchPositions: (chainId: MerklChainId, user: string, opportunities: Opportunity["model"][]) => Promise<PositionT[]>;
6
+ }
@@ -0,0 +1,47 @@
1
+ import { BN2Number, Campaign, ChainInteractionService, ERC20Interface } from "@sdk";
2
+ import { CampaignService } from "../../campaign";
3
+ const campaignType = Campaign.ERC20;
4
+ export class ERC20PositionFetcher {
5
+ fetchPositions = async (chainId, user, opportunities) => {
6
+ opportunities = opportunities.filter(o => o.type === Campaign[campaignType] && o.tokens?.length > 0 && o.chainId === chainId);
7
+ const calls = [];
8
+ // Generic calls
9
+ for (const opportunity of opportunities) {
10
+ // Call per opportunity
11
+ const _campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
12
+ for (const token of opportunity.tokens) {
13
+ // Call per token
14
+ calls.push({
15
+ allowFailure: true,
16
+ callData: ERC20Interface.encodeFunctionData("balanceOf", [user]),
17
+ target: token.address,
18
+ });
19
+ }
20
+ }
21
+ const res = await ChainInteractionService(chainId).fetchState(calls);
22
+ const result = [];
23
+ // Decoding Generic calls
24
+ for (const [index, opportunity] of opportunities.entries()) {
25
+ // Decoding per opportunity
26
+ const _campaign = CampaignService.formatAsCampaignParameters(opportunity.campaigns[0]);
27
+ for (const [subIndex, token] of opportunity.tokens.entries()) {
28
+ // Decoding per token
29
+ const balance = ERC20Interface.decodeFunctionResult("balanceOf", res[index + subIndex].returnData)[0].toString();
30
+ if (BigInt(balance) > 0n) {
31
+ const position = {
32
+ flags: {},
33
+ opportunity,
34
+ tokens: [
35
+ {
36
+ token,
37
+ breakdown: [{ type: "balance", value: BN2Number(balance, token.decimals) }],
38
+ },
39
+ ],
40
+ };
41
+ result.push(position);
42
+ }
43
+ }
44
+ }
45
+ return result;
46
+ };
47
+ }
@@ -0,0 +1,6 @@
1
+ import { type MerklChainId } from "@sdk";
2
+ import type { Opportunity } from "../../opportunity";
3
+ import type { PositionFetcher, PositionT } from "../position.model";
4
+ export declare class EulerPositionFetcher implements PositionFetcher {
5
+ fetchPositions: (chainId: MerklChainId, user: string, opportunities: Opportunity["model"][]) => Promise<PositionT[]>;
6
+ }
@@ -0,0 +1,40 @@
1
+ import { BN2Number, Campaign, ChainInteractionService, ERC20Interface } from "@sdk";
2
+ const campaignType = Campaign.EULER;
3
+ export class EulerPositionFetcher {
4
+ fetchPositions = async (chainId, user, opportunities) => {
5
+ opportunities = opportunities.filter(o => o.type === Campaign[campaignType] && o.tokens?.length > 0 && o.chainId === chainId);
6
+ const calls = [];
7
+ for (const opportunity of opportunities) {
8
+ // Call per opportunity
9
+ for (const token of opportunity.tokens) {
10
+ calls.push({
11
+ allowFailure: true,
12
+ callData: ERC20Interface.encodeFunctionData("balanceOf", [user]),
13
+ target: token.address,
14
+ });
15
+ }
16
+ }
17
+ const res = await ChainInteractionService(chainId).fetchState(calls);
18
+ const result = [];
19
+ for (const [index, opportunity] of opportunities.entries()) {
20
+ // Decoding calls per opportunity
21
+ for (const [subIndex, token] of opportunity.tokens.entries()) {
22
+ const balance = ERC20Interface.decodeFunctionResult("balanceOf", res[index + subIndex].returnData)[0].toString();
23
+ if (BigInt(balance) > 0n) {
24
+ const position = {
25
+ flags: {},
26
+ opportunity,
27
+ tokens: [
28
+ {
29
+ token,
30
+ breakdown: [{ type: "balance", value: BN2Number(balance, token.decimals) }],
31
+ },
32
+ ],
33
+ };
34
+ result.push(position);
35
+ }
36
+ }
37
+ }
38
+ return result;
39
+ };
40
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./position.model";
2
+ export * from "./position.service";
@@ -0,0 +1,2 @@
1
+ export * from "./position.model";
2
+ export * from "./position.service";
@@ -0,0 +1,39 @@
1
+ import { Elysia } from "elysia";
2
+ export declare const PositionController: Elysia<"/positions", false, {
3
+ decorator: {};
4
+ store: {};
5
+ derive: {};
6
+ resolve: {};
7
+ }, {
8
+ type: {};
9
+ error: {};
10
+ }, {
11
+ schema: {};
12
+ macro: {};
13
+ macroFn: {};
14
+ }, {
15
+ positions: {
16
+ index: {
17
+ get: {
18
+ body: unknown;
19
+ params: {};
20
+ query: {
21
+ chainId: number;
22
+ address: string;
23
+ };
24
+ headers: unknown;
25
+ response: {
26
+ 200: any[];
27
+ };
28
+ };
29
+ };
30
+ };
31
+ }, {
32
+ derive: {};
33
+ resolve: {};
34
+ schema: {};
35
+ }, {
36
+ derive: {};
37
+ resolve: {};
38
+ schema: {};
39
+ }>;
@@ -0,0 +1,16 @@
1
+ import { throwOnInvalidRequiredAddress, throwOnUnsupportedChainId } from "../../../utils/throw";
2
+ import { Elysia } from "elysia";
3
+ import { PositionsInputDto } from "./position.model";
4
+ import { PositionService } from "./position.service";
5
+ // ─── Positions Controller ──────────────────────────────────────────────
6
+ export const PositionController = new Elysia({ prefix: "/positions", detail: { tags: ["Positions"] } })
7
+ // ─── Get User Positions on a Chain ───────────────────────────────────
8
+ .get("/", async ({ query }) => {
9
+ return await PositionService.fetchPositions(query);
10
+ }, {
11
+ query: PositionsInputDto,
12
+ beforeHandle: ({ query }) => {
13
+ throwOnUnsupportedChainId(query.chainId);
14
+ query.address = throwOnInvalidRequiredAddress(query.address);
15
+ },
16
+ });
@@ -0,0 +1,25 @@
1
+ import type { MerklChainId } from "@sdk";
2
+ import type { Opportunity } from "../opportunity";
3
+ export type PositionT = {
4
+ flags: {
5
+ range?: string;
6
+ id?: string;
7
+ link?: string;
8
+ };
9
+ opportunity: Opportunity["model"];
10
+ tokens: {
11
+ token: Opportunity["model"]["tokens"][0];
12
+ breakdown: {
13
+ type: string;
14
+ value: number;
15
+ }[];
16
+ }[];
17
+ };
18
+ export interface PositionFetcher {
19
+ fetchPositions(chainId: MerklChainId, user: string, opportunity: Opportunity["model"][]): Promise<PositionT[]>;
20
+ }
21
+ export declare const PositionsInputDto: import("@sinclair/typebox").TObject<{
22
+ chainId: import("@sinclair/typebox").TNumber;
23
+ address: import("@sinclair/typebox").TString;
24
+ }>;
25
+ export type PositionsInputModel = typeof PositionsInputDto.static;
@@ -0,0 +1,5 @@
1
+ import { t } from "elysia";
2
+ export const PositionsInputDto = t.Object({
3
+ chainId: t.Numeric(),
4
+ address: t.String(),
5
+ });
@@ -0,0 +1,14 @@
1
+ import type { MerklChainId } from "@sdk";
2
+ export declare class PositionRepository {
3
+ static findManyDolomitePositions(chainId: MerklChainId, user: string): Promise<{
4
+ balance: string;
5
+ token: {
6
+ id: string;
7
+ marketId: string;
8
+ symbol: string;
9
+ decimals: string;
10
+ borrowLiquidity: string;
11
+ supplyLiquidity: string;
12
+ };
13
+ }[]>;
14
+ }
@@ -0,0 +1,6 @@
1
+ import axios from "axios";
2
+ export class PositionRepository {
3
+ static async findManyDolomitePositions(chainId, user) {
4
+ return (await axios.get(`https://api.dolomite.io/balances/${chainId}/users/${user}`)).data.Result;
5
+ }
6
+ }
@@ -0,0 +1,5 @@
1
+ import type { PositionsInputModel } from "./position.model";
2
+ export declare class PositionService {
3
+ #private;
4
+ static fetchPositions(query: PositionsInputModel): Promise<any[]>;
5
+ }
@@ -0,0 +1,34 @@
1
+ import { MERKL_USER_POSITION_FETCHING_GLOBAL_TIMEOUT } from "../../../constants";
2
+ import { Campaign, withTimeout } from "@sdk";
3
+ import { OpportunityService } from "../opportunity";
4
+ import { AjnaPositionFetcher } from "./implementations/AjnaPositionFetcher";
5
+ import { BadgerPositionFetcher } from "./implementations/BadgerPositionFetcher";
6
+ import { ClammPositionFetcher } from "./implementations/ClammPositionFetcher";
7
+ import { DolomitePositionFetcher } from "./implementations/DolomitePositionFetcher";
8
+ import { ERC20PositionFetcher } from "./implementations/ERC20PositionFetcher";
9
+ import { EulerPositionFetcher } from "./implementations/EulerPositionFetcher";
10
+ export class PositionService {
11
+ static #fetchers = {
12
+ [Campaign.ERC20]: new ERC20PositionFetcher(),
13
+ [Campaign.EULER]: new EulerPositionFetcher(),
14
+ [Campaign.AJNA]: new AjnaPositionFetcher(),
15
+ [Campaign.BADGER]: new BadgerPositionFetcher(),
16
+ [Campaign.CLAMM]: new ClammPositionFetcher(),
17
+ [Campaign.DOLOMITE]: new DolomitePositionFetcher(),
18
+ };
19
+ static async fetchPositions(query) {
20
+ const opportunities = await OpportunityService.findLiveWithFirstCampaign(query.chainId);
21
+ const promises = [];
22
+ for (const campaignType of Object.keys(Campaign)) {
23
+ const fetcher = PositionService.#fetchers[Number.parseInt(campaignType)];
24
+ if (fetcher) {
25
+ promises.push(withTimeout(fetcher.fetchPositions(query.chainId, query.address, opportunities.filter(o => !!o)), MERKL_USER_POSITION_FETCHING_GLOBAL_TIMEOUT));
26
+ }
27
+ }
28
+ const settledPromises = await Promise.allSettled(promises);
29
+ for (const errors of settledPromises.filter(p => p.status === "rejected")) {
30
+ console.error(errors.reason);
31
+ }
32
+ return settledPromises.filter(p => p.status === "fulfilled").flatMap(p => p.value);
33
+ }
34
+ }
@@ -532,6 +532,11 @@ export declare abstract class RewardService {
532
532
  campaignId: string;
533
533
  amount: bigint;
534
534
  }>;
535
+ static getAmountAndClaimedForCampaigns(x: CampaignIdWithoutPageModel): Promise<{
536
+ campaignId: string;
537
+ amount: string;
538
+ claimed: string;
539
+ }[]>;
535
540
  static getUnclaimed(x: CampaignIdWithoutPageModel): Promise<Record<string, string>>;
536
541
  static extractDailyRewardsRecordFromDynamicData<C extends Campaign>(type: C, dynamicData: CampaignDynamicData<C>[], timestamp?: bigint): Promise<DailyRewardsRecord["model"]>;
537
542
  }
@@ -259,13 +259,16 @@ export class RewardService {
259
259
  const root = await MerklRootService.fetch(query.chainId);
260
260
  return RewardRepository.total(CampaignService.hashId({ distributionChain: query.chainId, campaignId: query.campaignId }), root.live);
261
261
  }
262
+ static async getAmountAndClaimedForCampaigns(x) {
263
+ const currentRoot = await MerklRootService.fetch(x.chainId);
264
+ return await RewardRepository.getAmountAndClaimedForCampaigns(currentRoot.live, x);
265
+ }
262
266
  static async getUnclaimed(x) {
263
267
  const campaignToCampaignIds = x.campaignIds.reduce((acc, campaignId) => {
264
268
  acc[CampaignService.hashId({ distributionChain: x.chainId, campaignId })] = campaignId;
265
269
  return acc;
266
270
  }, {});
267
- const currentRoot = await MerklRootService.fetch(x.chainId);
268
- const data = await RewardRepository.getAmountAndClaimedForCampaigns(currentRoot.live, x);
271
+ const data = await RewardService.getAmountAndClaimedForCampaigns(x);
269
272
  return data.reduce((acc, { amount, campaignId, claimed }) => {
270
273
  if (!acc[campaignToCampaignIds[campaignId]])
271
274
  acc[campaignToCampaignIds[campaignId]] = "0";