@merkl/api 0.10.168 → 0.10.169

Sign up to get free protection for your applications and to get access to all the features.
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";