@defisaver/positions-sdk 2.1.98 → 2.1.102-aave-v4-merkl-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.
@@ -1,4 +1,6 @@
1
1
  import Dec from 'decimal.js';
2
+ import { assetAmountInWei } from '@defisaver/tokens';
3
+ import { Client } from 'viem';
2
4
  import {
3
5
  aprToApy,
4
6
  calcLeverageLiqPrice,
@@ -10,11 +12,16 @@ import {
10
12
  AaveV4AggregatedPositionData,
11
13
  AaveV4AssetsData,
12
14
  AaveV4ReserveAssetData,
15
+ AaveV4SpokeInfo,
13
16
  AaveV4UsedReserveAsset,
14
17
  AaveV4UsedReserveAssets,
18
+ EthereumProvider,
15
19
  LeverageType,
16
20
  NetworkNumber,
17
21
  } from '../../types';
22
+ import { borrowOperations } from '../../constants';
23
+ import { AaveV4ViewContractViem } from '../../contracts';
24
+ import { getViemProvider } from '../../services/viem';
18
25
 
19
26
  export const calcUserRiskPremiumBps = (usedAssets: AaveV4UsedReserveAssets, assetsData: AaveV4AssetsData): number => {
20
27
  type CollateralInfo = { riskBps: number; valueUsd: Dec };
@@ -83,31 +90,6 @@ export const calcUserRiskPremiumBps = (usedAssets: AaveV4UsedReserveAssets, asse
83
90
  return riskPremiumBps.toNumber();
84
91
  };
85
92
 
86
- export const getApyAfterValuesEstimation = (
87
- usedAssets: AaveV4UsedReserveAssets,
88
- assetsData: AaveV4AssetsData,
89
- ): Record<string, { borrowRate: string; supplyRate: string }> => {
90
- const riskPremiumBps = calcUserRiskPremiumBps(usedAssets, assetsData);
91
- const riskPremiumFraction = new Dec(riskPremiumBps).div(10000); // bps to fraction
92
-
93
- const result: Record<string, { borrowRate: string; supplyRate: string }> = {};
94
-
95
- Object.entries(assetsData).forEach(([identifier, assetData]) => {
96
- const drawnRate = new Dec(assetData.drawnRate);
97
- const baseBorrowApr = drawnRate.mul(100);
98
- // finalBorrowRate = baseBorrowRate * (1 + riskPremiumFraction)
99
- const userBorrowApr = baseBorrowApr.mul(new Dec(1).add(riskPremiumFraction));
100
-
101
- result[identifier] = {
102
- borrowRate: aprToApy(userBorrowApr.toString()),
103
- // Supply rate is market-level (not user-specific), use existing value
104
- supplyRate: assetData.supplyRate,
105
- };
106
- });
107
-
108
- return result;
109
- };
110
-
111
93
  export const calculateNetApyAaveV4 = ({
112
94
  usedAssets,
113
95
  assetsData,
@@ -288,4 +270,55 @@ export const aaveV4GetAggregatedPositionData = ({
288
270
  payload.incentiveUsd = incentiveUsd;
289
271
  payload.totalInterestUsd = totalInterestUsd;
290
272
  return payload;
291
- };
273
+ };
274
+
275
+ const getAaveV4ApyAfterValuesEstimationInner = async (selectedSpoke: AaveV4SpokeInfo, assetsData: AaveV4AssetsData, actions: { action: string, amount: string, asset: string }[], client: Client, network: NetworkNumber) => {
276
+ const params = actions.map(({ action, asset, amount }) => {
277
+ const isDebtAsset = borrowOperations.includes(action);
278
+ const assetData = assetsData[asset];
279
+ const amountInWei = assetAmountInWei(amount, assetData.symbol);
280
+ let liquidityAdded;
281
+ let liquidityTaken;
282
+ if (isDebtAsset) {
283
+ liquidityAdded = action === 'payback' ? amountInWei : '0';
284
+ liquidityTaken = action === 'borrow' ? amountInWei : '0';
285
+ } else {
286
+ liquidityAdded = action === 'collateral' ? amountInWei : '0';
287
+ liquidityTaken = action === 'withdraw' ? amountInWei : '0';
288
+ }
289
+ return {
290
+ reserveId: BigInt(assetData.reserveId),
291
+ liquidityAdded: BigInt(liquidityAdded),
292
+ liquidityTaken: BigInt(liquidityTaken),
293
+ isDebtAsset,
294
+ };
295
+ });
296
+ const viewContract = AaveV4ViewContractViem(client, network);
297
+ const data = await viewContract.read.getApyAfterValuesEstimation([selectedSpoke.address, params]);
298
+
299
+ const rates: { [key: string]: { supplyRate: string, borrowRate: string } } = {};
300
+ data.forEach((item: any) => {
301
+ const {
302
+ hubDrawnRateEstimation, hubTotalDrawnEstimation, hubTotalLiquidityEstimation, hubSwept, reserveId,
303
+ } = item;
304
+ const drawnRate = new Dec(hubDrawnRateEstimation.toString()).div(new Dec(10).pow(27));
305
+ const borrowApr = drawnRate.mul(100);
306
+
307
+ const assetData = Object.values(assetsData).find(({ reserveId: rId }) => rId === Number(reserveId));
308
+
309
+ const totalDrawn = new Dec(hubTotalDrawnEstimation.toString());
310
+ const swept = new Dec(hubSwept.toString());
311
+ const hubUtilizationDenominator = totalDrawn.add(swept).add(hubTotalLiquidityEstimation.toString());
312
+ const hubUtilization = hubUtilizationDenominator.isZero() ? new Dec(0) : totalDrawn.div(hubUtilizationDenominator);
313
+
314
+ const supplyApr = borrowApr.mul(hubUtilization).mul(assetData?.premiumMultiplier || '1').mul(new Dec(1).minus(assetData?.liquidityFee || '0'));
315
+
316
+ rates[`${assetData?.symbol}-${assetData?.reserveId}`] = {
317
+ borrowRate: aprToApy(borrowApr.toString()),
318
+ supplyRate: aprToApy(supplyApr.toString()),
319
+ };
320
+ });
321
+ return rates;
322
+ };
323
+
324
+ export const getAaveV4ApyAfterValuesEstimation = async (selectedSpoke: AaveV4SpokeInfo, assetsData: AaveV4AssetsData, actions: { action: string, amount: string, asset: string }[], provider: EthereumProvider, network: NetworkNumber) => getAaveV4ApyAfterValuesEstimationInner(selectedSpoke, assetsData, actions, getViemProvider(provider, network), network);
@@ -111,6 +111,16 @@ export interface AaveV4ReserveAssetData {
111
111
  borrowRate: string,
112
112
  supplyIncentives: IncentiveData[];
113
113
  borrowIncentives: IncentiveData[];
114
+ /**
115
+ * Intrinsic incentives pre-combined with scope-specific Merkl rewards, ready to render per surface:
116
+ * - spoke* → spoke supply/borrow (Create page + dashboard market table)
117
+ * - hub* → hub supply/borrow (Lend page)
118
+ * `supplyIncentives`/`borrowIncentives` above remain intrinsic-only so the net-APY calc is unaffected.
119
+ */
120
+ spokeSupplyIncentives?: IncentiveData[];
121
+ spokeBorrowIncentives?: IncentiveData[];
122
+ hubSupplyIncentives?: IncentiveData[];
123
+ hubBorrowIncentives?: IncentiveData[];
114
124
  canBeBorrowed: boolean;
115
125
  canBeSupplied: boolean;
116
126
  canBeWithdrawn: boolean;
@@ -118,6 +128,8 @@ export interface AaveV4ReserveAssetData {
118
128
  utilization: string;
119
129
  /** Hub `liquidity` for this assetId (underlying amount), used for pool-level borrow/withdraw limits */
120
130
  hubLiquidity: string,
131
+ premiumMultiplier: string;
132
+ liquidityFee: string;
121
133
  }
122
134
 
123
135
  export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
@@ -1,4 +1,4 @@
1
- import { EthAddress } from './common';
1
+ import { EthAddress, IncentiveData } from './common';
2
2
 
3
3
  export enum OpportunityAction {
4
4
  LEND = 'LEND',
@@ -68,4 +68,16 @@ export type MerklOpportunity = {
68
68
  };
69
69
 
70
70
  export type MerkleRewardInfo = { apy: string; rewardTokenSymbol: string, description: string, identifier: string };
71
- export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; borrow?: MerkleRewardInfo }>;
71
+ export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; borrow?: MerkleRewardInfo }>;
72
+
73
+ export type AaveV4MerklScopedReward = { supply?: IncentiveData; borrow?: IncentiveData };
74
+
75
+ /**
76
+ * Aave V4 Merkl reward campaigns split by scope:
77
+ * - `hub`: keyed by underlying token address (lowercase) — rewards for supplying to a hub
78
+ * - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
79
+ */
80
+ export type AaveV4MerklRewardMap = {
81
+ hub: Record<string, AaveV4MerklScopedReward>;
82
+ spoke: Record<string, AaveV4MerklScopedReward>;
83
+ };