@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.
- 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";
|