@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.
- package/dist/src/cache/declaration.d.ts +0 -30
- package/dist/src/cache/declaration.js +0 -30
- package/dist/src/eden/index.d.ts +59 -12
- package/dist/src/index.d.ts +23 -4
- package/dist/src/libs/campaigns/campaignTypes/CLAMMDynamicData.js +2 -2
- package/dist/src/libs/positions/clamm/index.d.ts +1 -2
- package/dist/src/libs/positions/clamm/index.js +322 -330
- package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.d.ts +1 -1
- package/dist/src/libs/positions/clamm/thegraph/fetchAlmPositions.js +1 -1
- package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.d.ts +1 -1
- package/dist/src/libs/positions/clamm/thegraph/fetchAmmPositions.js +2 -2
- package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.d.ts +1 -2
- package/dist/src/libs/positions/clamm/thegraph/fetchFarmedPositions.js +1 -4
- package/dist/src/libs/positions/euler/index.js +2 -3
- package/dist/src/libs/positions/index.js +1 -1
- package/dist/src/modules/v4/campaign/campaign.controller.d.ts +2 -2
- package/dist/src/modules/v4/campaign/campaign.service.d.ts +6 -5
- package/dist/src/modules/v4/campaign/campaign.service.js +10 -2
- package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +2 -2
- package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +159 -0
- package/dist/src/modules/v4/opportunity/opportunity.repository.js +29 -0
- package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +131 -3
- package/dist/src/modules/v4/opportunity/opportunity.service.js +11 -0
- package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/AjnaPositionFetcher.js +90 -0
- package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/BadgerPositionFetcher.js +69 -0
- package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/ClammPositionFetcher.js +71 -0
- package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/DolomitePositionFetcher.js +45 -0
- package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/ERC20PositionFetcher.js +47 -0
- package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.d.ts +6 -0
- package/dist/src/modules/v4/position/implementations/EulerPositionFetcher.js +40 -0
- package/dist/src/modules/v4/position/index.d.ts +2 -0
- package/dist/src/modules/v4/position/index.js +2 -0
- package/dist/src/modules/v4/position/position.controller.d.ts +39 -0
- package/dist/src/modules/v4/position/position.controller.js +16 -0
- package/dist/src/modules/v4/position/position.model.d.ts +25 -0
- package/dist/src/modules/v4/position/position.model.js +5 -0
- package/dist/src/modules/v4/position/position.repository.d.ts +14 -0
- package/dist/src/modules/v4/position/position.repository.js +6 -0
- package/dist/src/modules/v4/position/position.service.d.ts +5 -0
- package/dist/src/modules/v4/position/position.service.js +34 -0
- package/dist/src/modules/v4/reward/reward.service.d.ts +5 -0
- package/dist/src/modules/v4/reward/reward.service.js +5 -2
- package/dist/src/modules/v4/router.d.ts +23 -4
- package/dist/src/modules/v4/router.js +3 -1
- package/dist/src/routes/v1/prices.js +2 -4
- package/dist/src/routes/v3/blacklist.d.ts +23 -4
- package/dist/src/routes/v3/campaigns.d.ts +24 -5
- package/dist/src/routes/v3/campaignsInfo.d.ts +23 -4
- package/dist/src/routes/v3/multiChainPositions.d.ts +23 -4
- package/dist/src/routes/v3/opportunity.d.ts +23 -4
- package/dist/src/routes/v3/positions.d.ts +23 -4
- package/dist/src/routes/v3/recipients.d.ts +6 -2
- package/dist/src/routes/v3/recipients.js +14 -8
- package/dist/src/routes/v3/rewards.d.ts +23 -4
- package/dist/src/routes/v3/updates.d.ts +23 -4
- package/dist/src/routes/v3/userRewards.d.ts +23 -4
- package/dist/src/utils/prices/services/erc4626Service.js +4 -4
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/libs/reports/campaignReport.d.ts +0 -9
- package/dist/src/libs/reports/campaignReport.js +0 -37
- package/dist/src/libs/reports/mainParameterRewards.d.ts +0 -3
- 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,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,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,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
|
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";
|