@defisaver/positions-sdk 2.1.138 → 2.1.140-dev
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/aaveV4/merkl.d.ts +2 -1
- package/cjs/aaveV4/merkl.js +42 -35
- package/cjs/helpers/aaveV4Helpers/index.d.ts +7 -1
- package/cjs/helpers/aaveV4Helpers/index.js +22 -10
- package/cjs/helpers/liquityV2Helpers/index.d.ts +2 -1
- package/cjs/helpers/liquityV2Helpers/index.js +3 -2
- package/cjs/liquityV2/index.js +3 -4
- package/cjs/types/common.d.ts +4 -0
- package/cjs/types/common.js +6 -1
- package/cjs/types/liquityV2.d.ts +3 -0
- package/cjs/types/merkl.d.ts +3 -4
- package/esm/aaveV4/merkl.d.ts +2 -1
- package/esm/aaveV4/merkl.js +41 -35
- package/esm/helpers/aaveV4Helpers/index.d.ts +7 -1
- package/esm/helpers/aaveV4Helpers/index.js +21 -10
- package/esm/helpers/liquityV2Helpers/index.d.ts +2 -1
- package/esm/helpers/liquityV2Helpers/index.js +3 -2
- package/esm/liquityV2/index.js +3 -4
- package/esm/types/common.d.ts +4 -0
- package/esm/types/common.js +5 -0
- package/esm/types/liquityV2.d.ts +3 -0
- package/esm/types/merkl.d.ts +3 -4
- package/package.json +1 -1
- package/src/aaveV4/merkl.ts +41 -32
- package/src/helpers/aaveV4Helpers/index.ts +22 -9
- package/src/helpers/liquityV2Helpers/index.ts +4 -1
- package/src/liquityV2/index.ts +4 -3
- package/src/types/common.ts +5 -0
- package/src/types/liquityV2.ts +3 -0
- package/src/types/merkl.ts +3 -3
package/cjs/aaveV4/merkl.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { AaveV4MerklRewardMap, AaveV4ReserveAssetData, NetworkNumber } from '../types';
|
|
1
|
+
import { AaveV4MerklRewardMap, AaveV4ReserveAssetData, MerklOpportunity, NetworkNumber } from '../types';
|
|
2
|
+
export declare const buildAaveV4MerklRewardMap: (opportunities: MerklOpportunity[], chainId: NetworkNumber) => AaveV4MerklRewardMap;
|
|
2
3
|
export declare const getAaveV4MerkleCampaigns: (chainId: NetworkNumber) => Promise<AaveV4MerklRewardMap>;
|
|
3
4
|
/**
|
|
4
5
|
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
package/cjs/aaveV4/merkl.js
CHANGED
|
@@ -9,17 +9,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.attachAaveV4MerklIncentives = exports.getAaveV4MerkleCampaigns = void 0;
|
|
12
|
+
exports.attachAaveV4MerklIncentives = exports.getAaveV4MerkleCampaigns = exports.buildAaveV4MerklRewardMap = void 0;
|
|
13
13
|
const moneymarket_1 = require("../moneymarket");
|
|
14
14
|
const utils_1 = require("../services/utils");
|
|
15
15
|
const types_1 = require("../types");
|
|
16
16
|
/**
|
|
17
17
|
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
18
|
-
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying
|
|
18
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per hub contract + underlying)
|
|
19
19
|
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
|
|
20
|
-
*
|
|
20
|
+
* Campaigns identify the underlying via `tokens[0]` and the scoping contract (the hub or spoke) via
|
|
21
|
+
* `explorerAddress`. The same underlying exists on several hubs (e.g. USDC on Prime and Paxos), so a
|
|
22
|
+
* hub campaign matched by underlying alone would leak onto every hub's reserves — a campaign whose
|
|
23
|
+
* `explorerAddress` isn't a contract any fetched reserve points to simply never matches.
|
|
21
24
|
*/
|
|
22
|
-
const
|
|
25
|
+
const scopeKey = (scopeAddress, underlying) => `${scopeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
|
|
23
26
|
const buildIncentive = (opportunity) => {
|
|
24
27
|
var _a, _b, _c, _d, _e;
|
|
25
28
|
const rewardToken = (_c = (_b = (_a = opportunity.rewardsRecord) === null || _a === void 0 ? void 0 : _a.breakdowns) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.token;
|
|
@@ -31,8 +34,38 @@ const buildIncentive = (opportunity) => {
|
|
|
31
34
|
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
32
35
|
};
|
|
33
36
|
};
|
|
34
|
-
const
|
|
37
|
+
const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
35
38
|
const result = { hub: {}, spoke: {} };
|
|
39
|
+
opportunities
|
|
40
|
+
.filter((o) => o.chainId === chainId)
|
|
41
|
+
.filter((o) => o.status === types_1.OpportunityStatus.LIVE)
|
|
42
|
+
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
43
|
+
.forEach((o) => {
|
|
44
|
+
var _a, _b, _c, _d;
|
|
45
|
+
const underlying = (_c = (_b = (_a = o.tokens) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
46
|
+
if (!underlying)
|
|
47
|
+
return;
|
|
48
|
+
const scopeAddress = (_d = o.explorerAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
49
|
+
if (!scopeAddress)
|
|
50
|
+
return;
|
|
51
|
+
const side = o.action === types_1.OpportunityAction.BORROW ? types_1.IncentiveSide.Borrow : types_1.IncentiveSide.Supply;
|
|
52
|
+
const incentive = buildIncentive(o);
|
|
53
|
+
const key = scopeKey(scopeAddress, underlying);
|
|
54
|
+
if (o.type.includes('HUB')) {
|
|
55
|
+
if (!result.hub[key])
|
|
56
|
+
result.hub[key] = {};
|
|
57
|
+
result.hub[key][side] = incentive;
|
|
58
|
+
}
|
|
59
|
+
else if (o.type.includes('SPOKE')) {
|
|
60
|
+
if (!result.spoke[key])
|
|
61
|
+
result.spoke[key] = {};
|
|
62
|
+
result.spoke[key][side] = incentive;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return result;
|
|
66
|
+
};
|
|
67
|
+
exports.buildAaveV4MerklRewardMap = buildAaveV4MerklRewardMap;
|
|
68
|
+
const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
69
|
try {
|
|
37
70
|
const res = yield fetch('https://fe.defisaver.com/api/merkl/opportunities?mainProtocolId=aave&type=AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW', {
|
|
38
71
|
signal: AbortSignal.timeout(utils_1.LONGER_TIMEOUT),
|
|
@@ -40,37 +73,11 @@ const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0,
|
|
|
40
73
|
if (!res.ok)
|
|
41
74
|
throw new Error('Failed to fetch Aave V4 Merkle campaigns');
|
|
42
75
|
const opportunities = yield res.json();
|
|
43
|
-
opportunities
|
|
44
|
-
.filter((o) => o.chainId === chainId)
|
|
45
|
-
.filter((o) => o.status === types_1.OpportunityStatus.LIVE)
|
|
46
|
-
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
47
|
-
.forEach((o) => {
|
|
48
|
-
var _a, _b, _c, _d;
|
|
49
|
-
const underlying = (_c = (_b = (_a = o.tokens) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
50
|
-
if (!underlying)
|
|
51
|
-
return;
|
|
52
|
-
const side = o.action === types_1.OpportunityAction.BORROW ? 'borrow' : 'supply';
|
|
53
|
-
const incentive = buildIncentive(o);
|
|
54
|
-
if (o.type.includes('HUB')) {
|
|
55
|
-
if (!result.hub[underlying])
|
|
56
|
-
result.hub[underlying] = {};
|
|
57
|
-
result.hub[underlying][side] = incentive;
|
|
58
|
-
}
|
|
59
|
-
else if (o.type.includes('SPOKE')) {
|
|
60
|
-
const spokeAddress = (_d = o.explorerAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
61
|
-
if (!spokeAddress)
|
|
62
|
-
return;
|
|
63
|
-
const key = spokeKey(spokeAddress, underlying);
|
|
64
|
-
if (!result.spoke[key])
|
|
65
|
-
result.spoke[key] = {};
|
|
66
|
-
result.spoke[key][side] = incentive;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
return result;
|
|
76
|
+
return (0, exports.buildAaveV4MerklRewardMap)(opportunities, chainId);
|
|
70
77
|
}
|
|
71
78
|
catch (e) {
|
|
72
79
|
console.error('Failed to fetch Aave V4 Merkle campaigns', e);
|
|
73
|
-
return
|
|
80
|
+
return { hub: {}, spoke: {} };
|
|
74
81
|
}
|
|
75
82
|
});
|
|
76
83
|
exports.getAaveV4MerkleCampaigns = getAaveV4MerkleCampaigns;
|
|
@@ -83,8 +90,8 @@ const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
|
|
|
83
90
|
const underlying = (_a = asset.underlying) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
84
91
|
const baseSupply = asset.supplyIncentives || [];
|
|
85
92
|
const baseBorrow = asset.borrowIncentives || [];
|
|
86
|
-
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[
|
|
87
|
-
const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
|
|
93
|
+
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[scopeKey(spokeAddress, underlying)] : undefined;
|
|
94
|
+
const hubScoped = (asset.hub && underlying) ? campaigns.hub[scopeKey(asset.hub, underlying)] : undefined;
|
|
88
95
|
return Object.assign(Object.assign({}, asset), { spokeSupplyIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.supply) ? [...baseSupply, spokeScoped.supply] : baseSupply, spokeBorrowIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.borrow) ? [...baseBorrow, spokeScoped.borrow] : baseBorrow, hubSupplyIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.supply) ? [...baseSupply, hubScoped.supply] : baseSupply, hubBorrowIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.borrow) ? [...baseBorrow, hubScoped.borrow] : baseBorrow });
|
|
89
96
|
};
|
|
90
97
|
exports.attachAaveV4MerklIncentives = attachAaveV4MerklIncentives;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4SpokeInfo, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets, EthereumProvider, LeverageType, NetworkNumber } from '../../types';
|
|
1
|
+
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4SpokeInfo, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets, EthereumProvider, IncentiveData, IncentiveSide, LeverageType, NetworkNumber } from '../../types';
|
|
2
2
|
export declare const calcUserRiskPremiumBps: (usedAssets: AaveV4UsedReserveAssets, assetsData: AaveV4AssetsData) => number;
|
|
3
|
+
/**
|
|
4
|
+
* The incentives that actually accrue to a position on this reserve for the given side: the
|
|
5
|
+
* intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
|
|
6
|
+
* use this rather than picking a scoped list directly, so badges always match the net APY math.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getAaveV4ApplicableIncentives: (assetData: AaveV4ReserveAssetData, side: IncentiveSide) => IncentiveData[];
|
|
3
9
|
export declare const calculateNetApyAaveV4: ({ usedAssets, assetsData, }: {
|
|
4
10
|
usedAssets: AaveV4UsedReserveAssets;
|
|
5
11
|
assetsData: AaveV4AssetsData;
|
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.getAaveV4ApyAfterValuesEstimation = exports.aaveV4GetAggregatedPositionData = exports.isLeveragedPosAaveV4 = exports.aaveV4GetCollateralFactor = exports.calculateNetApyAaveV4 = exports.calcUserRiskPremiumBps = void 0;
|
|
15
|
+
exports.getAaveV4ApyAfterValuesEstimation = exports.aaveV4GetAggregatedPositionData = exports.isLeveragedPosAaveV4 = exports.aaveV4GetCollateralFactor = exports.calculateNetApyAaveV4 = exports.getAaveV4ApplicableIncentives = exports.calcUserRiskPremiumBps = void 0;
|
|
16
16
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
17
17
|
const tokens_1 = require("@defisaver/tokens");
|
|
18
18
|
const moneymarket_1 = require("../../moneymarket");
|
|
@@ -76,14 +76,26 @@ const calcUserRiskPremiumBps = (usedAssets, assetsData) => {
|
|
|
76
76
|
exports.calcUserRiskPremiumBps = calcUserRiskPremiumBps;
|
|
77
77
|
/**
|
|
78
78
|
* `spokeXIncentives`/`hubXIncentives` are each the intrinsic `base` list with at most one Merkl
|
|
79
|
-
* reward appended (see attachAaveV4MerklIncentives).
|
|
80
|
-
*
|
|
79
|
+
* reward appended (see attachAaveV4MerklIncentives). Merkl regularly publishes the same reward
|
|
80
|
+
* stream at both scopes (a spoke campaign and a hub campaign covering the same borrows, e.g. USDC
|
|
81
|
+
* borrowed from the Prime Hub via the Bluechip Spoke), so the scopes must never be summed — the
|
|
82
|
+
* more specific spoke reward wins and the hub reward only applies when no spoke campaign exists,
|
|
83
|
+
* which is also what every per-asset APY badge (and Aave's own UI) shows.
|
|
81
84
|
*/
|
|
82
|
-
const mergeScopedIncentives = (base = [], spokeScoped, hubScoped) =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
...
|
|
86
|
-
|
|
85
|
+
const mergeScopedIncentives = (base = [], spokeScoped, hubScoped) => {
|
|
86
|
+
const spokeExtras = spokeScoped ? spokeScoped.slice(base.length) : [];
|
|
87
|
+
const hubExtras = hubScoped ? hubScoped.slice(base.length) : [];
|
|
88
|
+
return [...base, ...(spokeExtras.length ? spokeExtras : hubExtras)];
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The incentives that actually accrue to a position on this reserve for the given side: the
|
|
92
|
+
* intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
|
|
93
|
+
* use this rather than picking a scoped list directly, so badges always match the net APY math.
|
|
94
|
+
*/
|
|
95
|
+
const getAaveV4ApplicableIncentives = (assetData, side) => (side === types_1.IncentiveSide.Supply
|
|
96
|
+
? mergeScopedIncentives(assetData.supplyIncentives, assetData.spokeSupplyIncentives, assetData.hubSupplyIncentives)
|
|
97
|
+
: mergeScopedIncentives(assetData.borrowIncentives, assetData.spokeBorrowIncentives, assetData.hubBorrowIncentives));
|
|
98
|
+
exports.getAaveV4ApplicableIncentives = getAaveV4ApplicableIncentives;
|
|
87
99
|
const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
|
|
88
100
|
const riskPremiumBps = (0, exports.calcUserRiskPremiumBps)(usedAssets, assetsData);
|
|
89
101
|
const riskPremiumFraction = new decimal_js_1.default(riskPremiumBps).div(10000);
|
|
@@ -97,7 +109,7 @@ const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
|
|
|
97
109
|
acc.suppliedUsd = new decimal_js_1.default(acc.suppliedUsd).add(amount).toString();
|
|
98
110
|
const supplyInterest = (0, staking_1.calculateInterestEarned)(amount, assetData.supplyRate, 'year', true);
|
|
99
111
|
acc.supplyInterest = new decimal_js_1.default(acc.supplyInterest).add(supplyInterest.toString()).toString();
|
|
100
|
-
const supplyIncentives =
|
|
112
|
+
const supplyIncentives = (0, exports.getAaveV4ApplicableIncentives)(assetData, types_1.IncentiveSide.Supply);
|
|
101
113
|
for (const supplyIncentive of supplyIncentives) {
|
|
102
114
|
const incentiveInterest = (0, staking_1.calculateInterestEarned)(amount, supplyIncentive.apy, 'year', true);
|
|
103
115
|
acc.incentiveUsd = new decimal_js_1.default(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
@@ -113,7 +125,7 @@ const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
|
|
|
113
125
|
const userBorrowRate = (0, moneymarket_1.aprToApy)(userBorrowApr.toString());
|
|
114
126
|
const borrowInterest = (0, staking_1.calculateInterestEarned)(amount, userBorrowRate, 'year', true);
|
|
115
127
|
acc.borrowInterest = new decimal_js_1.default(acc.borrowInterest).sub(borrowInterest.toString()).toString();
|
|
116
|
-
const borrowIncentives =
|
|
128
|
+
const borrowIncentives = (0, exports.getAaveV4ApplicableIncentives)(assetData, types_1.IncentiveSide.Borrow);
|
|
117
129
|
for (const borrowIncentive of borrowIncentives) {
|
|
118
130
|
const incentiveInterest = (0, staking_1.calculateInterestEarned)(amount, borrowIncentive.apy, 'year', true);
|
|
119
131
|
acc.incentiveUsd = new decimal_js_1.default(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
@@ -4,9 +4,10 @@ export declare const calculateNetApyLiquityV2: (usedAssets: LiquityV2UsedAssets,
|
|
|
4
4
|
totalInterestUsd: string;
|
|
5
5
|
incentiveUsd: string;
|
|
6
6
|
};
|
|
7
|
-
export declare const getLiquityV2AggregatedPositionData: ({ usedAssets, assetsData, minCollRatio, interestRate, }: {
|
|
7
|
+
export declare const getLiquityV2AggregatedPositionData: ({ usedAssets, assetsData, minCollRatio, interestRate, liqRatio, }: {
|
|
8
8
|
usedAssets: LiquityV2UsedAssets;
|
|
9
9
|
assetsData: LiquityV2AssetsData;
|
|
10
10
|
minCollRatio: string;
|
|
11
11
|
interestRate: string;
|
|
12
|
+
liqRatio?: string;
|
|
12
13
|
}) => LiquityV2AggregatedTroveData;
|
|
@@ -38,11 +38,12 @@ const calculateNetApyLiquityV2 = (usedAssets, assetsData, interestRate) => {
|
|
|
38
38
|
return { netApy, totalInterestUsd, incentiveUsd };
|
|
39
39
|
};
|
|
40
40
|
exports.calculateNetApyLiquityV2 = calculateNetApyLiquityV2;
|
|
41
|
-
const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRatio, interestRate, }) => {
|
|
41
|
+
const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRatio, interestRate, liqRatio, }) => {
|
|
42
42
|
const payload = {};
|
|
43
43
|
payload.suppliedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, (usedAsset) => usedAsset, ({ suppliedUsd }) => suppliedUsd);
|
|
44
44
|
payload.borrowedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, (usedAsset) => usedAsset, ({ borrowedUsd }) => borrowedUsd);
|
|
45
45
|
payload.borrowLimitUsd = new decimal_js_1.default(payload.suppliedUsd).div(minCollRatio).mul(100).toString();
|
|
46
|
+
payload.liquidationLimitUsd = new decimal_js_1.default(payload.suppliedUsd).div(liqRatio !== null && liqRatio !== void 0 ? liqRatio : minCollRatio).mul(100).toString();
|
|
46
47
|
const leftToBorrowUsd = new decimal_js_1.default(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
47
48
|
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
48
49
|
payload.ratio = (+payload.suppliedUsd && +payload.borrowedUsd) ? new decimal_js_1.default(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
@@ -57,7 +58,7 @@ const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRat
|
|
|
57
58
|
payload.liquidationPrice = '';
|
|
58
59
|
if (leveragedType !== '') {
|
|
59
60
|
const assetPrice = assetsData[leveragedAsset].price;
|
|
60
|
-
payload.liquidationPrice = (0, moneymarket_1.calcLeverageLiqPrice)(leveragedType, assetPrice, payload.borrowedUsd, payload.
|
|
61
|
+
payload.liquidationPrice = (0, moneymarket_1.calcLeverageLiqPrice)(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
61
62
|
}
|
|
62
63
|
payload.exposure = (0, moneymarket_1.getExposure)(payload.borrowedUsd, payload.suppliedUsd);
|
|
63
64
|
return payload;
|
package/cjs/liquityV2/index.js
CHANGED
|
@@ -322,15 +322,14 @@ const _getLiquityV2TroveData = (provider_1, network_1, _a, ...args_1) => __await
|
|
|
322
322
|
const interestBatchManager = data.interestBatchManager;
|
|
323
323
|
const lastInterestRateAdjTime = data.lastInterestRateAdjTime.toString();
|
|
324
324
|
const hasInterestBatchManager = !(0, utils_1.compareAddresses)(interestBatchManager, constants_1.ZERO_ADDRESS);
|
|
325
|
-
const
|
|
325
|
+
const borrowLimitRatio = hasInterestBatchManager ? new decimal_js_1.default(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
|
|
326
326
|
const payload = Object.assign(Object.assign({ usedAssets,
|
|
327
327
|
troveId,
|
|
328
328
|
interestRate,
|
|
329
329
|
interestBatchManager,
|
|
330
330
|
debtInFront,
|
|
331
|
-
lastInterestRateAdjTime,
|
|
332
|
-
|
|
333
|
-
usedAssets, assetsData, minCollRatio: liqRatio, interestRate,
|
|
331
|
+
lastInterestRateAdjTime, liqRatio: minCollRatio, borrowLimitRatio, troveStatus: types_1.LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status.toString(), 10)] }, (0, liquityV2Helpers_1.getLiquityV2AggregatedPositionData)({
|
|
332
|
+
usedAssets, assetsData, minCollRatio: borrowLimitRatio, interestRate, liqRatio: minCollRatio,
|
|
334
333
|
})), { collRatio });
|
|
335
334
|
return payload;
|
|
336
335
|
});
|
package/cjs/types/common.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export declare enum IncentiveKind {
|
|
|
2
2
|
Staking = "staking",
|
|
3
3
|
Reward = "reward"
|
|
4
4
|
}
|
|
5
|
+
export declare enum IncentiveSide {
|
|
6
|
+
Supply = "supply",
|
|
7
|
+
Borrow = "borrow"
|
|
8
|
+
}
|
|
5
9
|
export declare enum IncentiveEligibilityId {
|
|
6
10
|
AaveV3EthenaLiquidLeverage = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL",
|
|
7
11
|
AaveV3ArbitrumEthSupply = "0x5d16261c6715a653248269861bbacf68a9774cde",
|
package/cjs/types/common.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NetworkNumber = exports.LeverageType = exports.IncentiveEligibilityId = exports.IncentiveKind = void 0;
|
|
3
|
+
exports.NetworkNumber = exports.LeverageType = exports.IncentiveEligibilityId = exports.IncentiveSide = exports.IncentiveKind = void 0;
|
|
4
4
|
var IncentiveKind;
|
|
5
5
|
(function (IncentiveKind) {
|
|
6
6
|
IncentiveKind["Staking"] = "staking";
|
|
7
7
|
IncentiveKind["Reward"] = "reward";
|
|
8
8
|
})(IncentiveKind || (exports.IncentiveKind = IncentiveKind = {}));
|
|
9
|
+
var IncentiveSide;
|
|
10
|
+
(function (IncentiveSide) {
|
|
11
|
+
IncentiveSide["Supply"] = "supply";
|
|
12
|
+
IncentiveSide["Borrow"] = "borrow";
|
|
13
|
+
})(IncentiveSide || (exports.IncentiveSide = IncentiveSide = {}));
|
|
9
14
|
var IncentiveEligibilityId;
|
|
10
15
|
(function (IncentiveEligibilityId) {
|
|
11
16
|
IncentiveEligibilityId["AaveV3EthenaLiquidLeverage"] = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL";
|
package/cjs/types/liquityV2.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export interface LiquityV2AggregatedTroveData {
|
|
|
84
84
|
suppliedUsd: string;
|
|
85
85
|
borrowedUsd: string;
|
|
86
86
|
borrowLimitUsd: string;
|
|
87
|
+
liquidationLimitUsd: string;
|
|
87
88
|
leftToBorrowUsd: string;
|
|
88
89
|
netApy: string;
|
|
89
90
|
incentiveUsd: string;
|
|
@@ -101,9 +102,11 @@ export interface LiquityV2TroveData {
|
|
|
101
102
|
ratio: string;
|
|
102
103
|
collRatio: string;
|
|
103
104
|
liqRatio: string;
|
|
105
|
+
borrowLimitRatio: string;
|
|
104
106
|
interestRate: string;
|
|
105
107
|
leftToBorrowUsd: string;
|
|
106
108
|
borrowLimitUsd: string;
|
|
109
|
+
liquidationLimitUsd: string;
|
|
107
110
|
suppliedUsd: string;
|
|
108
111
|
borrowedUsd: string;
|
|
109
112
|
netApy: string;
|
package/cjs/types/merkl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress, IncentiveData } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData, IncentiveSide } from './common';
|
|
2
2
|
export declare enum OpportunityAction {
|
|
3
3
|
LEND = "LEND",
|
|
4
4
|
BORROW = "BORROW"
|
|
@@ -74,12 +74,11 @@ export type MerkleRewardMap = Record<EthAddress, {
|
|
|
74
74
|
borrow?: MerkleRewardInfo;
|
|
75
75
|
}>;
|
|
76
76
|
export type AaveV4MerklScopedReward = {
|
|
77
|
-
|
|
78
|
-
borrow?: IncentiveData;
|
|
77
|
+
[side in IncentiveSide]?: IncentiveData;
|
|
79
78
|
};
|
|
80
79
|
/**
|
|
81
80
|
* Aave V4 Merkl reward campaigns split by scope:
|
|
82
|
-
* - `hub`: keyed by
|
|
81
|
+
* - `hub`: keyed by `${hubAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing via a hub
|
|
83
82
|
* - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
|
|
84
83
|
*/
|
|
85
84
|
export type AaveV4MerklRewardMap = {
|
package/esm/aaveV4/merkl.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { AaveV4MerklRewardMap, AaveV4ReserveAssetData, NetworkNumber } from '../types';
|
|
1
|
+
import { AaveV4MerklRewardMap, AaveV4ReserveAssetData, MerklOpportunity, NetworkNumber } from '../types';
|
|
2
|
+
export declare const buildAaveV4MerklRewardMap: (opportunities: MerklOpportunity[], chainId: NetworkNumber) => AaveV4MerklRewardMap;
|
|
2
3
|
export declare const getAaveV4MerkleCampaigns: (chainId: NetworkNumber) => Promise<AaveV4MerklRewardMap>;
|
|
3
4
|
/**
|
|
4
5
|
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
package/esm/aaveV4/merkl.js
CHANGED
|
@@ -9,14 +9,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { aprToApy } from '../moneymarket';
|
|
11
11
|
import { LONGER_TIMEOUT } from '../services/utils';
|
|
12
|
-
import { IncentiveKind, OpportunityAction, OpportunityStatus, } from '../types';
|
|
12
|
+
import { IncentiveKind, IncentiveSide, OpportunityAction, OpportunityStatus, } from '../types';
|
|
13
13
|
/**
|
|
14
14
|
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
15
|
-
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying
|
|
15
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per hub contract + underlying)
|
|
16
16
|
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
|
|
17
|
-
*
|
|
17
|
+
* Campaigns identify the underlying via `tokens[0]` and the scoping contract (the hub or spoke) via
|
|
18
|
+
* `explorerAddress`. The same underlying exists on several hubs (e.g. USDC on Prime and Paxos), so a
|
|
19
|
+
* hub campaign matched by underlying alone would leak onto every hub's reserves — a campaign whose
|
|
20
|
+
* `explorerAddress` isn't a contract any fetched reserve points to simply never matches.
|
|
18
21
|
*/
|
|
19
|
-
const
|
|
22
|
+
const scopeKey = (scopeAddress, underlying) => `${scopeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
|
|
20
23
|
const buildIncentive = (opportunity) => {
|
|
21
24
|
var _a, _b, _c, _d, _e;
|
|
22
25
|
const rewardToken = (_c = (_b = (_a = opportunity.rewardsRecord) === null || _a === void 0 ? void 0 : _a.breakdowns) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.token;
|
|
@@ -28,8 +31,37 @@ const buildIncentive = (opportunity) => {
|
|
|
28
31
|
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
29
32
|
};
|
|
30
33
|
};
|
|
31
|
-
export const
|
|
34
|
+
export const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
32
35
|
const result = { hub: {}, spoke: {} };
|
|
36
|
+
opportunities
|
|
37
|
+
.filter((o) => o.chainId === chainId)
|
|
38
|
+
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
39
|
+
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
40
|
+
.forEach((o) => {
|
|
41
|
+
var _a, _b, _c, _d;
|
|
42
|
+
const underlying = (_c = (_b = (_a = o.tokens) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
43
|
+
if (!underlying)
|
|
44
|
+
return;
|
|
45
|
+
const scopeAddress = (_d = o.explorerAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
46
|
+
if (!scopeAddress)
|
|
47
|
+
return;
|
|
48
|
+
const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
|
|
49
|
+
const incentive = buildIncentive(o);
|
|
50
|
+
const key = scopeKey(scopeAddress, underlying);
|
|
51
|
+
if (o.type.includes('HUB')) {
|
|
52
|
+
if (!result.hub[key])
|
|
53
|
+
result.hub[key] = {};
|
|
54
|
+
result.hub[key][side] = incentive;
|
|
55
|
+
}
|
|
56
|
+
else if (o.type.includes('SPOKE')) {
|
|
57
|
+
if (!result.spoke[key])
|
|
58
|
+
result.spoke[key] = {};
|
|
59
|
+
result.spoke[key][side] = incentive;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return result;
|
|
63
|
+
};
|
|
64
|
+
export const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
65
|
try {
|
|
34
66
|
const res = yield fetch('https://fe.defisaver.com/api/merkl/opportunities?mainProtocolId=aave&type=AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW', {
|
|
35
67
|
signal: AbortSignal.timeout(LONGER_TIMEOUT),
|
|
@@ -37,37 +69,11 @@ export const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, v
|
|
|
37
69
|
if (!res.ok)
|
|
38
70
|
throw new Error('Failed to fetch Aave V4 Merkle campaigns');
|
|
39
71
|
const opportunities = yield res.json();
|
|
40
|
-
opportunities
|
|
41
|
-
.filter((o) => o.chainId === chainId)
|
|
42
|
-
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
43
|
-
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
44
|
-
.forEach((o) => {
|
|
45
|
-
var _a, _b, _c, _d;
|
|
46
|
-
const underlying = (_c = (_b = (_a = o.tokens) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
47
|
-
if (!underlying)
|
|
48
|
-
return;
|
|
49
|
-
const side = o.action === OpportunityAction.BORROW ? 'borrow' : 'supply';
|
|
50
|
-
const incentive = buildIncentive(o);
|
|
51
|
-
if (o.type.includes('HUB')) {
|
|
52
|
-
if (!result.hub[underlying])
|
|
53
|
-
result.hub[underlying] = {};
|
|
54
|
-
result.hub[underlying][side] = incentive;
|
|
55
|
-
}
|
|
56
|
-
else if (o.type.includes('SPOKE')) {
|
|
57
|
-
const spokeAddress = (_d = o.explorerAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
58
|
-
if (!spokeAddress)
|
|
59
|
-
return;
|
|
60
|
-
const key = spokeKey(spokeAddress, underlying);
|
|
61
|
-
if (!result.spoke[key])
|
|
62
|
-
result.spoke[key] = {};
|
|
63
|
-
result.spoke[key][side] = incentive;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
return result;
|
|
72
|
+
return buildAaveV4MerklRewardMap(opportunities, chainId);
|
|
67
73
|
}
|
|
68
74
|
catch (e) {
|
|
69
75
|
console.error('Failed to fetch Aave V4 Merkle campaigns', e);
|
|
70
|
-
return
|
|
76
|
+
return { hub: {}, spoke: {} };
|
|
71
77
|
}
|
|
72
78
|
});
|
|
73
79
|
/**
|
|
@@ -79,7 +85,7 @@ export const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
|
|
|
79
85
|
const underlying = (_a = asset.underlying) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
80
86
|
const baseSupply = asset.supplyIncentives || [];
|
|
81
87
|
const baseBorrow = asset.borrowIncentives || [];
|
|
82
|
-
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[
|
|
83
|
-
const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
|
|
88
|
+
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[scopeKey(spokeAddress, underlying)] : undefined;
|
|
89
|
+
const hubScoped = (asset.hub && underlying) ? campaigns.hub[scopeKey(asset.hub, underlying)] : undefined;
|
|
84
90
|
return Object.assign(Object.assign({}, asset), { spokeSupplyIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.supply) ? [...baseSupply, spokeScoped.supply] : baseSupply, spokeBorrowIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.borrow) ? [...baseBorrow, spokeScoped.borrow] : baseBorrow, hubSupplyIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.supply) ? [...baseSupply, hubScoped.supply] : baseSupply, hubBorrowIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.borrow) ? [...baseBorrow, hubScoped.borrow] : baseBorrow });
|
|
85
91
|
};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4SpokeInfo, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets, EthereumProvider, LeverageType, NetworkNumber } from '../../types';
|
|
1
|
+
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4SpokeInfo, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets, EthereumProvider, IncentiveData, IncentiveSide, LeverageType, NetworkNumber } from '../../types';
|
|
2
2
|
export declare const calcUserRiskPremiumBps: (usedAssets: AaveV4UsedReserveAssets, assetsData: AaveV4AssetsData) => number;
|
|
3
|
+
/**
|
|
4
|
+
* The incentives that actually accrue to a position on this reserve for the given side: the
|
|
5
|
+
* intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
|
|
6
|
+
* use this rather than picking a scoped list directly, so badges always match the net APY math.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getAaveV4ApplicableIncentives: (assetData: AaveV4ReserveAssetData, side: IncentiveSide) => IncentiveData[];
|
|
3
9
|
export declare const calculateNetApyAaveV4: ({ usedAssets, assetsData, }: {
|
|
4
10
|
usedAssets: AaveV4UsedReserveAssets;
|
|
5
11
|
assetsData: AaveV4AssetsData;
|
|
@@ -11,7 +11,7 @@ import Dec from 'decimal.js';
|
|
|
11
11
|
import { assetAmountInWei } from '@defisaver/tokens';
|
|
12
12
|
import { aprToApy, calcLeverageLiqPrice, getAssetsTotal, STABLE_ASSETS, } from '../../moneymarket';
|
|
13
13
|
import { calculateInterestEarned } from '../../staking';
|
|
14
|
-
import { LeverageType, } from '../../types';
|
|
14
|
+
import { IncentiveSide, LeverageType, } from '../../types';
|
|
15
15
|
import { borrowOperations } from '../../constants';
|
|
16
16
|
import { AaveV4ViewContractViem } from '../../contracts';
|
|
17
17
|
import { getViemProvider } from '../../services/viem';
|
|
@@ -69,14 +69,25 @@ export const calcUserRiskPremiumBps = (usedAssets, assetsData) => {
|
|
|
69
69
|
};
|
|
70
70
|
/**
|
|
71
71
|
* `spokeXIncentives`/`hubXIncentives` are each the intrinsic `base` list with at most one Merkl
|
|
72
|
-
* reward appended (see attachAaveV4MerklIncentives).
|
|
73
|
-
*
|
|
72
|
+
* reward appended (see attachAaveV4MerklIncentives). Merkl regularly publishes the same reward
|
|
73
|
+
* stream at both scopes (a spoke campaign and a hub campaign covering the same borrows, e.g. USDC
|
|
74
|
+
* borrowed from the Prime Hub via the Bluechip Spoke), so the scopes must never be summed — the
|
|
75
|
+
* more specific spoke reward wins and the hub reward only applies when no spoke campaign exists,
|
|
76
|
+
* which is also what every per-asset APY badge (and Aave's own UI) shows.
|
|
74
77
|
*/
|
|
75
|
-
const mergeScopedIncentives = (base = [], spokeScoped, hubScoped) =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
...
|
|
79
|
-
|
|
78
|
+
const mergeScopedIncentives = (base = [], spokeScoped, hubScoped) => {
|
|
79
|
+
const spokeExtras = spokeScoped ? spokeScoped.slice(base.length) : [];
|
|
80
|
+
const hubExtras = hubScoped ? hubScoped.slice(base.length) : [];
|
|
81
|
+
return [...base, ...(spokeExtras.length ? spokeExtras : hubExtras)];
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* The incentives that actually accrue to a position on this reserve for the given side: the
|
|
85
|
+
* intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
|
|
86
|
+
* use this rather than picking a scoped list directly, so badges always match the net APY math.
|
|
87
|
+
*/
|
|
88
|
+
export const getAaveV4ApplicableIncentives = (assetData, side) => (side === IncentiveSide.Supply
|
|
89
|
+
? mergeScopedIncentives(assetData.supplyIncentives, assetData.spokeSupplyIncentives, assetData.hubSupplyIncentives)
|
|
90
|
+
: mergeScopedIncentives(assetData.borrowIncentives, assetData.spokeBorrowIncentives, assetData.hubBorrowIncentives));
|
|
80
91
|
export const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
|
|
81
92
|
const riskPremiumBps = calcUserRiskPremiumBps(usedAssets, assetsData);
|
|
82
93
|
const riskPremiumFraction = new Dec(riskPremiumBps).div(10000);
|
|
@@ -90,7 +101,7 @@ export const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
|
|
|
90
101
|
acc.suppliedUsd = new Dec(acc.suppliedUsd).add(amount).toString();
|
|
91
102
|
const supplyInterest = calculateInterestEarned(amount, assetData.supplyRate, 'year', true);
|
|
92
103
|
acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
|
|
93
|
-
const supplyIncentives =
|
|
104
|
+
const supplyIncentives = getAaveV4ApplicableIncentives(assetData, IncentiveSide.Supply);
|
|
94
105
|
for (const supplyIncentive of supplyIncentives) {
|
|
95
106
|
const incentiveInterest = calculateInterestEarned(amount, supplyIncentive.apy, 'year', true);
|
|
96
107
|
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
@@ -106,7 +117,7 @@ export const calculateNetApyAaveV4 = ({ usedAssets, assetsData, }) => {
|
|
|
106
117
|
const userBorrowRate = aprToApy(userBorrowApr.toString());
|
|
107
118
|
const borrowInterest = calculateInterestEarned(amount, userBorrowRate, 'year', true);
|
|
108
119
|
acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
|
|
109
|
-
const borrowIncentives =
|
|
120
|
+
const borrowIncentives = getAaveV4ApplicableIncentives(assetData, IncentiveSide.Borrow);
|
|
110
121
|
for (const borrowIncentive of borrowIncentives) {
|
|
111
122
|
const incentiveInterest = calculateInterestEarned(amount, borrowIncentive.apy, 'year', true);
|
|
112
123
|
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
@@ -4,9 +4,10 @@ export declare const calculateNetApyLiquityV2: (usedAssets: LiquityV2UsedAssets,
|
|
|
4
4
|
totalInterestUsd: string;
|
|
5
5
|
incentiveUsd: string;
|
|
6
6
|
};
|
|
7
|
-
export declare const getLiquityV2AggregatedPositionData: ({ usedAssets, assetsData, minCollRatio, interestRate, }: {
|
|
7
|
+
export declare const getLiquityV2AggregatedPositionData: ({ usedAssets, assetsData, minCollRatio, interestRate, liqRatio, }: {
|
|
8
8
|
usedAssets: LiquityV2UsedAssets;
|
|
9
9
|
assetsData: LiquityV2AssetsData;
|
|
10
10
|
minCollRatio: string;
|
|
11
11
|
interestRate: string;
|
|
12
|
+
liqRatio?: string;
|
|
12
13
|
}) => LiquityV2AggregatedTroveData;
|
|
@@ -31,11 +31,12 @@ export const calculateNetApyLiquityV2 = (usedAssets, assetsData, interestRate) =
|
|
|
31
31
|
const netApy = new Dec(totalInterestUsd).div(balance).times(100).toString();
|
|
32
32
|
return { netApy, totalInterestUsd, incentiveUsd };
|
|
33
33
|
};
|
|
34
|
-
export const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRatio, interestRate, }) => {
|
|
34
|
+
export const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRatio, interestRate, liqRatio, }) => {
|
|
35
35
|
const payload = {};
|
|
36
36
|
payload.suppliedUsd = getAssetsTotal(usedAssets, (usedAsset) => usedAsset, ({ suppliedUsd }) => suppliedUsd);
|
|
37
37
|
payload.borrowedUsd = getAssetsTotal(usedAssets, (usedAsset) => usedAsset, ({ borrowedUsd }) => borrowedUsd);
|
|
38
38
|
payload.borrowLimitUsd = new Dec(payload.suppliedUsd).div(minCollRatio).mul(100).toString();
|
|
39
|
+
payload.liquidationLimitUsd = new Dec(payload.suppliedUsd).div(liqRatio !== null && liqRatio !== void 0 ? liqRatio : minCollRatio).mul(100).toString();
|
|
39
40
|
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
40
41
|
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
41
42
|
payload.ratio = (+payload.suppliedUsd && +payload.borrowedUsd) ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
@@ -50,7 +51,7 @@ export const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, min
|
|
|
50
51
|
payload.liquidationPrice = '';
|
|
51
52
|
if (leveragedType !== '') {
|
|
52
53
|
const assetPrice = assetsData[leveragedAsset].price;
|
|
53
|
-
payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.
|
|
54
|
+
payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
54
55
|
}
|
|
55
56
|
payload.exposure = getExposure(payload.borrowedUsd, payload.suppliedUsd);
|
|
56
57
|
return payload;
|
package/esm/liquityV2/index.js
CHANGED
|
@@ -309,15 +309,14 @@ export const _getLiquityV2TroveData = (provider_1, network_1, _a, ...args_1) =>
|
|
|
309
309
|
const interestBatchManager = data.interestBatchManager;
|
|
310
310
|
const lastInterestRateAdjTime = data.lastInterestRateAdjTime.toString();
|
|
311
311
|
const hasInterestBatchManager = !compareAddresses(interestBatchManager, ZERO_ADDRESS);
|
|
312
|
-
const
|
|
312
|
+
const borrowLimitRatio = hasInterestBatchManager ? new Dec(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
|
|
313
313
|
const payload = Object.assign(Object.assign({ usedAssets,
|
|
314
314
|
troveId,
|
|
315
315
|
interestRate,
|
|
316
316
|
interestBatchManager,
|
|
317
317
|
debtInFront,
|
|
318
|
-
lastInterestRateAdjTime,
|
|
319
|
-
|
|
320
|
-
usedAssets, assetsData, minCollRatio: liqRatio, interestRate,
|
|
318
|
+
lastInterestRateAdjTime, liqRatio: minCollRatio, borrowLimitRatio, troveStatus: LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status.toString(), 10)] }, getLiquityV2AggregatedPositionData({
|
|
319
|
+
usedAssets, assetsData, minCollRatio: borrowLimitRatio, interestRate, liqRatio: minCollRatio,
|
|
321
320
|
})), { collRatio });
|
|
322
321
|
return payload;
|
|
323
322
|
});
|
package/esm/types/common.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export declare enum IncentiveKind {
|
|
|
2
2
|
Staking = "staking",
|
|
3
3
|
Reward = "reward"
|
|
4
4
|
}
|
|
5
|
+
export declare enum IncentiveSide {
|
|
6
|
+
Supply = "supply",
|
|
7
|
+
Borrow = "borrow"
|
|
8
|
+
}
|
|
5
9
|
export declare enum IncentiveEligibilityId {
|
|
6
10
|
AaveV3EthenaLiquidLeverage = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL",
|
|
7
11
|
AaveV3ArbitrumEthSupply = "0x5d16261c6715a653248269861bbacf68a9774cde",
|
package/esm/types/common.js
CHANGED
|
@@ -3,6 +3,11 @@ export var IncentiveKind;
|
|
|
3
3
|
IncentiveKind["Staking"] = "staking";
|
|
4
4
|
IncentiveKind["Reward"] = "reward";
|
|
5
5
|
})(IncentiveKind || (IncentiveKind = {}));
|
|
6
|
+
export var IncentiveSide;
|
|
7
|
+
(function (IncentiveSide) {
|
|
8
|
+
IncentiveSide["Supply"] = "supply";
|
|
9
|
+
IncentiveSide["Borrow"] = "borrow";
|
|
10
|
+
})(IncentiveSide || (IncentiveSide = {}));
|
|
6
11
|
export var IncentiveEligibilityId;
|
|
7
12
|
(function (IncentiveEligibilityId) {
|
|
8
13
|
IncentiveEligibilityId["AaveV3EthenaLiquidLeverage"] = "0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL";
|
package/esm/types/liquityV2.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export interface LiquityV2AggregatedTroveData {
|
|
|
84
84
|
suppliedUsd: string;
|
|
85
85
|
borrowedUsd: string;
|
|
86
86
|
borrowLimitUsd: string;
|
|
87
|
+
liquidationLimitUsd: string;
|
|
87
88
|
leftToBorrowUsd: string;
|
|
88
89
|
netApy: string;
|
|
89
90
|
incentiveUsd: string;
|
|
@@ -101,9 +102,11 @@ export interface LiquityV2TroveData {
|
|
|
101
102
|
ratio: string;
|
|
102
103
|
collRatio: string;
|
|
103
104
|
liqRatio: string;
|
|
105
|
+
borrowLimitRatio: string;
|
|
104
106
|
interestRate: string;
|
|
105
107
|
leftToBorrowUsd: string;
|
|
106
108
|
borrowLimitUsd: string;
|
|
109
|
+
liquidationLimitUsd: string;
|
|
107
110
|
suppliedUsd: string;
|
|
108
111
|
borrowedUsd: string;
|
|
109
112
|
netApy: string;
|
package/esm/types/merkl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress, IncentiveData } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData, IncentiveSide } from './common';
|
|
2
2
|
export declare enum OpportunityAction {
|
|
3
3
|
LEND = "LEND",
|
|
4
4
|
BORROW = "BORROW"
|
|
@@ -74,12 +74,11 @@ export type MerkleRewardMap = Record<EthAddress, {
|
|
|
74
74
|
borrow?: MerkleRewardInfo;
|
|
75
75
|
}>;
|
|
76
76
|
export type AaveV4MerklScopedReward = {
|
|
77
|
-
|
|
78
|
-
borrow?: IncentiveData;
|
|
77
|
+
[side in IncentiveSide]?: IncentiveData;
|
|
79
78
|
};
|
|
80
79
|
/**
|
|
81
80
|
* Aave V4 Merkl reward campaigns split by scope:
|
|
82
|
-
* - `hub`: keyed by
|
|
81
|
+
* - `hub`: keyed by `${hubAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing via a hub
|
|
83
82
|
* - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
|
|
84
83
|
*/
|
|
85
84
|
export type AaveV4MerklRewardMap = {
|
package/package.json
CHANGED
package/src/aaveV4/merkl.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
AaveV4ReserveAssetData,
|
|
6
6
|
IncentiveData,
|
|
7
7
|
IncentiveKind,
|
|
8
|
+
IncentiveSide,
|
|
8
9
|
MerklOpportunity,
|
|
9
10
|
OpportunityAction,
|
|
10
11
|
OpportunityStatus,
|
|
@@ -13,12 +14,15 @@ import {
|
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
16
|
-
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying
|
|
17
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per hub contract + underlying)
|
|
17
18
|
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
|
|
18
|
-
*
|
|
19
|
+
* Campaigns identify the underlying via `tokens[0]` and the scoping contract (the hub or spoke) via
|
|
20
|
+
* `explorerAddress`. The same underlying exists on several hubs (e.g. USDC on Prime and Paxos), so a
|
|
21
|
+
* hub campaign matched by underlying alone would leak onto every hub's reserves — a campaign whose
|
|
22
|
+
* `explorerAddress` isn't a contract any fetched reserve points to simply never matches.
|
|
19
23
|
*/
|
|
20
24
|
|
|
21
|
-
const
|
|
25
|
+
const scopeKey = (scopeAddress: string, underlying: string) => `${scopeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
|
|
22
26
|
|
|
23
27
|
const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
|
|
24
28
|
const rewardToken = opportunity.rewardsRecord?.breakdowns?.[0]?.token;
|
|
@@ -31,42 +35,47 @@ const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
|
|
|
31
35
|
};
|
|
32
36
|
};
|
|
33
37
|
|
|
34
|
-
export const
|
|
38
|
+
export const buildAaveV4MerklRewardMap = (opportunities: MerklOpportunity[], chainId: NetworkNumber): AaveV4MerklRewardMap => {
|
|
35
39
|
const result: AaveV4MerklRewardMap = { hub: {}, spoke: {} };
|
|
40
|
+
|
|
41
|
+
opportunities
|
|
42
|
+
.filter((o) => o.chainId === chainId)
|
|
43
|
+
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
44
|
+
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
45
|
+
.forEach((o) => {
|
|
46
|
+
const underlying = o.tokens?.[0]?.address?.toLowerCase();
|
|
47
|
+
if (!underlying) return;
|
|
48
|
+
|
|
49
|
+
const scopeAddress = o.explorerAddress?.toLowerCase();
|
|
50
|
+
if (!scopeAddress) return;
|
|
51
|
+
|
|
52
|
+
const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
|
|
53
|
+
const incentive = buildIncentive(o);
|
|
54
|
+
const key = scopeKey(scopeAddress, underlying);
|
|
55
|
+
|
|
56
|
+
if (o.type.includes('HUB')) {
|
|
57
|
+
if (!result.hub[key]) result.hub[key] = {};
|
|
58
|
+
result.hub[key][side] = incentive;
|
|
59
|
+
} else if (o.type.includes('SPOKE')) {
|
|
60
|
+
if (!result.spoke[key]) result.spoke[key] = {};
|
|
61
|
+
result.spoke[key][side] = incentive;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<AaveV4MerklRewardMap> => {
|
|
36
69
|
try {
|
|
37
70
|
const res = await fetch('https://fe.defisaver.com/api/merkl/opportunities?mainProtocolId=aave&type=AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW', {
|
|
38
71
|
signal: AbortSignal.timeout(LONGER_TIMEOUT),
|
|
39
72
|
});
|
|
40
73
|
if (!res.ok) throw new Error('Failed to fetch Aave V4 Merkle campaigns');
|
|
41
74
|
const opportunities = await res.json() as MerklOpportunity[];
|
|
42
|
-
|
|
43
|
-
opportunities
|
|
44
|
-
.filter((o) => o.chainId === chainId)
|
|
45
|
-
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
46
|
-
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
47
|
-
.forEach((o) => {
|
|
48
|
-
const underlying = o.tokens?.[0]?.address?.toLowerCase();
|
|
49
|
-
if (!underlying) return;
|
|
50
|
-
|
|
51
|
-
const side: 'supply' | 'borrow' = o.action === OpportunityAction.BORROW ? 'borrow' : 'supply';
|
|
52
|
-
const incentive = buildIncentive(o);
|
|
53
|
-
|
|
54
|
-
if (o.type.includes('HUB')) {
|
|
55
|
-
if (!result.hub[underlying]) result.hub[underlying] = {};
|
|
56
|
-
result.hub[underlying][side] = incentive;
|
|
57
|
-
} else if (o.type.includes('SPOKE')) {
|
|
58
|
-
const spokeAddress = o.explorerAddress?.toLowerCase();
|
|
59
|
-
if (!spokeAddress) return;
|
|
60
|
-
const key = spokeKey(spokeAddress, underlying);
|
|
61
|
-
if (!result.spoke[key]) result.spoke[key] = {};
|
|
62
|
-
result.spoke[key][side] = incentive;
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return result;
|
|
75
|
+
return buildAaveV4MerklRewardMap(opportunities, chainId);
|
|
67
76
|
} catch (e) {
|
|
68
77
|
console.error('Failed to fetch Aave V4 Merkle campaigns', e);
|
|
69
|
-
return
|
|
78
|
+
return { hub: {}, spoke: {} };
|
|
70
79
|
}
|
|
71
80
|
};
|
|
72
81
|
|
|
@@ -79,8 +88,8 @@ export const attachAaveV4MerklIncentives = (asset: AaveV4ReserveAssetData, spoke
|
|
|
79
88
|
const baseSupply = asset.supplyIncentives || [];
|
|
80
89
|
const baseBorrow = asset.borrowIncentives || [];
|
|
81
90
|
|
|
82
|
-
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[
|
|
83
|
-
const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
|
|
91
|
+
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[scopeKey(spokeAddress, underlying)] : undefined;
|
|
92
|
+
const hubScoped = (asset.hub && underlying) ? campaigns.hub[scopeKey(asset.hub, underlying)] : undefined;
|
|
84
93
|
|
|
85
94
|
return {
|
|
86
95
|
...asset,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
AaveV4UsedReserveAssets,
|
|
18
18
|
EthereumProvider,
|
|
19
19
|
IncentiveData,
|
|
20
|
+
IncentiveSide,
|
|
20
21
|
LeverageType,
|
|
21
22
|
NetworkNumber,
|
|
22
23
|
} from '../../types';
|
|
@@ -93,14 +94,26 @@ export const calcUserRiskPremiumBps = (usedAssets: AaveV4UsedReserveAssets, asse
|
|
|
93
94
|
|
|
94
95
|
/**
|
|
95
96
|
* `spokeXIncentives`/`hubXIncentives` are each the intrinsic `base` list with at most one Merkl
|
|
96
|
-
* reward appended (see attachAaveV4MerklIncentives).
|
|
97
|
-
*
|
|
97
|
+
* reward appended (see attachAaveV4MerklIncentives). Merkl regularly publishes the same reward
|
|
98
|
+
* stream at both scopes (a spoke campaign and a hub campaign covering the same borrows, e.g. USDC
|
|
99
|
+
* borrowed from the Prime Hub via the Bluechip Spoke), so the scopes must never be summed — the
|
|
100
|
+
* more specific spoke reward wins and the hub reward only applies when no spoke campaign exists,
|
|
101
|
+
* which is also what every per-asset APY badge (and Aave's own UI) shows.
|
|
98
102
|
*/
|
|
99
|
-
const mergeScopedIncentives = (base: IncentiveData[] = [], spokeScoped?: IncentiveData[], hubScoped?: IncentiveData[]): IncentiveData[] =>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
...
|
|
103
|
-
|
|
103
|
+
const mergeScopedIncentives = (base: IncentiveData[] = [], spokeScoped?: IncentiveData[], hubScoped?: IncentiveData[]): IncentiveData[] => {
|
|
104
|
+
const spokeExtras = spokeScoped ? spokeScoped.slice(base.length) : [];
|
|
105
|
+
const hubExtras = hubScoped ? hubScoped.slice(base.length) : [];
|
|
106
|
+
return [...base, ...(spokeExtras.length ? spokeExtras : hubExtras)];
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The incentives that actually accrue to a position on this reserve for the given side: the
|
|
111
|
+
* intrinsic (staking) incentives plus the single applicable Merkl reward. Display surfaces should
|
|
112
|
+
* use this rather than picking a scoped list directly, so badges always match the net APY math.
|
|
113
|
+
*/
|
|
114
|
+
export const getAaveV4ApplicableIncentives = (assetData: AaveV4ReserveAssetData, side: IncentiveSide): IncentiveData[] => (side === IncentiveSide.Supply
|
|
115
|
+
? mergeScopedIncentives(assetData.supplyIncentives, assetData.spokeSupplyIncentives, assetData.hubSupplyIncentives)
|
|
116
|
+
: mergeScopedIncentives(assetData.borrowIncentives, assetData.spokeBorrowIncentives, assetData.hubBorrowIncentives));
|
|
104
117
|
|
|
105
118
|
export const calculateNetApyAaveV4 = ({
|
|
106
119
|
usedAssets,
|
|
@@ -123,7 +136,7 @@ export const calculateNetApyAaveV4 = ({
|
|
|
123
136
|
const supplyInterest = calculateInterestEarned(amount, assetData.supplyRate, 'year', true);
|
|
124
137
|
acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
|
|
125
138
|
|
|
126
|
-
const supplyIncentives =
|
|
139
|
+
const supplyIncentives = getAaveV4ApplicableIncentives(assetData, IncentiveSide.Supply);
|
|
127
140
|
for (const supplyIncentive of supplyIncentives) {
|
|
128
141
|
const incentiveInterest = calculateInterestEarned(amount, supplyIncentive.apy, 'year', true);
|
|
129
142
|
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
@@ -141,7 +154,7 @@ export const calculateNetApyAaveV4 = ({
|
|
|
141
154
|
const borrowInterest = calculateInterestEarned(amount, userBorrowRate, 'year', true);
|
|
142
155
|
acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
|
|
143
156
|
|
|
144
|
-
const borrowIncentives =
|
|
157
|
+
const borrowIncentives = getAaveV4ApplicableIncentives(assetData, IncentiveSide.Borrow);
|
|
145
158
|
for (const borrowIncentive of borrowIncentives) {
|
|
146
159
|
const incentiveInterest = calculateInterestEarned(amount, borrowIncentive.apy, 'year', true);
|
|
147
160
|
acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
|
|
@@ -52,16 +52,19 @@ export const getLiquityV2AggregatedPositionData = ({
|
|
|
52
52
|
assetsData,
|
|
53
53
|
minCollRatio,
|
|
54
54
|
interestRate,
|
|
55
|
+
liqRatio,
|
|
55
56
|
}: {
|
|
56
57
|
usedAssets: LiquityV2UsedAssets
|
|
57
58
|
assetsData: LiquityV2AssetsData
|
|
58
59
|
minCollRatio: string
|
|
59
60
|
interestRate: string
|
|
61
|
+
liqRatio?: string // liquidation threshold (MCR), when different from minCollRatio (MCR + BCR for troves in a batch)
|
|
60
62
|
}): LiquityV2AggregatedTroveData => {
|
|
61
63
|
const payload = {} as LiquityV2AggregatedTroveData;
|
|
62
64
|
payload.suppliedUsd = getAssetsTotal(usedAssets, (usedAsset: LiquityV2UsedAsset) => usedAsset, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
|
|
63
65
|
payload.borrowedUsd = getAssetsTotal(usedAssets, (usedAsset: LiquityV2UsedAsset) => usedAsset, ({ borrowedUsd }: { borrowedUsd: string }) => borrowedUsd);
|
|
64
66
|
payload.borrowLimitUsd = new Dec(payload.suppliedUsd).div(minCollRatio).mul(100).toString();
|
|
67
|
+
payload.liquidationLimitUsd = new Dec(payload.suppliedUsd).div(liqRatio ?? minCollRatio).mul(100).toString();
|
|
65
68
|
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
66
69
|
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
67
70
|
payload.ratio = (+payload.suppliedUsd && +payload.borrowedUsd) ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
@@ -77,7 +80,7 @@ export const getLiquityV2AggregatedPositionData = ({
|
|
|
77
80
|
payload.liquidationPrice = '';
|
|
78
81
|
if (leveragedType !== '') {
|
|
79
82
|
const assetPrice = assetsData[leveragedAsset].price;
|
|
80
|
-
payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.
|
|
83
|
+
payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
81
84
|
}
|
|
82
85
|
payload.exposure = getExposure(payload.borrowedUsd, payload.suppliedUsd);
|
|
83
86
|
|
package/src/liquityV2/index.ts
CHANGED
|
@@ -425,7 +425,7 @@ export const _getLiquityV2TroveData = async (
|
|
|
425
425
|
const lastInterestRateAdjTime = data.lastInterestRateAdjTime.toString();
|
|
426
426
|
|
|
427
427
|
const hasInterestBatchManager = !compareAddresses(interestBatchManager, ZERO_ADDRESS);
|
|
428
|
-
const
|
|
428
|
+
const borrowLimitRatio = hasInterestBatchManager ? new Dec(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
|
|
429
429
|
|
|
430
430
|
const payload: LiquityV2TroveData = {
|
|
431
431
|
usedAssets,
|
|
@@ -434,10 +434,11 @@ export const _getLiquityV2TroveData = async (
|
|
|
434
434
|
interestBatchManager,
|
|
435
435
|
debtInFront,
|
|
436
436
|
lastInterestRateAdjTime,
|
|
437
|
-
liqRatio,
|
|
437
|
+
liqRatio: minCollRatio,
|
|
438
|
+
borrowLimitRatio,
|
|
438
439
|
troveStatus: LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status.toString(), 10)],
|
|
439
440
|
...getLiquityV2AggregatedPositionData({
|
|
440
|
-
usedAssets, assetsData, minCollRatio:
|
|
441
|
+
usedAssets, assetsData, minCollRatio: borrowLimitRatio, interestRate, liqRatio: minCollRatio,
|
|
441
442
|
}),
|
|
442
443
|
collRatio,
|
|
443
444
|
};
|
package/src/types/common.ts
CHANGED
|
@@ -3,6 +3,11 @@ export enum IncentiveKind {
|
|
|
3
3
|
Reward = 'reward',
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
export enum IncentiveSide {
|
|
7
|
+
Supply = 'supply',
|
|
8
|
+
Borrow = 'borrow',
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
export enum IncentiveEligibilityId {
|
|
7
12
|
AaveV3EthenaLiquidLeverage = '0x0dC599DBB180E64E42b87332FDeC3a551445ea44BORROW_BL',
|
|
8
13
|
AaveV3ArbitrumEthSupply = '0x5d16261c6715a653248269861bbacf68a9774cde',
|
package/src/types/liquityV2.ts
CHANGED
|
@@ -94,6 +94,7 @@ export interface LiquityV2AggregatedTroveData {
|
|
|
94
94
|
suppliedUsd: string,
|
|
95
95
|
borrowedUsd: string,
|
|
96
96
|
borrowLimitUsd: string,
|
|
97
|
+
liquidationLimitUsd: string,
|
|
97
98
|
leftToBorrowUsd: string,
|
|
98
99
|
netApy: string,
|
|
99
100
|
incentiveUsd: string,
|
|
@@ -112,9 +113,11 @@ export interface LiquityV2TroveData {
|
|
|
112
113
|
ratio: string,
|
|
113
114
|
collRatio: string,
|
|
114
115
|
liqRatio: string,
|
|
116
|
+
borrowLimitRatio: string,
|
|
115
117
|
interestRate: string,
|
|
116
118
|
leftToBorrowUsd: string,
|
|
117
119
|
borrowLimitUsd: string,
|
|
120
|
+
liquidationLimitUsd: string,
|
|
118
121
|
suppliedUsd: string,
|
|
119
122
|
borrowedUsd: string,
|
|
120
123
|
netApy: string,
|
package/src/types/merkl.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress, IncentiveData } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData, IncentiveSide } from './common';
|
|
2
2
|
|
|
3
3
|
export enum OpportunityAction {
|
|
4
4
|
LEND = 'LEND',
|
|
@@ -70,11 +70,11 @@ export type MerklOpportunity = {
|
|
|
70
70
|
export type MerkleRewardInfo = { apy: string; rewardTokenSymbol: string, description: string, identifier: string };
|
|
71
71
|
export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; borrow?: MerkleRewardInfo }>;
|
|
72
72
|
|
|
73
|
-
export type AaveV4MerklScopedReward = {
|
|
73
|
+
export type AaveV4MerklScopedReward = { [side in IncentiveSide]?: IncentiveData };
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Aave V4 Merkl reward campaigns split by scope:
|
|
77
|
-
* - `hub`: keyed by
|
|
77
|
+
* - `hub`: keyed by `${hubAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing via a hub
|
|
78
78
|
* - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
|
|
79
79
|
*/
|
|
80
80
|
export type AaveV4MerklRewardMap = {
|