@defisaver/positions-sdk 2.1.142 → 2.1.143
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/cjs/claiming/aaveV3.d.ts +2 -1
- package/cjs/claiming/aaveV3.js +13 -4
- package/cjs/fluid/index.d.ts +8 -0
- package/cjs/fluid/index.js +8 -2
- package/cjs/fluid/merkl.d.ts +2 -1
- package/cjs/fluid/merkl.js +7 -3
- package/cjs/helpers/fluidHelpers/index.d.ts +3 -1
- package/cjs/helpers/fluidHelpers/index.js +37 -4
- package/cjs/portfolio/index.js +5 -5
- package/cjs/types/claiming.d.ts +12 -1
- package/cjs/types/claiming.js +2 -0
- package/cjs/types/common.d.ts +4 -0
- package/cjs/types/common.js +5 -1
- package/cjs/types/fluid.d.ts +2 -0
- package/esm/claiming/aaveV3.d.ts +2 -1
- package/esm/claiming/aaveV3.js +12 -4
- package/esm/fluid/index.d.ts +8 -0
- package/esm/fluid/index.js +8 -2
- package/esm/fluid/merkl.d.ts +2 -1
- package/esm/fluid/merkl.js +5 -3
- package/esm/helpers/fluidHelpers/index.d.ts +3 -1
- package/esm/helpers/fluidHelpers/index.js +38 -6
- package/esm/portfolio/index.js +6 -6
- package/esm/types/claiming.d.ts +12 -1
- package/esm/types/claiming.js +2 -0
- package/esm/types/common.d.ts +4 -0
- package/esm/types/common.js +4 -0
- package/esm/types/fluid.d.ts +2 -0
- package/package.json +1 -1
- package/src/claiming/aaveV3.ts +19 -7
- package/src/fluid/index.ts +8 -2
- package/src/fluid/merkl.ts +5 -2
- package/src/helpers/fluidHelpers/index.ts +68 -6
- package/src/portfolio/index.ts +6 -6
- package/src/types/claiming.ts +13 -0
- package/src/types/common.ts +5 -0
- package/src/types/fluid.ts +3 -1
package/cjs/claiming/aaveV3.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import { EthAddress, NetworkNumber } from '../types';
|
|
3
|
-
import { ClaimableToken } from '../types/claiming';
|
|
3
|
+
import { ClaimableToken, MerklRewardsClaimableToken } from '../types/claiming';
|
|
4
4
|
export declare function getUnclaimedRewardsForAllMarkets(provider: Client, network: NetworkNumber, walletAddress: EthAddress, marketAddress: EthAddress): Promise<ClaimableToken[]>;
|
|
5
|
+
export declare function getMerklUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<MerklRewardsClaimableToken[]>;
|
|
5
6
|
export declare function getMeritUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<ClaimableToken[]>;
|
package/cjs/claiming/aaveV3.js
CHANGED
|
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.getUnclaimedRewardsForAllMarkets = getUnclaimedRewardsForAllMarkets;
|
|
16
|
+
exports.getMerklUnclaimedRewards = getMerklUnclaimedRewards;
|
|
16
17
|
exports.getMeritUnclaimedRewards = getMeritUnclaimedRewards;
|
|
17
18
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
18
19
|
const claiming_1 = require("../types/claiming");
|
|
@@ -95,7 +96,7 @@ function getUnclaimedRewardsForAllMarkets(provider, network, walletAddress, mark
|
|
|
95
96
|
return mapAaveRewardsToClaimableTokens(Object.values(totalUnclaimedPerRewardToken), marketAddress, walletAddress);
|
|
96
97
|
});
|
|
97
98
|
}
|
|
98
|
-
function
|
|
99
|
+
function getMerklUnclaimedRewards(account, network) {
|
|
99
100
|
return __awaiter(this, void 0, void 0, function* () {
|
|
100
101
|
let data;
|
|
101
102
|
try {
|
|
@@ -103,7 +104,7 @@ function getMeritUnclaimedRewards(account, network) {
|
|
|
103
104
|
data = yield res.json();
|
|
104
105
|
}
|
|
105
106
|
catch (error) {
|
|
106
|
-
console.error('External API Failure:
|
|
107
|
+
console.error('External API Failure: Merkl rewards', error);
|
|
107
108
|
data = [];
|
|
108
109
|
}
|
|
109
110
|
const claimableTokens = [];
|
|
@@ -121,12 +122,12 @@ function getMeritUnclaimedRewards(account, network) {
|
|
|
121
122
|
return;
|
|
122
123
|
const unclaimed = (0, utils_1.getEthAmountForDecimals)(unclaimedAmount, token.decimals);
|
|
123
124
|
claimableTokens.push({
|
|
124
|
-
claimType: claiming_1.ClaimType.
|
|
125
|
+
claimType: claiming_1.ClaimType.MERKL_REWARDS,
|
|
125
126
|
amount: unclaimed,
|
|
126
127
|
symbol: token.symbol,
|
|
127
128
|
tokenAddress: token.address,
|
|
128
129
|
walletAddress: account,
|
|
129
|
-
label: '
|
|
130
|
+
label: 'Merkl Rewards',
|
|
130
131
|
underlyingSymbol: (0, aaveHelpers_1.getAaveUnderlyingSymbol)(token.symbol),
|
|
131
132
|
additionalClaimFields: {
|
|
132
133
|
accumulated: amount,
|
|
@@ -140,3 +141,11 @@ function getMeritUnclaimedRewards(account, network) {
|
|
|
140
141
|
return claimableTokens;
|
|
141
142
|
});
|
|
142
143
|
}
|
|
144
|
+
function getMeritUnclaimedRewards(account, network) {
|
|
145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
const rewards = yield getMerklUnclaimedRewards(account, network);
|
|
147
|
+
return rewards
|
|
148
|
+
.filter(reward => reward.symbol.startsWith('a'))
|
|
149
|
+
.map(reward => (Object.assign(Object.assign({}, reward), { claimType: claiming_1.ClaimType.AAVE_MERIT_REWARDS, label: 'AAVE Merit Rewards' })));
|
|
150
|
+
});
|
|
151
|
+
}
|
package/cjs/fluid/index.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export declare const EMPTY_FLUID_DATA: {
|
|
|
22
22
|
minRatio: string;
|
|
23
23
|
netApy: string;
|
|
24
24
|
incentiveUsd: string;
|
|
25
|
+
merklSupplyIncentives: never[];
|
|
26
|
+
merklBorrowIncentives: never[];
|
|
25
27
|
totalInterestUsd: string;
|
|
26
28
|
isSubscribedToAutomation: boolean;
|
|
27
29
|
automationResubscribeRequired: boolean;
|
|
@@ -127,6 +129,8 @@ export declare const _getUserPositions: (provider: PublicClient, network: Networ
|
|
|
127
129
|
leftToBorrowUsd: string;
|
|
128
130
|
netApy: string;
|
|
129
131
|
incentiveUsd: string;
|
|
132
|
+
merklSupplyIncentives: import("../types").IncentiveData[];
|
|
133
|
+
merklBorrowIncentives: import("../types").IncentiveData[];
|
|
130
134
|
ratio: string;
|
|
131
135
|
collRatio: string;
|
|
132
136
|
minRatio: string;
|
|
@@ -159,6 +163,8 @@ export declare const getUserPositions: (provider: EthereumProvider, network: Net
|
|
|
159
163
|
leftToBorrowUsd: string;
|
|
160
164
|
netApy: string;
|
|
161
165
|
incentiveUsd: string;
|
|
166
|
+
merklSupplyIncentives: import("../types").IncentiveData[];
|
|
167
|
+
merklBorrowIncentives: import("../types").IncentiveData[];
|
|
162
168
|
ratio: string;
|
|
163
169
|
collRatio: string;
|
|
164
170
|
minRatio: string;
|
|
@@ -191,6 +197,8 @@ export declare const _getUserPositionsPortfolio: (provider: PublicClient, networ
|
|
|
191
197
|
leftToBorrowUsd: string;
|
|
192
198
|
netApy: string;
|
|
193
199
|
incentiveUsd: string;
|
|
200
|
+
merklSupplyIncentives: import("../types").IncentiveData[];
|
|
201
|
+
merklBorrowIncentives: import("../types").IncentiveData[];
|
|
194
202
|
ratio: string;
|
|
195
203
|
collRatio: string;
|
|
196
204
|
minRatio: string;
|
package/cjs/fluid/index.js
CHANGED
|
@@ -922,6 +922,8 @@ exports.EMPTY_FLUID_DATA = {
|
|
|
922
922
|
minRatio: '0',
|
|
923
923
|
netApy: '0',
|
|
924
924
|
incentiveUsd: '0',
|
|
925
|
+
merklSupplyIncentives: [],
|
|
926
|
+
merklBorrowIncentives: [],
|
|
925
927
|
totalInterestUsd: '0',
|
|
926
928
|
isSubscribedToAutomation: false,
|
|
927
929
|
automationResubscribeRequired: false,
|
|
@@ -1391,8 +1393,12 @@ const _getUserPositionsPortfolio = (provider, network, user) => __awaiter(void 0
|
|
|
1391
1393
|
tokens.push('ETH');
|
|
1392
1394
|
if (!tokens.includes('WBTC'))
|
|
1393
1395
|
tokens.push('WBTC');
|
|
1394
|
-
const tokenPrices = yield
|
|
1395
|
-
|
|
1396
|
+
const [tokenPrices, merklCampaigns] = yield Promise.all([
|
|
1397
|
+
getTokensPricesForPortfolio(tokens, provider, network),
|
|
1398
|
+
(0, merkl_1.getFluidMerklCampaigns)(network),
|
|
1399
|
+
]);
|
|
1400
|
+
const parsedMarketData = (yield Promise.all(data[1].map((vaultData) => __awaiter(void 0, void 0, void 0, function* () { return parseMarketData(provider, vaultData, network, tokenPrices); }))))
|
|
1401
|
+
.map(marketData => (marketData ? (0, merkl_1.attachFluidMerklIncentives)(marketData, merklCampaigns) : marketData));
|
|
1396
1402
|
const userData = data[0].map((position, i) => (parsedMarketData[i] && Object.assign({}, parseUserData(position, parsedMarketData[i]))));
|
|
1397
1403
|
return parsedMarketData.map((market, i) => ({
|
|
1398
1404
|
marketData: market,
|
package/cjs/fluid/merkl.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import memoize from 'memoizee';
|
|
1
2
|
import { FluidMarketData, FluidMerklRewardMap, MerklOpportunity, NetworkNumber } from '../types';
|
|
2
3
|
/**
|
|
3
4
|
* Merkl tags Fluid vault-scoped reward campaigns via the opportunity `type` field:
|
|
@@ -22,7 +23,7 @@ export declare const buildFluidMerklRewardMap: (opportunities: MerklOpportunity[
|
|
|
22
23
|
* Fetches live vault-scoped Fluid campaigns for the chain, keyed by vault address. Never throws —
|
|
23
24
|
* on any failure it returns an empty map, so Merkl being down can't break market data.
|
|
24
25
|
*/
|
|
25
|
-
export declare const getFluidMerklCampaigns: (chainId: NetworkNumber) => Promise<FluidMerklRewardMap
|
|
26
|
+
export declare const getFluidMerklCampaigns: ((chainId: NetworkNumber) => Promise<FluidMerklRewardMap>) & memoize.Memoized<(chainId: NetworkNumber) => Promise<FluidMerklRewardMap>>;
|
|
26
27
|
/**
|
|
27
28
|
* Appends the vault's Merkl campaigns to its assets' incentive arrays — supply rewards on every
|
|
28
29
|
* suppliable asset and borrow rewards on every borrowable one. Smart collateral/debt vaults hold
|
package/cjs/fluid/merkl.js
CHANGED
|
@@ -8,8 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.attachFluidMerklIncentives = exports.getFluidMerklCampaigns = exports.buildFluidMerklRewardMap = exports.FluidMerklOpportunityType = void 0;
|
|
16
|
+
const memoizee_1 = __importDefault(require("memoizee"));
|
|
13
17
|
const moneymarket_1 = require("../moneymarket");
|
|
14
18
|
const merkl_1 = require("../services/merkl");
|
|
15
19
|
const types_1 = require("../types");
|
|
@@ -41,6 +45,7 @@ const buildMerklIncentive = (opportunity) => {
|
|
|
41
45
|
apy: (0, moneymarket_1.aprToApy)(opportunity.apr),
|
|
42
46
|
token,
|
|
43
47
|
incentiveKind: types_1.IncentiveKind.Reward,
|
|
48
|
+
source: types_1.IncentiveSource.Merkl,
|
|
44
49
|
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
45
50
|
eligibilityId: opportunity.identifier,
|
|
46
51
|
};
|
|
@@ -68,7 +73,7 @@ exports.buildFluidMerklRewardMap = buildFluidMerklRewardMap;
|
|
|
68
73
|
* Fetches live vault-scoped Fluid campaigns for the chain, keyed by vault address. Never throws —
|
|
69
74
|
* on any failure it returns an empty map, so Merkl being down can't break market data.
|
|
70
75
|
*/
|
|
71
|
-
|
|
76
|
+
exports.getFluidMerklCampaigns = (0, memoizee_1.default)((chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
77
|
try {
|
|
73
78
|
const opportunities = yield (0, merkl_1.fetchAllMerklOpportunities)({
|
|
74
79
|
mainProtocolId: 'fluid',
|
|
@@ -81,8 +86,7 @@ const getFluidMerklCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, fu
|
|
|
81
86
|
console.error('Failed to fetch Fluid Merkl campaigns', e);
|
|
82
87
|
return {};
|
|
83
88
|
}
|
|
84
|
-
});
|
|
85
|
-
exports.getFluidMerklCampaigns = getFluidMerklCampaigns;
|
|
89
|
+
}), { promise: true, maxAge: 60 * 1000 });
|
|
86
90
|
/**
|
|
87
91
|
* Appends the vault's Merkl campaigns to its assets' incentive arrays — supply rewards on every
|
|
88
92
|
* suppliable asset and borrow rewards on every borrowable one. Smart collateral/debt vaults hold
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { FluidAggregatedVaultData, FluidAssetData, FluidAssetsData, FluidDexBorrowDataStructOutput, FluidDexSupplyDataStructOutput, FluidUsedAsset, FluidUsedAssets, InnerFluidMarketData } from '../../types';
|
|
1
|
+
import { FluidAggregatedVaultData, FluidAssetData, FluidAssetsData, FluidDexBorrowDataStructOutput, FluidDexSupplyDataStructOutput, FluidUsedAsset, FluidUsedAssets, IncentiveData, InnerFluidMarketData } from '../../types';
|
|
2
|
+
import { IncentiveSide } from '../../types/common';
|
|
3
|
+
export declare const getUniqueFluidMerklIncentives: (assetsData: FluidAssetsData, side: IncentiveSide) => IncentiveData[];
|
|
2
4
|
export declare const getFluidAggregatedData: ({ usedAssets, assetsData, marketData, }: {
|
|
3
5
|
usedAssets: FluidUsedAssets;
|
|
4
6
|
marketData: InnerFluidMarketData;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mergeUsedAssets = exports.EMPTY_USED_ASSET = exports.mergeAssetData = exports.parseDexBorrowData = exports.parseDexSupplyData = exports.getFluidAggregatedData = void 0;
|
|
6
|
+
exports.mergeUsedAssets = exports.EMPTY_USED_ASSET = exports.mergeAssetData = exports.parseDexBorrowData = exports.parseDexSupplyData = exports.getFluidAggregatedData = exports.getUniqueFluidMerklIncentives = void 0;
|
|
7
7
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
8
8
|
const tokens_1 = require("@defisaver/tokens");
|
|
9
9
|
const types_1 = require("../../types");
|
|
@@ -11,13 +11,38 @@ const moneymarket_1 = require("../../moneymarket");
|
|
|
11
11
|
const staking_1 = require("../../staking");
|
|
12
12
|
const common_1 = require("../../types/common");
|
|
13
13
|
const utils_1 = require("../../services/utils");
|
|
14
|
-
const
|
|
14
|
+
const getUniqueFluidMerklIncentives = (assetsData, side) => {
|
|
15
|
+
const field = side === common_1.IncentiveSide.Supply ? 'supplyIncentives' : 'borrowIncentives';
|
|
16
|
+
const incentives = Object.values(assetsData)
|
|
17
|
+
.flatMap(asset => asset[field])
|
|
18
|
+
.filter(incentive => incentive.source === types_1.IncentiveSource.Merkl);
|
|
19
|
+
return [...new Map(incentives.map(incentive => [
|
|
20
|
+
[incentive.eligibilityId, incentive.token, incentive.apy, incentive.description].join('|'),
|
|
21
|
+
incentive,
|
|
22
|
+
])).values()];
|
|
23
|
+
};
|
|
24
|
+
exports.getUniqueFluidMerklIncentives = getUniqueFluidMerklIncentives;
|
|
25
|
+
const getMerklIncentiveInterest = (incentives, principal, usedAssets) => incentives.reduce((total, { apy, eligibilityId }) => {
|
|
26
|
+
const eligibilityCheck = eligibilityId ? staking_1.EligibilityMapping[eligibilityId] : null;
|
|
27
|
+
const { isEligible, eligibleUSDAmount } = eligibilityCheck
|
|
28
|
+
? eligibilityCheck(usedAssets, undefined)
|
|
29
|
+
: { isEligible: true, eligibleUSDAmount: principal };
|
|
30
|
+
const incentiveInterest = isEligible ? (0, staking_1.calculateInterestEarned)(eligibleUSDAmount, apy, 'year', true) : 0;
|
|
31
|
+
return new decimal_js_1.default(total).add(incentiveInterest).toString();
|
|
32
|
+
}, '0');
|
|
33
|
+
const calculateNetApyDex = ({ marketData, assetsData, usedAssets, suppliedUsd, borrowedUsd, }) => {
|
|
15
34
|
const { borrowRate, supplyRate, incentiveBorrowRate, incentiveSupplyRate, tradingBorrowRate, tradingSupplyRate, } = marketData;
|
|
16
35
|
const totalBorrowRate = new decimal_js_1.default(borrowRate).minus(tradingBorrowRate || '0').toString();
|
|
17
36
|
const totalSupplyRate = new decimal_js_1.default(supplyRate).add(tradingSupplyRate || '0').toString();
|
|
18
37
|
const borrowIncentive = new decimal_js_1.default(incentiveBorrowRate || '0').mul(borrowedUsd).div(100).toString();
|
|
19
38
|
const supplyIncentive = new decimal_js_1.default(incentiveSupplyRate || '0').mul(suppliedUsd).div(100).toString();
|
|
20
|
-
const
|
|
39
|
+
const merklSupplyIncentive = getMerklIncentiveInterest((0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Supply), suppliedUsd, usedAssets);
|
|
40
|
+
const merklBorrowIncentive = getMerklIncentiveInterest((0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Borrow), borrowedUsd, usedAssets);
|
|
41
|
+
const incentiveUsd = new decimal_js_1.default(supplyIncentive)
|
|
42
|
+
.minus(borrowIncentive)
|
|
43
|
+
.add(merklSupplyIncentive)
|
|
44
|
+
.add(merklBorrowIncentive)
|
|
45
|
+
.toString();
|
|
21
46
|
const borrowInterest = new decimal_js_1.default(totalBorrowRate).mul(borrowedUsd).div(100).toString();
|
|
22
47
|
const supplyInterest = new decimal_js_1.default(totalSupplyRate).mul(suppliedUsd).div(100).toString();
|
|
23
48
|
const totalInterestUsd = new decimal_js_1.default(supplyInterest).add(incentiveUsd).minus(borrowInterest).toString();
|
|
@@ -38,7 +63,15 @@ const getFluidAggregatedData = ({ usedAssets, assetsData, marketData, }, supplyS
|
|
|
38
63
|
? (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd)
|
|
39
64
|
: new decimal_js_1.default(marketData.debtSharePrice).mul(borrowShares).toString();
|
|
40
65
|
const isDex = [types_1.FluidVaultType.T2, types_1.FluidVaultType.T3, types_1.FluidVaultType.T4].includes(marketData.vaultType);
|
|
41
|
-
|
|
66
|
+
payload.merklSupplyIncentives = (0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Supply);
|
|
67
|
+
payload.merklBorrowIncentives = (0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Borrow);
|
|
68
|
+
const { netApy, incentiveUsd, totalInterestUsd } = isDex ? calculateNetApyDex({
|
|
69
|
+
marketData,
|
|
70
|
+
assetsData,
|
|
71
|
+
usedAssets,
|
|
72
|
+
suppliedUsd: payload.suppliedUsd,
|
|
73
|
+
borrowedUsd: payload.borrowedUsd,
|
|
74
|
+
}) : (0, staking_1.calculateNetApy)({ usedAssets, assetsData: assetsData });
|
|
42
75
|
payload.netApy = netApy;
|
|
43
76
|
payload.incentiveUsd = incentiveUsd;
|
|
44
77
|
payload.totalInterestUsd = totalInterestUsd;
|
package/cjs/portfolio/index.js
CHANGED
|
@@ -134,7 +134,7 @@ function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1)
|
|
|
134
134
|
},
|
|
135
135
|
};
|
|
136
136
|
rewardsData[address.toLowerCase()] = {
|
|
137
|
-
|
|
137
|
+
merkl: {},
|
|
138
138
|
aaveV3: {},
|
|
139
139
|
compV3: {},
|
|
140
140
|
spark: {},
|
|
@@ -355,12 +355,12 @@ function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1)
|
|
|
355
355
|
}))).flat(),
|
|
356
356
|
...addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
|
357
357
|
try {
|
|
358
|
-
const
|
|
359
|
-
rewardsData[address.toLowerCase()].
|
|
358
|
+
const merklData = yield (0, aaveV3_2.getMerklUnclaimedRewards)(address, network);
|
|
359
|
+
rewardsData[address.toLowerCase()].merkl = { error: '', data: merklData };
|
|
360
360
|
}
|
|
361
361
|
catch (error) {
|
|
362
|
-
console.error(`Error fetching
|
|
363
|
-
rewardsData[address.toLowerCase()].
|
|
362
|
+
console.error(`Error fetching Merkl rewards data for address ${address}:`, error);
|
|
363
|
+
rewardsData[address.toLowerCase()].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
364
364
|
}
|
|
365
365
|
})),
|
|
366
366
|
...aaveV3Markets.map(market => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
package/cjs/types/claiming.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare enum ClaimType {
|
|
|
4
4
|
AAVE_REWARDS = "AAVE_REWARDS",
|
|
5
5
|
/** Merit rewards from AAVE (various tokens) */
|
|
6
6
|
AAVE_MERIT_REWARDS = "AAVE_MERIT_REWARDS",
|
|
7
|
+
/** Rewards distributed through Merkl across supported protocols */
|
|
8
|
+
MERKL_REWARDS = "MERKL_REWARDS",
|
|
7
9
|
/** Rewards from Compound V3 (only in COMP) */
|
|
8
10
|
COMPOUND_V3_COMP = "COMPOUND_V3_COMP",
|
|
9
11
|
/** Rewards from Spark (wstETH only for now) */
|
|
@@ -43,6 +45,15 @@ export type AaveMeritRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
|
43
45
|
unclaimed: string;
|
|
44
46
|
};
|
|
45
47
|
};
|
|
48
|
+
export type MerklRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
49
|
+
claimType: ClaimType.MERKL_REWARDS;
|
|
50
|
+
additionalClaimFields: {
|
|
51
|
+
accumulated: string;
|
|
52
|
+
proof: string[];
|
|
53
|
+
decimals: string;
|
|
54
|
+
unclaimed: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
46
57
|
export type SparkRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
47
58
|
claimType: ClaimType.SPARK_REWARDS;
|
|
48
59
|
additionalClaimFields: {
|
|
@@ -91,5 +102,5 @@ export type UniswapAirdropClaimableToken = _ClaimableTokenPartial & {
|
|
|
91
102
|
proof: string[];
|
|
92
103
|
};
|
|
93
104
|
};
|
|
94
|
-
export type ClaimableToken = AaveRewardsClaimableToken | AaveMeritRewardsClaimableToken | CompoundV3CompClaimableToken | SparkRewardsClaimableToken | KingRewardsClaimableToken | SparkAirdropClaimableToken | SparkWstEthRewardsClaimableToken | EthenaAirdropClaimableToken;
|
|
105
|
+
export type ClaimableToken = AaveRewardsClaimableToken | AaveMeritRewardsClaimableToken | MerklRewardsClaimableToken | CompoundV3CompClaimableToken | SparkRewardsClaimableToken | KingRewardsClaimableToken | SparkAirdropClaimableToken | SparkWstEthRewardsClaimableToken | EthenaAirdropClaimableToken;
|
|
95
106
|
export {};
|
package/cjs/types/claiming.js
CHANGED
|
@@ -7,6 +7,8 @@ var ClaimType;
|
|
|
7
7
|
ClaimType["AAVE_REWARDS"] = "AAVE_REWARDS";
|
|
8
8
|
/** Merit rewards from AAVE (various tokens) */
|
|
9
9
|
ClaimType["AAVE_MERIT_REWARDS"] = "AAVE_MERIT_REWARDS";
|
|
10
|
+
/** Rewards distributed through Merkl across supported protocols */
|
|
11
|
+
ClaimType["MERKL_REWARDS"] = "MERKL_REWARDS";
|
|
10
12
|
/** Rewards from Compound V3 (only in COMP) */
|
|
11
13
|
ClaimType["COMPOUND_V3_COMP"] = "COMPOUND_V3_COMP";
|
|
12
14
|
/** Rewards from Spark (wstETH only for now) */
|
package/cjs/types/common.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export declare enum IncentiveSide {
|
|
|
6
6
|
Supply = "supply",
|
|
7
7
|
Borrow = "borrow"
|
|
8
8
|
}
|
|
9
|
+
export declare enum IncentiveSource {
|
|
10
|
+
Merkl = "merkl"
|
|
11
|
+
}
|
|
9
12
|
export declare enum IncentiveEligibilityId {
|
|
10
13
|
AaveV3EthenaLiquidLeverage = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL",
|
|
11
14
|
AaveV3ArbitrumEthSupply = "0x5d16261c6715a653248269861bbacf68a9774cde",
|
|
@@ -25,6 +28,7 @@ export interface IncentiveData {
|
|
|
25
28
|
token: string;
|
|
26
29
|
apy: string;
|
|
27
30
|
incentiveKind?: IncentiveKind;
|
|
31
|
+
source?: IncentiveSource;
|
|
28
32
|
description?: string;
|
|
29
33
|
eligibilityId?: IncentiveEligibilityId;
|
|
30
34
|
}
|
package/cjs/types/common.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NetworkNumber = exports.LeverageType = exports.IncentiveEligibilityId = exports.IncentiveSide = exports.IncentiveKind = void 0;
|
|
3
|
+
exports.NetworkNumber = exports.LeverageType = exports.IncentiveEligibilityId = exports.IncentiveSource = exports.IncentiveSide = exports.IncentiveKind = void 0;
|
|
4
4
|
var IncentiveKind;
|
|
5
5
|
(function (IncentiveKind) {
|
|
6
6
|
IncentiveKind["Staking"] = "staking";
|
|
@@ -11,6 +11,10 @@ var IncentiveSide;
|
|
|
11
11
|
IncentiveSide["Supply"] = "supply";
|
|
12
12
|
IncentiveSide["Borrow"] = "borrow";
|
|
13
13
|
})(IncentiveSide || (exports.IncentiveSide = IncentiveSide = {}));
|
|
14
|
+
var IncentiveSource;
|
|
15
|
+
(function (IncentiveSource) {
|
|
16
|
+
IncentiveSource["Merkl"] = "merkl";
|
|
17
|
+
})(IncentiveSource || (exports.IncentiveSource = IncentiveSource = {}));
|
|
14
18
|
var IncentiveEligibilityId;
|
|
15
19
|
(function (IncentiveEligibilityId) {
|
|
16
20
|
IncentiveEligibilityId["AaveV3EthenaLiquidLeverage"] = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL";
|
package/cjs/types/fluid.d.ts
CHANGED
|
@@ -319,6 +319,8 @@ export interface FluidAggregatedVaultData {
|
|
|
319
319
|
leftToBorrowUsd: string;
|
|
320
320
|
netApy: string;
|
|
321
321
|
incentiveUsd: string;
|
|
322
|
+
merklSupplyIncentives: IncentiveData[];
|
|
323
|
+
merklBorrowIncentives: IncentiveData[];
|
|
322
324
|
ratio: string;
|
|
323
325
|
collRatio: string;
|
|
324
326
|
minRatio: string;
|
package/esm/claiming/aaveV3.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import { EthAddress, NetworkNumber } from '../types';
|
|
3
|
-
import { ClaimableToken } from '../types/claiming';
|
|
3
|
+
import { ClaimableToken, MerklRewardsClaimableToken } from '../types/claiming';
|
|
4
4
|
export declare function getUnclaimedRewardsForAllMarkets(provider: Client, network: NetworkNumber, walletAddress: EthAddress, marketAddress: EthAddress): Promise<ClaimableToken[]>;
|
|
5
|
+
export declare function getMerklUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<MerklRewardsClaimableToken[]>;
|
|
5
6
|
export declare function getMeritUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<ClaimableToken[]>;
|
package/esm/claiming/aaveV3.js
CHANGED
|
@@ -88,7 +88,7 @@ export function getUnclaimedRewardsForAllMarkets(provider, network, walletAddres
|
|
|
88
88
|
return mapAaveRewardsToClaimableTokens(Object.values(totalUnclaimedPerRewardToken), marketAddress, walletAddress);
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
export function
|
|
91
|
+
export function getMerklUnclaimedRewards(account, network) {
|
|
92
92
|
return __awaiter(this, void 0, void 0, function* () {
|
|
93
93
|
let data;
|
|
94
94
|
try {
|
|
@@ -96,7 +96,7 @@ export function getMeritUnclaimedRewards(account, network) {
|
|
|
96
96
|
data = yield res.json();
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|
|
99
|
-
console.error('External API Failure:
|
|
99
|
+
console.error('External API Failure: Merkl rewards', error);
|
|
100
100
|
data = [];
|
|
101
101
|
}
|
|
102
102
|
const claimableTokens = [];
|
|
@@ -114,12 +114,12 @@ export function getMeritUnclaimedRewards(account, network) {
|
|
|
114
114
|
return;
|
|
115
115
|
const unclaimed = getEthAmountForDecimals(unclaimedAmount, token.decimals);
|
|
116
116
|
claimableTokens.push({
|
|
117
|
-
claimType: ClaimType.
|
|
117
|
+
claimType: ClaimType.MERKL_REWARDS,
|
|
118
118
|
amount: unclaimed,
|
|
119
119
|
symbol: token.symbol,
|
|
120
120
|
tokenAddress: token.address,
|
|
121
121
|
walletAddress: account,
|
|
122
|
-
label: '
|
|
122
|
+
label: 'Merkl Rewards',
|
|
123
123
|
underlyingSymbol: getAaveUnderlyingSymbol(token.symbol),
|
|
124
124
|
additionalClaimFields: {
|
|
125
125
|
accumulated: amount,
|
|
@@ -133,3 +133,11 @@ export function getMeritUnclaimedRewards(account, network) {
|
|
|
133
133
|
return claimableTokens;
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
+
export function getMeritUnclaimedRewards(account, network) {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
const rewards = yield getMerklUnclaimedRewards(account, network);
|
|
139
|
+
return rewards
|
|
140
|
+
.filter(reward => reward.symbol.startsWith('a'))
|
|
141
|
+
.map(reward => (Object.assign(Object.assign({}, reward), { claimType: ClaimType.AAVE_MERIT_REWARDS, label: 'AAVE Merit Rewards' })));
|
|
142
|
+
});
|
|
143
|
+
}
|
package/esm/fluid/index.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export declare const EMPTY_FLUID_DATA: {
|
|
|
22
22
|
minRatio: string;
|
|
23
23
|
netApy: string;
|
|
24
24
|
incentiveUsd: string;
|
|
25
|
+
merklSupplyIncentives: never[];
|
|
26
|
+
merklBorrowIncentives: never[];
|
|
25
27
|
totalInterestUsd: string;
|
|
26
28
|
isSubscribedToAutomation: boolean;
|
|
27
29
|
automationResubscribeRequired: boolean;
|
|
@@ -127,6 +129,8 @@ export declare const _getUserPositions: (provider: PublicClient, network: Networ
|
|
|
127
129
|
leftToBorrowUsd: string;
|
|
128
130
|
netApy: string;
|
|
129
131
|
incentiveUsd: string;
|
|
132
|
+
merklSupplyIncentives: import("../types").IncentiveData[];
|
|
133
|
+
merklBorrowIncentives: import("../types").IncentiveData[];
|
|
130
134
|
ratio: string;
|
|
131
135
|
collRatio: string;
|
|
132
136
|
minRatio: string;
|
|
@@ -159,6 +163,8 @@ export declare const getUserPositions: (provider: EthereumProvider, network: Net
|
|
|
159
163
|
leftToBorrowUsd: string;
|
|
160
164
|
netApy: string;
|
|
161
165
|
incentiveUsd: string;
|
|
166
|
+
merklSupplyIncentives: import("../types").IncentiveData[];
|
|
167
|
+
merklBorrowIncentives: import("../types").IncentiveData[];
|
|
162
168
|
ratio: string;
|
|
163
169
|
collRatio: string;
|
|
164
170
|
minRatio: string;
|
|
@@ -191,6 +197,8 @@ export declare const _getUserPositionsPortfolio: (provider: PublicClient, networ
|
|
|
191
197
|
leftToBorrowUsd: string;
|
|
192
198
|
netApy: string;
|
|
193
199
|
incentiveUsd: string;
|
|
200
|
+
merklSupplyIncentives: import("../types").IncentiveData[];
|
|
201
|
+
merklBorrowIncentives: import("../types").IncentiveData[];
|
|
194
202
|
ratio: string;
|
|
195
203
|
collRatio: string;
|
|
196
204
|
minRatio: string;
|
package/esm/fluid/index.js
CHANGED
|
@@ -912,6 +912,8 @@ export const EMPTY_FLUID_DATA = {
|
|
|
912
912
|
minRatio: '0',
|
|
913
913
|
netApy: '0',
|
|
914
914
|
incentiveUsd: '0',
|
|
915
|
+
merklSupplyIncentives: [],
|
|
916
|
+
merklBorrowIncentives: [],
|
|
915
917
|
totalInterestUsd: '0',
|
|
916
918
|
isSubscribedToAutomation: false,
|
|
917
919
|
automationResubscribeRequired: false,
|
|
@@ -1363,8 +1365,12 @@ export const _getUserPositionsPortfolio = (provider, network, user) => __awaiter
|
|
|
1363
1365
|
tokens.push('ETH');
|
|
1364
1366
|
if (!tokens.includes('WBTC'))
|
|
1365
1367
|
tokens.push('WBTC');
|
|
1366
|
-
const tokenPrices = yield
|
|
1367
|
-
|
|
1368
|
+
const [tokenPrices, merklCampaigns] = yield Promise.all([
|
|
1369
|
+
getTokensPricesForPortfolio(tokens, provider, network),
|
|
1370
|
+
getFluidMerklCampaigns(network),
|
|
1371
|
+
]);
|
|
1372
|
+
const parsedMarketData = (yield Promise.all(data[1].map((vaultData) => __awaiter(void 0, void 0, void 0, function* () { return parseMarketData(provider, vaultData, network, tokenPrices); }))))
|
|
1373
|
+
.map(marketData => (marketData ? attachFluidMerklIncentives(marketData, merklCampaigns) : marketData));
|
|
1368
1374
|
const userData = data[0].map((position, i) => (parsedMarketData[i] && Object.assign({}, parseUserData(position, parsedMarketData[i]))));
|
|
1369
1375
|
return parsedMarketData.map((market, i) => ({
|
|
1370
1376
|
marketData: market,
|
package/esm/fluid/merkl.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import memoize from 'memoizee';
|
|
1
2
|
import { FluidMarketData, FluidMerklRewardMap, MerklOpportunity, NetworkNumber } from '../types';
|
|
2
3
|
/**
|
|
3
4
|
* Merkl tags Fluid vault-scoped reward campaigns via the opportunity `type` field:
|
|
@@ -22,7 +23,7 @@ export declare const buildFluidMerklRewardMap: (opportunities: MerklOpportunity[
|
|
|
22
23
|
* Fetches live vault-scoped Fluid campaigns for the chain, keyed by vault address. Never throws —
|
|
23
24
|
* on any failure it returns an empty map, so Merkl being down can't break market data.
|
|
24
25
|
*/
|
|
25
|
-
export declare const getFluidMerklCampaigns: (chainId: NetworkNumber) => Promise<FluidMerklRewardMap
|
|
26
|
+
export declare const getFluidMerklCampaigns: ((chainId: NetworkNumber) => Promise<FluidMerklRewardMap>) & memoize.Memoized<(chainId: NetworkNumber) => Promise<FluidMerklRewardMap>>;
|
|
26
27
|
/**
|
|
27
28
|
* Appends the vault's Merkl campaigns to its assets' incentive arrays — supply rewards on every
|
|
28
29
|
* suppliable asset and borrow rewards on every borrowable one. Smart collateral/debt vaults hold
|
package/esm/fluid/merkl.js
CHANGED
|
@@ -7,9 +7,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import memoize from 'memoizee';
|
|
10
11
|
import { aprToApy } from '../moneymarket';
|
|
11
12
|
import { fetchAllMerklOpportunities } from '../services/merkl';
|
|
12
|
-
import { IncentiveKind, OpportunityStatus, } from '../types';
|
|
13
|
+
import { IncentiveKind, IncentiveSource, OpportunityStatus, } from '../types';
|
|
13
14
|
/**
|
|
14
15
|
* Merkl tags Fluid vault-scoped reward campaigns via the opportunity `type` field:
|
|
15
16
|
* - FLUIDVAULT_COLLATERAL → reward for supplying collateral to the vault
|
|
@@ -38,6 +39,7 @@ const buildMerklIncentive = (opportunity) => {
|
|
|
38
39
|
apy: aprToApy(opportunity.apr),
|
|
39
40
|
token,
|
|
40
41
|
incentiveKind: IncentiveKind.Reward,
|
|
42
|
+
source: IncentiveSource.Merkl,
|
|
41
43
|
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
42
44
|
eligibilityId: opportunity.identifier,
|
|
43
45
|
};
|
|
@@ -64,7 +66,7 @@ export const buildFluidMerklRewardMap = (opportunities, chainId) => {
|
|
|
64
66
|
* Fetches live vault-scoped Fluid campaigns for the chain, keyed by vault address. Never throws —
|
|
65
67
|
* on any failure it returns an empty map, so Merkl being down can't break market data.
|
|
66
68
|
*/
|
|
67
|
-
export const getFluidMerklCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
export const getFluidMerklCampaigns = memoize((chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
70
|
try {
|
|
69
71
|
const opportunities = yield fetchAllMerklOpportunities({
|
|
70
72
|
mainProtocolId: 'fluid',
|
|
@@ -77,7 +79,7 @@ export const getFluidMerklCampaigns = (chainId) => __awaiter(void 0, void 0, voi
|
|
|
77
79
|
console.error('Failed to fetch Fluid Merkl campaigns', e);
|
|
78
80
|
return {};
|
|
79
81
|
}
|
|
80
|
-
});
|
|
82
|
+
}), { promise: true, maxAge: 60 * 1000 });
|
|
81
83
|
/**
|
|
82
84
|
* Appends the vault's Merkl campaigns to its assets' incentive arrays — supply rewards on every
|
|
83
85
|
* suppliable asset and borrow rewards on every borrowable one. Smart collateral/debt vaults hold
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { FluidAggregatedVaultData, FluidAssetData, FluidAssetsData, FluidDexBorrowDataStructOutput, FluidDexSupplyDataStructOutput, FluidUsedAsset, FluidUsedAssets, InnerFluidMarketData } from '../../types';
|
|
1
|
+
import { FluidAggregatedVaultData, FluidAssetData, FluidAssetsData, FluidDexBorrowDataStructOutput, FluidDexSupplyDataStructOutput, FluidUsedAsset, FluidUsedAssets, IncentiveData, InnerFluidMarketData } from '../../types';
|
|
2
|
+
import { IncentiveSide } from '../../types/common';
|
|
3
|
+
export declare const getUniqueFluidMerklIncentives: (assetsData: FluidAssetsData, side: IncentiveSide) => IncentiveData[];
|
|
2
4
|
export declare const getFluidAggregatedData: ({ usedAssets, assetsData, marketData, }: {
|
|
3
5
|
usedAssets: FluidUsedAssets;
|
|
4
6
|
marketData: InnerFluidMarketData;
|
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import Dec from 'decimal.js';
|
|
2
2
|
import { assetAmountInEth } from '@defisaver/tokens';
|
|
3
|
-
import { FluidVaultType, } from '../../types';
|
|
3
|
+
import { FluidVaultType, IncentiveSource, } from '../../types';
|
|
4
4
|
import { calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos, } from '../../moneymarket';
|
|
5
|
-
import { calculateNetApy } from '../../staking';
|
|
6
|
-
import { LeverageType } from '../../types/common';
|
|
5
|
+
import { calculateInterestEarned, calculateNetApy, EligibilityMapping } from '../../staking';
|
|
6
|
+
import { IncentiveSide, LeverageType } from '../../types/common';
|
|
7
7
|
import { getEthAmountForDecimals } from '../../services/utils';
|
|
8
|
-
const
|
|
8
|
+
export const getUniqueFluidMerklIncentives = (assetsData, side) => {
|
|
9
|
+
const field = side === IncentiveSide.Supply ? 'supplyIncentives' : 'borrowIncentives';
|
|
10
|
+
const incentives = Object.values(assetsData)
|
|
11
|
+
.flatMap(asset => asset[field])
|
|
12
|
+
.filter(incentive => incentive.source === IncentiveSource.Merkl);
|
|
13
|
+
return [...new Map(incentives.map(incentive => [
|
|
14
|
+
[incentive.eligibilityId, incentive.token, incentive.apy, incentive.description].join('|'),
|
|
15
|
+
incentive,
|
|
16
|
+
])).values()];
|
|
17
|
+
};
|
|
18
|
+
const getMerklIncentiveInterest = (incentives, principal, usedAssets) => incentives.reduce((total, { apy, eligibilityId }) => {
|
|
19
|
+
const eligibilityCheck = eligibilityId ? EligibilityMapping[eligibilityId] : null;
|
|
20
|
+
const { isEligible, eligibleUSDAmount } = eligibilityCheck
|
|
21
|
+
? eligibilityCheck(usedAssets, undefined)
|
|
22
|
+
: { isEligible: true, eligibleUSDAmount: principal };
|
|
23
|
+
const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : 0;
|
|
24
|
+
return new Dec(total).add(incentiveInterest).toString();
|
|
25
|
+
}, '0');
|
|
26
|
+
const calculateNetApyDex = ({ marketData, assetsData, usedAssets, suppliedUsd, borrowedUsd, }) => {
|
|
9
27
|
const { borrowRate, supplyRate, incentiveBorrowRate, incentiveSupplyRate, tradingBorrowRate, tradingSupplyRate, } = marketData;
|
|
10
28
|
const totalBorrowRate = new Dec(borrowRate).minus(tradingBorrowRate || '0').toString();
|
|
11
29
|
const totalSupplyRate = new Dec(supplyRate).add(tradingSupplyRate || '0').toString();
|
|
12
30
|
const borrowIncentive = new Dec(incentiveBorrowRate || '0').mul(borrowedUsd).div(100).toString();
|
|
13
31
|
const supplyIncentive = new Dec(incentiveSupplyRate || '0').mul(suppliedUsd).div(100).toString();
|
|
14
|
-
const
|
|
32
|
+
const merklSupplyIncentive = getMerklIncentiveInterest(getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Supply), suppliedUsd, usedAssets);
|
|
33
|
+
const merklBorrowIncentive = getMerklIncentiveInterest(getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Borrow), borrowedUsd, usedAssets);
|
|
34
|
+
const incentiveUsd = new Dec(supplyIncentive)
|
|
35
|
+
.minus(borrowIncentive)
|
|
36
|
+
.add(merklSupplyIncentive)
|
|
37
|
+
.add(merklBorrowIncentive)
|
|
38
|
+
.toString();
|
|
15
39
|
const borrowInterest = new Dec(totalBorrowRate).mul(borrowedUsd).div(100).toString();
|
|
16
40
|
const supplyInterest = new Dec(totalSupplyRate).mul(suppliedUsd).div(100).toString();
|
|
17
41
|
const totalInterestUsd = new Dec(supplyInterest).add(incentiveUsd).minus(borrowInterest).toString();
|
|
@@ -32,7 +56,15 @@ export const getFluidAggregatedData = ({ usedAssets, assetsData, marketData, },
|
|
|
32
56
|
? getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd)
|
|
33
57
|
: new Dec(marketData.debtSharePrice).mul(borrowShares).toString();
|
|
34
58
|
const isDex = [FluidVaultType.T2, FluidVaultType.T3, FluidVaultType.T4].includes(marketData.vaultType);
|
|
35
|
-
|
|
59
|
+
payload.merklSupplyIncentives = getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Supply);
|
|
60
|
+
payload.merklBorrowIncentives = getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Borrow);
|
|
61
|
+
const { netApy, incentiveUsd, totalInterestUsd } = isDex ? calculateNetApyDex({
|
|
62
|
+
marketData,
|
|
63
|
+
assetsData,
|
|
64
|
+
usedAssets,
|
|
65
|
+
suppliedUsd: payload.suppliedUsd,
|
|
66
|
+
borrowedUsd: payload.borrowedUsd,
|
|
67
|
+
}) : calculateNetApy({ usedAssets, assetsData: assetsData });
|
|
36
68
|
payload.netApy = netApy;
|
|
37
69
|
payload.incentiveUsd = incentiveUsd;
|
|
38
70
|
payload.totalInterestUsd = totalInterestUsd;
|
package/esm/portfolio/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
|
|
|
26
26
|
import { _getLiquityV2MarketData, getLiquitySAndYBold, getLiquityV2Staking } from '../liquityV2';
|
|
27
27
|
import { _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
|
|
28
28
|
import { getUmbrellaData } from '../umbrella';
|
|
29
|
-
import {
|
|
29
|
+
import { getMerklUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
|
|
30
30
|
import { getCompoundV3Rewards } from '../claiming/compV3';
|
|
31
31
|
import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
32
32
|
import { getKingRewards } from '../claiming/king';
|
|
@@ -114,7 +114,7 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
|
|
|
114
114
|
},
|
|
115
115
|
};
|
|
116
116
|
rewardsData[address.toLowerCase()] = {
|
|
117
|
-
|
|
117
|
+
merkl: {},
|
|
118
118
|
aaveV3: {},
|
|
119
119
|
compV3: {},
|
|
120
120
|
spark: {},
|
|
@@ -335,12 +335,12 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
|
|
|
335
335
|
}))).flat(),
|
|
336
336
|
...addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
|
337
337
|
try {
|
|
338
|
-
const
|
|
339
|
-
rewardsData[address.toLowerCase()].
|
|
338
|
+
const merklData = yield getMerklUnclaimedRewards(address, network);
|
|
339
|
+
rewardsData[address.toLowerCase()].merkl = { error: '', data: merklData };
|
|
340
340
|
}
|
|
341
341
|
catch (error) {
|
|
342
|
-
console.error(`Error fetching
|
|
343
|
-
rewardsData[address.toLowerCase()].
|
|
342
|
+
console.error(`Error fetching Merkl rewards data for address ${address}:`, error);
|
|
343
|
+
rewardsData[address.toLowerCase()].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
344
344
|
}
|
|
345
345
|
})),
|
|
346
346
|
...aaveV3Markets.map(market => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
package/esm/types/claiming.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare enum ClaimType {
|
|
|
4
4
|
AAVE_REWARDS = "AAVE_REWARDS",
|
|
5
5
|
/** Merit rewards from AAVE (various tokens) */
|
|
6
6
|
AAVE_MERIT_REWARDS = "AAVE_MERIT_REWARDS",
|
|
7
|
+
/** Rewards distributed through Merkl across supported protocols */
|
|
8
|
+
MERKL_REWARDS = "MERKL_REWARDS",
|
|
7
9
|
/** Rewards from Compound V3 (only in COMP) */
|
|
8
10
|
COMPOUND_V3_COMP = "COMPOUND_V3_COMP",
|
|
9
11
|
/** Rewards from Spark (wstETH only for now) */
|
|
@@ -43,6 +45,15 @@ export type AaveMeritRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
|
43
45
|
unclaimed: string;
|
|
44
46
|
};
|
|
45
47
|
};
|
|
48
|
+
export type MerklRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
49
|
+
claimType: ClaimType.MERKL_REWARDS;
|
|
50
|
+
additionalClaimFields: {
|
|
51
|
+
accumulated: string;
|
|
52
|
+
proof: string[];
|
|
53
|
+
decimals: string;
|
|
54
|
+
unclaimed: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
46
57
|
export type SparkRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
47
58
|
claimType: ClaimType.SPARK_REWARDS;
|
|
48
59
|
additionalClaimFields: {
|
|
@@ -91,5 +102,5 @@ export type UniswapAirdropClaimableToken = _ClaimableTokenPartial & {
|
|
|
91
102
|
proof: string[];
|
|
92
103
|
};
|
|
93
104
|
};
|
|
94
|
-
export type ClaimableToken = AaveRewardsClaimableToken | AaveMeritRewardsClaimableToken | CompoundV3CompClaimableToken | SparkRewardsClaimableToken | KingRewardsClaimableToken | SparkAirdropClaimableToken | SparkWstEthRewardsClaimableToken | EthenaAirdropClaimableToken;
|
|
105
|
+
export type ClaimableToken = AaveRewardsClaimableToken | AaveMeritRewardsClaimableToken | MerklRewardsClaimableToken | CompoundV3CompClaimableToken | SparkRewardsClaimableToken | KingRewardsClaimableToken | SparkAirdropClaimableToken | SparkWstEthRewardsClaimableToken | EthenaAirdropClaimableToken;
|
|
95
106
|
export {};
|
package/esm/types/claiming.js
CHANGED
|
@@ -4,6 +4,8 @@ export var ClaimType;
|
|
|
4
4
|
ClaimType["AAVE_REWARDS"] = "AAVE_REWARDS";
|
|
5
5
|
/** Merit rewards from AAVE (various tokens) */
|
|
6
6
|
ClaimType["AAVE_MERIT_REWARDS"] = "AAVE_MERIT_REWARDS";
|
|
7
|
+
/** Rewards distributed through Merkl across supported protocols */
|
|
8
|
+
ClaimType["MERKL_REWARDS"] = "MERKL_REWARDS";
|
|
7
9
|
/** Rewards from Compound V3 (only in COMP) */
|
|
8
10
|
ClaimType["COMPOUND_V3_COMP"] = "COMPOUND_V3_COMP";
|
|
9
11
|
/** Rewards from Spark (wstETH only for now) */
|
package/esm/types/common.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export declare enum IncentiveSide {
|
|
|
6
6
|
Supply = "supply",
|
|
7
7
|
Borrow = "borrow"
|
|
8
8
|
}
|
|
9
|
+
export declare enum IncentiveSource {
|
|
10
|
+
Merkl = "merkl"
|
|
11
|
+
}
|
|
9
12
|
export declare enum IncentiveEligibilityId {
|
|
10
13
|
AaveV3EthenaLiquidLeverage = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL",
|
|
11
14
|
AaveV3ArbitrumEthSupply = "0x5d16261c6715a653248269861bbacf68a9774cde",
|
|
@@ -25,6 +28,7 @@ export interface IncentiveData {
|
|
|
25
28
|
token: string;
|
|
26
29
|
apy: string;
|
|
27
30
|
incentiveKind?: IncentiveKind;
|
|
31
|
+
source?: IncentiveSource;
|
|
28
32
|
description?: string;
|
|
29
33
|
eligibilityId?: IncentiveEligibilityId;
|
|
30
34
|
}
|
package/esm/types/common.js
CHANGED
|
@@ -8,6 +8,10 @@ export var IncentiveSide;
|
|
|
8
8
|
IncentiveSide["Supply"] = "supply";
|
|
9
9
|
IncentiveSide["Borrow"] = "borrow";
|
|
10
10
|
})(IncentiveSide || (IncentiveSide = {}));
|
|
11
|
+
export var IncentiveSource;
|
|
12
|
+
(function (IncentiveSource) {
|
|
13
|
+
IncentiveSource["Merkl"] = "merkl";
|
|
14
|
+
})(IncentiveSource || (IncentiveSource = {}));
|
|
11
15
|
export var IncentiveEligibilityId;
|
|
12
16
|
(function (IncentiveEligibilityId) {
|
|
13
17
|
IncentiveEligibilityId["AaveV3EthenaLiquidLeverage"] = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL";
|
package/esm/types/fluid.d.ts
CHANGED
|
@@ -319,6 +319,8 @@ export interface FluidAggregatedVaultData {
|
|
|
319
319
|
leftToBorrowUsd: string;
|
|
320
320
|
netApy: string;
|
|
321
321
|
incentiveUsd: string;
|
|
322
|
+
merklSupplyIncentives: IncentiveData[];
|
|
323
|
+
merklBorrowIncentives: IncentiveData[];
|
|
322
324
|
ratio: string;
|
|
323
325
|
collRatio: string;
|
|
324
326
|
minRatio: string;
|
package/package.json
CHANGED
package/src/claiming/aaveV3.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import Dec from 'decimal.js';
|
|
3
3
|
import { EthAddress, NetworkNumber } from '../types';
|
|
4
|
-
import { ClaimableToken, ClaimType } from '../types/claiming';
|
|
4
|
+
import { ClaimableToken, ClaimType, MerklRewardsClaimableToken } from '../types/claiming';
|
|
5
5
|
import {
|
|
6
6
|
AaveIncentiveDataProviderV3ContractViem,
|
|
7
7
|
AaveRewardsControllerViem,
|
|
@@ -103,18 +103,18 @@ export async function getUnclaimedRewardsForAllMarkets(
|
|
|
103
103
|
return mapAaveRewardsToClaimableTokens(Object.values(totalUnclaimedPerRewardToken), marketAddress, walletAddress);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
export async function
|
|
106
|
+
export async function getMerklUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<MerklRewardsClaimableToken[]> {
|
|
107
107
|
let data;
|
|
108
108
|
try {
|
|
109
109
|
const res = await fetch(`https://fe.defisaver.com/api/merkl/get-user-rewards/${account}?chainId=${network}`,
|
|
110
110
|
{ signal: AbortSignal.timeout(LONGER_TIMEOUT) });
|
|
111
111
|
data = await res.json();
|
|
112
112
|
} catch (error) {
|
|
113
|
-
console.error('External API Failure:
|
|
113
|
+
console.error('External API Failure: Merkl rewards', error);
|
|
114
114
|
data = [];
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
const claimableTokens:
|
|
117
|
+
const claimableTokens: MerklRewardsClaimableToken[] = [];
|
|
118
118
|
|
|
119
119
|
// Merkl (or our proxy, on a non-2xx) can return a non-array error body; `fetch` doesn't throw on
|
|
120
120
|
// HTTP errors, so guard before iterating. Mirrors the app-side getMeritUnclaimedRewards.
|
|
@@ -137,12 +137,12 @@ export async function getMeritUnclaimedRewards(account: EthAddress, network: Net
|
|
|
137
137
|
const unclaimed = getEthAmountForDecimals(unclaimedAmount, token.decimals);
|
|
138
138
|
|
|
139
139
|
claimableTokens.push({
|
|
140
|
-
claimType: ClaimType.
|
|
140
|
+
claimType: ClaimType.MERKL_REWARDS,
|
|
141
141
|
amount: unclaimed,
|
|
142
142
|
symbol: token.symbol,
|
|
143
143
|
tokenAddress: token.address,
|
|
144
144
|
walletAddress: account,
|
|
145
|
-
label: '
|
|
145
|
+
label: 'Merkl Rewards',
|
|
146
146
|
underlyingSymbol: getAaveUnderlyingSymbol(token.symbol),
|
|
147
147
|
additionalClaimFields: {
|
|
148
148
|
accumulated: amount,
|
|
@@ -155,4 +155,16 @@ export async function getMeritUnclaimedRewards(account: EthAddress, network: Net
|
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
return claimableTokens;
|
|
158
|
-
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function getMeritUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<ClaimableToken[]> {
|
|
161
|
+
const rewards = await getMerklUnclaimedRewards(account, network);
|
|
162
|
+
|
|
163
|
+
return rewards
|
|
164
|
+
.filter(reward => reward.symbol.startsWith('a'))
|
|
165
|
+
.map(reward => ({
|
|
166
|
+
...reward,
|
|
167
|
+
claimType: ClaimType.AAVE_MERIT_REWARDS,
|
|
168
|
+
label: 'AAVE Merit Rewards',
|
|
169
|
+
}));
|
|
170
|
+
}
|
package/src/fluid/index.ts
CHANGED
|
@@ -1154,6 +1154,8 @@ export const EMPTY_FLUID_DATA = {
|
|
|
1154
1154
|
minRatio: '0',
|
|
1155
1155
|
netApy: '0',
|
|
1156
1156
|
incentiveUsd: '0',
|
|
1157
|
+
merklSupplyIncentives: [],
|
|
1158
|
+
merklBorrowIncentives: [],
|
|
1157
1159
|
totalInterestUsd: '0',
|
|
1158
1160
|
isSubscribedToAutomation: false,
|
|
1159
1161
|
automationResubscribeRequired: false,
|
|
@@ -1809,9 +1811,13 @@ export const _getUserPositionsPortfolio = async (provider: PublicClient, network
|
|
|
1809
1811
|
if (!tokens.includes('ETH')) tokens.push('ETH');
|
|
1810
1812
|
if (!tokens.includes('WBTC')) tokens.push('WBTC');
|
|
1811
1813
|
|
|
1812
|
-
const tokenPrices = await
|
|
1814
|
+
const [tokenPrices, merklCampaigns] = await Promise.all([
|
|
1815
|
+
getTokensPricesForPortfolio(tokens, provider, network),
|
|
1816
|
+
getFluidMerklCampaigns(network),
|
|
1817
|
+
]);
|
|
1813
1818
|
|
|
1814
|
-
const parsedMarketData = (await Promise.all(data[1].map(async (vaultData) => parseMarketData(provider, vaultData, network, tokenPrices))))
|
|
1819
|
+
const parsedMarketData = (await Promise.all(data[1].map(async (vaultData) => parseMarketData(provider, vaultData, network, tokenPrices))))
|
|
1820
|
+
.map(marketData => (marketData ? attachFluidMerklIncentives(marketData, merklCampaigns) : marketData));
|
|
1815
1821
|
|
|
1816
1822
|
const userData = data[0].map((position, i) => (parsedMarketData[i] && { ...parseUserData(position, parsedMarketData[i]) }));
|
|
1817
1823
|
|
package/src/fluid/merkl.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import memoize from 'memoizee';
|
|
1
2
|
import { aprToApy } from '../moneymarket';
|
|
2
3
|
import { fetchAllMerklOpportunities } from '../services/merkl';
|
|
3
4
|
import {
|
|
@@ -6,6 +7,7 @@ import {
|
|
|
6
7
|
IncentiveData,
|
|
7
8
|
IncentiveEligibilityId,
|
|
8
9
|
IncentiveKind,
|
|
10
|
+
IncentiveSource,
|
|
9
11
|
MerklOpportunity,
|
|
10
12
|
NetworkNumber,
|
|
11
13
|
OpportunityStatus,
|
|
@@ -39,6 +41,7 @@ const buildMerklIncentive = (opportunity: MerklOpportunity): IncentiveData => {
|
|
|
39
41
|
apy: aprToApy(opportunity.apr),
|
|
40
42
|
token,
|
|
41
43
|
incentiveKind: IncentiveKind.Reward,
|
|
44
|
+
source: IncentiveSource.Merkl,
|
|
42
45
|
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
43
46
|
eligibilityId: opportunity.identifier as IncentiveEligibilityId,
|
|
44
47
|
};
|
|
@@ -66,7 +69,7 @@ export const buildFluidMerklRewardMap = (opportunities: MerklOpportunity[], chai
|
|
|
66
69
|
* Fetches live vault-scoped Fluid campaigns for the chain, keyed by vault address. Never throws —
|
|
67
70
|
* on any failure it returns an empty map, so Merkl being down can't break market data.
|
|
68
71
|
*/
|
|
69
|
-
export const getFluidMerklCampaigns = async (chainId: NetworkNumber): Promise<FluidMerklRewardMap> => {
|
|
72
|
+
export const getFluidMerklCampaigns = memoize(async (chainId: NetworkNumber): Promise<FluidMerklRewardMap> => {
|
|
70
73
|
try {
|
|
71
74
|
const opportunities = await fetchAllMerklOpportunities({
|
|
72
75
|
mainProtocolId: 'fluid',
|
|
@@ -78,7 +81,7 @@ export const getFluidMerklCampaigns = async (chainId: NetworkNumber): Promise<Fl
|
|
|
78
81
|
console.error('Failed to fetch Fluid Merkl campaigns', e);
|
|
79
82
|
return {};
|
|
80
83
|
}
|
|
81
|
-
};
|
|
84
|
+
}, { promise: true, maxAge: 60 * 1000 });
|
|
82
85
|
|
|
83
86
|
/**
|
|
84
87
|
* Appends the vault's Merkl campaigns to its assets' incentive arrays — supply rewards on every
|
|
@@ -5,16 +5,56 @@ import {
|
|
|
5
5
|
FluidAssetsData, FluidDexBorrowDataStructOutput, FluidDexSupplyDataStructOutput, FluidUsedAsset,
|
|
6
6
|
FluidUsedAssets,
|
|
7
7
|
FluidVaultType,
|
|
8
|
+
IncentiveData,
|
|
9
|
+
IncentiveSource,
|
|
8
10
|
InnerFluidMarketData,
|
|
9
11
|
} from '../../types';
|
|
10
12
|
import {
|
|
11
13
|
calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos,
|
|
12
14
|
} from '../../moneymarket';
|
|
13
|
-
import { calculateNetApy } from '../../staking';
|
|
14
|
-
import { LeverageType, MMAssetsData } from '../../types/common';
|
|
15
|
+
import { calculateInterestEarned, calculateNetApy, EligibilityMapping } from '../../staking';
|
|
16
|
+
import { IncentiveSide, LeverageType, MMAssetsData } from '../../types/common';
|
|
15
17
|
import { getEthAmountForDecimals } from '../../services/utils';
|
|
16
18
|
|
|
17
|
-
const
|
|
19
|
+
export const getUniqueFluidMerklIncentives = (assetsData: FluidAssetsData, side: IncentiveSide): IncentiveData[] => {
|
|
20
|
+
const field = side === IncentiveSide.Supply ? 'supplyIncentives' : 'borrowIncentives';
|
|
21
|
+
const incentives = Object.values(assetsData)
|
|
22
|
+
.flatMap(asset => asset[field])
|
|
23
|
+
.filter(incentive => incentive.source === IncentiveSource.Merkl);
|
|
24
|
+
|
|
25
|
+
return [...new Map(incentives.map(incentive => [
|
|
26
|
+
[incentive.eligibilityId, incentive.token, incentive.apy, incentive.description].join('|'),
|
|
27
|
+
incentive,
|
|
28
|
+
])).values()];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const getMerklIncentiveInterest = (
|
|
32
|
+
incentives: IncentiveData[],
|
|
33
|
+
principal: string,
|
|
34
|
+
usedAssets: FluidUsedAssets,
|
|
35
|
+
) => incentives.reduce((total, { apy, eligibilityId }) => {
|
|
36
|
+
const eligibilityCheck = eligibilityId ? EligibilityMapping[eligibilityId] : null;
|
|
37
|
+
const { isEligible, eligibleUSDAmount } = eligibilityCheck
|
|
38
|
+
? eligibilityCheck(usedAssets, undefined)
|
|
39
|
+
: { isEligible: true, eligibleUSDAmount: principal };
|
|
40
|
+
const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : 0;
|
|
41
|
+
|
|
42
|
+
return new Dec(total).add(incentiveInterest).toString();
|
|
43
|
+
}, '0');
|
|
44
|
+
|
|
45
|
+
const calculateNetApyDex = ({
|
|
46
|
+
marketData,
|
|
47
|
+
assetsData,
|
|
48
|
+
usedAssets,
|
|
49
|
+
suppliedUsd,
|
|
50
|
+
borrowedUsd,
|
|
51
|
+
}: {
|
|
52
|
+
marketData: InnerFluidMarketData,
|
|
53
|
+
assetsData: FluidAssetsData,
|
|
54
|
+
usedAssets: FluidUsedAssets,
|
|
55
|
+
suppliedUsd: string,
|
|
56
|
+
borrowedUsd: string,
|
|
57
|
+
}) => {
|
|
18
58
|
const {
|
|
19
59
|
borrowRate,
|
|
20
60
|
supplyRate,
|
|
@@ -29,7 +69,21 @@ const calculateNetApyDex = ({ marketData, suppliedUsd, borrowedUsd }: { marketDa
|
|
|
29
69
|
|
|
30
70
|
const borrowIncentive = new Dec(incentiveBorrowRate || '0').mul(borrowedUsd).div(100).toString();
|
|
31
71
|
const supplyIncentive = new Dec(incentiveSupplyRate || '0').mul(suppliedUsd).div(100).toString();
|
|
32
|
-
const
|
|
72
|
+
const merklSupplyIncentive = getMerklIncentiveInterest(
|
|
73
|
+
getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Supply),
|
|
74
|
+
suppliedUsd,
|
|
75
|
+
usedAssets,
|
|
76
|
+
);
|
|
77
|
+
const merklBorrowIncentive = getMerklIncentiveInterest(
|
|
78
|
+
getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Borrow),
|
|
79
|
+
borrowedUsd,
|
|
80
|
+
usedAssets,
|
|
81
|
+
);
|
|
82
|
+
const incentiveUsd = new Dec(supplyIncentive)
|
|
83
|
+
.minus(borrowIncentive)
|
|
84
|
+
.add(merklSupplyIncentive)
|
|
85
|
+
.add(merklBorrowIncentive)
|
|
86
|
+
.toString();
|
|
33
87
|
|
|
34
88
|
const borrowInterest = new Dec(totalBorrowRate).mul(borrowedUsd).div(100).toString();
|
|
35
89
|
const supplyInterest = new Dec(totalSupplyRate).mul(suppliedUsd).div(100).toString();
|
|
@@ -66,7 +120,15 @@ borrowShares?: string,
|
|
|
66
120
|
: new Dec(marketData.debtSharePrice!).mul(borrowShares!).toString();
|
|
67
121
|
|
|
68
122
|
const isDex = [FluidVaultType.T2, FluidVaultType.T3, FluidVaultType.T4].includes(marketData.vaultType);
|
|
69
|
-
|
|
123
|
+
payload.merklSupplyIncentives = getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Supply);
|
|
124
|
+
payload.merklBorrowIncentives = getUniqueFluidMerklIncentives(assetsData, IncentiveSide.Borrow);
|
|
125
|
+
const { netApy, incentiveUsd, totalInterestUsd } = isDex ? calculateNetApyDex({
|
|
126
|
+
marketData,
|
|
127
|
+
assetsData,
|
|
128
|
+
usedAssets,
|
|
129
|
+
suppliedUsd: payload.suppliedUsd,
|
|
130
|
+
borrowedUsd: payload.borrowedUsd,
|
|
131
|
+
}) : calculateNetApy({ usedAssets, assetsData: assetsData as unknown as MMAssetsData });
|
|
70
132
|
payload.netApy = netApy;
|
|
71
133
|
payload.incentiveUsd = incentiveUsd;
|
|
72
134
|
payload.totalInterestUsd = totalInterestUsd;
|
|
@@ -337,4 +399,4 @@ export const mergeUsedAssets = (existing: Partial<FluidUsedAsset> = {}, addition
|
|
|
337
399
|
...EMPTY_USED_ASSET,
|
|
338
400
|
...existing,
|
|
339
401
|
...additional,
|
|
340
|
-
});
|
|
402
|
+
});
|
package/src/portfolio/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
|
|
|
41
41
|
import { _getLiquityV2MarketData, getLiquitySAndYBold, getLiquityV2Staking } from '../liquityV2';
|
|
42
42
|
import { _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
|
|
43
43
|
import { getUmbrellaData } from '../umbrella';
|
|
44
|
-
import {
|
|
44
|
+
import { getMerklUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
|
|
45
45
|
import { getCompoundV3Rewards } from '../claiming/compV3';
|
|
46
46
|
import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
47
47
|
import { getKingRewards } from '../claiming/king';
|
|
@@ -143,7 +143,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
rewardsData[address.toLowerCase() as EthAddress] = {
|
|
146
|
-
|
|
146
|
+
merkl: {},
|
|
147
147
|
aaveV3: {},
|
|
148
148
|
compV3: {},
|
|
149
149
|
spark: {},
|
|
@@ -352,11 +352,11 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
352
352
|
})).flat(),
|
|
353
353
|
...addresses.map(async (address) => {
|
|
354
354
|
try {
|
|
355
|
-
const
|
|
356
|
-
rewardsData[address.toLowerCase() as EthAddress].
|
|
355
|
+
const merklData = await getMerklUnclaimedRewards(address, network);
|
|
356
|
+
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
|
|
357
357
|
} catch (error) {
|
|
358
|
-
console.error(`Error fetching
|
|
359
|
-
rewardsData[address.toLowerCase() as EthAddress].
|
|
358
|
+
console.error(`Error fetching Merkl rewards data for address ${address}:`, error);
|
|
359
|
+
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
360
360
|
}
|
|
361
361
|
}),
|
|
362
362
|
...aaveV3Markets.map(market => addresses.map(async (address) => {
|
package/src/types/claiming.ts
CHANGED
|
@@ -5,6 +5,8 @@ export enum ClaimType {
|
|
|
5
5
|
AAVE_REWARDS = 'AAVE_REWARDS',
|
|
6
6
|
/** Merit rewards from AAVE (various tokens) */
|
|
7
7
|
AAVE_MERIT_REWARDS = 'AAVE_MERIT_REWARDS',
|
|
8
|
+
/** Rewards distributed through Merkl across supported protocols */
|
|
9
|
+
MERKL_REWARDS = 'MERKL_REWARDS',
|
|
8
10
|
/** Rewards from Compound V3 (only in COMP) */
|
|
9
11
|
COMPOUND_V3_COMP = 'COMPOUND_V3_COMP',
|
|
10
12
|
/** Rewards from Spark (wstETH only for now) */
|
|
@@ -47,6 +49,16 @@ export type AaveMeritRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
|
47
49
|
};
|
|
48
50
|
};
|
|
49
51
|
|
|
52
|
+
export type MerklRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
53
|
+
claimType: ClaimType.MERKL_REWARDS;
|
|
54
|
+
additionalClaimFields: {
|
|
55
|
+
accumulated: string;
|
|
56
|
+
proof: string[];
|
|
57
|
+
decimals: string;
|
|
58
|
+
unclaimed: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
50
62
|
export type SparkRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
51
63
|
claimType: ClaimType.SPARK_REWARDS;
|
|
52
64
|
additionalClaimFields: {
|
|
@@ -102,6 +114,7 @@ export type UniswapAirdropClaimableToken = _ClaimableTokenPartial & {
|
|
|
102
114
|
export type ClaimableToken =
|
|
103
115
|
AaveRewardsClaimableToken
|
|
104
116
|
| AaveMeritRewardsClaimableToken
|
|
117
|
+
| MerklRewardsClaimableToken
|
|
105
118
|
| CompoundV3CompClaimableToken
|
|
106
119
|
| SparkRewardsClaimableToken
|
|
107
120
|
| KingRewardsClaimableToken
|
package/src/types/common.ts
CHANGED
|
@@ -8,6 +8,10 @@ export enum IncentiveSide {
|
|
|
8
8
|
Borrow = 'borrow',
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export enum IncentiveSource {
|
|
12
|
+
Merkl = 'merkl',
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
export enum IncentiveEligibilityId {
|
|
12
16
|
AaveV3EthenaLiquidLeverage = '0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL',
|
|
13
17
|
AaveV3ArbitrumEthSupply = '0x5d16261c6715a653248269861bbacf68a9774cde',
|
|
@@ -29,6 +33,7 @@ export interface IncentiveData {
|
|
|
29
33
|
token: string,
|
|
30
34
|
apy: string,
|
|
31
35
|
incentiveKind?: IncentiveKind;
|
|
36
|
+
source?: IncentiveSource;
|
|
32
37
|
description?: string;
|
|
33
38
|
eligibilityId?: IncentiveEligibilityId;
|
|
34
39
|
}
|
package/src/types/fluid.ts
CHANGED
|
@@ -340,6 +340,8 @@ export interface FluidAggregatedVaultData {
|
|
|
340
340
|
leftToBorrowUsd: string,
|
|
341
341
|
netApy: string,
|
|
342
342
|
incentiveUsd: string,
|
|
343
|
+
merklSupplyIncentives: IncentiveData[],
|
|
344
|
+
merklBorrowIncentives: IncentiveData[],
|
|
343
345
|
ratio: string,
|
|
344
346
|
collRatio: string,
|
|
345
347
|
minRatio: string,
|
|
@@ -484,4 +486,4 @@ export interface FluidFTokenDataStructOutput {
|
|
|
484
486
|
supplyRate: bigint;
|
|
485
487
|
rewardsRate: bigint;
|
|
486
488
|
withdrawable: bigint;
|
|
487
|
-
}
|
|
489
|
+
}
|