@defisaver/positions-sdk 2.1.109 → 2.1.111-merkl-auth-api-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.
Files changed (43) hide show
  1. package/cjs/aaveV3/merkl.js +3 -2
  2. package/cjs/aaveV4/index.d.ts +3 -1
  3. package/cjs/aaveV4/index.js +41 -1
  4. package/cjs/aaveV4/merkl.js +3 -2
  5. package/cjs/claiming/aaveV3.js +6 -1
  6. package/cjs/helpers/aaveHelpers/index.d.ts +17 -0
  7. package/cjs/helpers/aaveHelpers/index.js +45 -1
  8. package/cjs/index.d.ts +1 -0
  9. package/cjs/index.js +4 -1
  10. package/cjs/markets/aaveV4/index.d.ts +1 -0
  11. package/cjs/markets/aaveV4/index.js +3 -1
  12. package/cjs/markets/index.d.ts +1 -1
  13. package/cjs/markets/index.js +2 -1
  14. package/cjs/services/merkl.d.ts +2 -0
  15. package/cjs/services/merkl.js +17 -0
  16. package/cjs/types/aave.d.ts +3 -0
  17. package/esm/aaveV3/merkl.js +4 -3
  18. package/esm/aaveV4/index.d.ts +3 -1
  19. package/esm/aaveV4/index.js +41 -3
  20. package/esm/aaveV4/merkl.js +4 -3
  21. package/esm/claiming/aaveV3.js +7 -2
  22. package/esm/helpers/aaveHelpers/index.d.ts +17 -0
  23. package/esm/helpers/aaveHelpers/index.js +43 -0
  24. package/esm/index.d.ts +1 -0
  25. package/esm/index.js +1 -0
  26. package/esm/markets/aaveV4/index.d.ts +1 -0
  27. package/esm/markets/aaveV4/index.js +1 -0
  28. package/esm/markets/index.d.ts +1 -1
  29. package/esm/markets/index.js +1 -1
  30. package/esm/services/merkl.d.ts +2 -0
  31. package/esm/services/merkl.js +12 -0
  32. package/esm/types/aave.d.ts +3 -0
  33. package/package.json +1 -1
  34. package/src/aaveV3/merkl.ts +4 -3
  35. package/src/aaveV4/index.ts +73 -3
  36. package/src/aaveV4/merkl.ts +4 -3
  37. package/src/claiming/aaveV3.ts +8 -3
  38. package/src/helpers/aaveHelpers/index.ts +60 -0
  39. package/src/index.ts +2 -0
  40. package/src/markets/aaveV4/index.ts +4 -0
  41. package/src/markets/index.ts +1 -1
  42. package/src/services/merkl.ts +14 -0
  43. package/src/types/aave.ts +4 -0
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.getMerkleCampaigns = exports.formatAaveAsset = exports.getAaveUnderlyingSymbol = void 0;
13
13
  const moneymarket_1 = require("../moneymarket");
14
14
  const utils_1 = require("../services/utils");
15
+ const merkl_1 = require("../services/merkl");
15
16
  const types_1 = require("../types");
16
17
  const getAaveUnderlyingSymbol = (_symbol = '') => {
17
18
  let symbol = _symbol
@@ -40,8 +41,8 @@ const formatAaveAsset = (_symbol) => {
40
41
  exports.formatAaveAsset = formatAaveAsset;
41
42
  const getMerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
42
43
  try {
43
- const res = yield fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
44
- signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
44
+ const res = yield fetch(`${(0, merkl_1.getMerklApiUrl)()}/v4/opportunities?mainProtocolId=aave`, {
45
+ signal: AbortSignal.timeout(utils_1.LONGER_TIMEOUT),
45
46
  });
46
47
  if (!res.ok)
47
48
  throw new Error('Failed to fetch Merkle campaigns');
@@ -1,9 +1,11 @@
1
1
  import { Client } from 'viem';
2
- import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo, EthAddress, EthereumProvider, NetworkNumber } from '../types';
2
+ import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo, EthAddress, EthereumProvider, NetworkNumber, Blockish, PositionBalances } from '../types';
3
3
  export * as lend from './lend';
4
4
  export { getAaveV4MerkleCampaigns } from './merkl';
5
5
  export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
6
6
  export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
7
7
  export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
8
8
  export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
9
+ export declare const _getAaveV4AccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
10
+ export declare const getAaveV4AccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
9
11
  export declare function getAaveV4UnderlyingFromReserveId(provider: EthereumProvider, network: NetworkNumber, spoke: EthAddress, reserveId: number): Promise<any>;
@@ -45,7 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
45
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
46
  };
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.getAaveV4MerkleCampaigns = exports.lend = void 0;
48
+ exports.getAaveV4AccountBalances = exports._getAaveV4AccountBalances = exports.getAaveV4MerkleCampaigns = exports.lend = void 0;
49
49
  exports._getAaveV4SpokeData = _getAaveV4SpokeData;
50
50
  exports.getAaveV4SpokeData = getAaveV4SpokeData;
51
51
  exports._getAaveV4AccountData = _getAaveV4AccountData;
@@ -285,6 +285,46 @@ function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
285
285
  return _getAaveV4AccountData((0, viem_1.getViemProvider)(provider, network), network, marketData, address, blockNumber);
286
286
  });
287
287
  }
288
+ const _getAaveV4AccountBalances = (provider, network, block, addressMapping, address, spokeAddress, spokeData) => __awaiter(void 0, void 0, void 0, function* () {
289
+ const balances = {
290
+ collateral: {},
291
+ debt: {},
292
+ };
293
+ if (!address || !spokeAddress) {
294
+ return balances;
295
+ }
296
+ const blockNumber = block === 'latest' ? 'latest' : Number(block);
297
+ let resolvedSpokeData = spokeData;
298
+ if (!resolvedSpokeData) {
299
+ const spokeInfo = (0, aaveV4_1.findAaveV4SpokeByAddress)(network, spokeAddress);
300
+ if (!spokeInfo) {
301
+ return balances;
302
+ }
303
+ resolvedSpokeData = yield _getAaveV4SpokeData(provider, network, spokeInfo, blockNumber);
304
+ }
305
+ const accountData = yield _getAaveV4AccountData(provider, network, resolvedSpokeData, address, blockNumber);
306
+ const finalSpokeData = resolvedSpokeData;
307
+ Object.entries(accountData.usedAssets).forEach(([key, asset]) => {
308
+ const reserveData = finalSpokeData.assetsData[key];
309
+ if (!reserveData)
310
+ return;
311
+ const balanceKey = addressMapping
312
+ ? (0, utils_1.wethToEthByAddress)(reserveData.underlying, network).toLowerCase()
313
+ : (0, utils_1.wethToEth)(asset.symbol);
314
+ if (asset.isSupplied && new decimal_js_1.default(asset.supplied || 0).gt(0)) {
315
+ balances.collateral[balanceKey] = (0, tokens_1.assetAmountInWei)(asset.supplied, asset.symbol);
316
+ }
317
+ if (asset.isBorrowed && new decimal_js_1.default(asset.borrowed || 0).gt(0)) {
318
+ balances.debt[balanceKey] = (0, tokens_1.assetAmountInWei)(asset.borrowed, asset.symbol);
319
+ }
320
+ });
321
+ return balances;
322
+ });
323
+ exports._getAaveV4AccountBalances = _getAaveV4AccountBalances;
324
+ const getAaveV4AccountBalances = (provider, network, block, addressMapping, address, spokeAddress, spokeData) => __awaiter(void 0, void 0, void 0, function* () {
325
+ return (0, exports._getAaveV4AccountBalances)((0, viem_1.getViemProvider)(provider, network), network, block, addressMapping, address, spokeAddress, spokeData);
326
+ });
327
+ exports.getAaveV4AccountBalances = getAaveV4AccountBalances;
288
328
  const _getAaveV4UnderlyingFromReserveId = (provider, network, spoke, reserveId) => __awaiter(void 0, void 0, void 0, function* () {
289
329
  const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network);
290
330
  const reserveData = yield viewContract.read.getReserveData([spoke, BigInt(reserveId)]);
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.attachAaveV4MerklIncentives = exports.getAaveV4MerkleCampaigns = void 0;
13
13
  const moneymarket_1 = require("../moneymarket");
14
14
  const utils_1 = require("../services/utils");
15
+ const merkl_1 = require("../services/merkl");
15
16
  const types_1 = require("../types");
16
17
  /**
17
18
  * Merkl tags Aave V4 reward campaigns by scope via the `type` field:
@@ -34,8 +35,8 @@ const buildIncentive = (opportunity) => {
34
35
  const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
35
36
  const result = { hub: {}, spoke: {} };
36
37
  try {
37
- const res = yield fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
38
- signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
38
+ const res = yield fetch(`${(0, merkl_1.getMerklApiUrl)()}/v4/opportunities?mainProtocolId=aave`, {
39
+ signal: AbortSignal.timeout(utils_1.LONGER_TIMEOUT),
39
40
  });
40
41
  if (!res.ok)
41
42
  throw new Error('Failed to fetch Aave V4 Merkle campaigns');
@@ -19,6 +19,7 @@ const claiming_1 = require("../types/claiming");
19
19
  const contracts_1 = require("../contracts");
20
20
  const utils_1 = require("../services/utils");
21
21
  const aaveHelpers_1 = require("../helpers/aaveHelpers");
22
+ const merkl_1 = require("../services/merkl");
22
23
  const mapAaveRewardsToClaimableTokens = (aaveRewards, marketAddress, walletAddress) => aaveRewards.map(reward => ({
23
24
  symbol: reward.symbol,
24
25
  amount: reward.amount,
@@ -99,7 +100,7 @@ function getMeritUnclaimedRewards(account, network) {
99
100
  return __awaiter(this, void 0, void 0, function* () {
100
101
  let data;
101
102
  try {
102
- const res = yield fetch(`https://api-merkl.angle.money/v4/users/${account}/rewards?chainId=${network}`, { signal: AbortSignal.timeout(3000) });
103
+ const res = yield fetch(`${(0, merkl_1.getMerklApiUrl)()}/v4/users/${account}/rewards?chainId=${network}`, { signal: AbortSignal.timeout(utils_1.LONGER_TIMEOUT) });
103
104
  data = yield res.json();
104
105
  }
105
106
  catch (error) {
@@ -107,6 +108,10 @@ function getMeritUnclaimedRewards(account, network) {
107
108
  data = [];
108
109
  }
109
110
  const claimableTokens = [];
111
+ // Merkl (or our proxy, on a non-2xx) can return a non-array error body; `fetch` doesn't throw on
112
+ // HTTP errors, so guard before iterating. Mirrors the app-side getMeritUnclaimedRewards.
113
+ if (!Array.isArray(data))
114
+ return claimableTokens;
110
115
  data.forEach((item) => {
111
116
  item.rewards.forEach(reward => {
112
117
  const { token, amount, claimed, proofs, } = reward;
@@ -30,6 +30,23 @@ export declare const aaveAnyGetEmodeMutableProps: ({ eModeCategory, eModeCategor
30
30
  liquidationRatio: any;
31
31
  collateralFactor: any;
32
32
  };
33
+ /**
34
+ * @description Per-asset effective LTV and liquidation threshold (LLTV) for the user, eMode-aware.
35
+ * Mirrors AaveV3View._getUserReserveLtvAndLltv: an asset in the active eMode's ltv-zero set has
36
+ * ltv 0 but KEEPS the eMode liquidation threshold (liquidations only consider LLTV). The returned
37
+ * `ltv` is identical to aaveAnyGetEmodeMutableProps().collateralFactor in every branch, so a ratio
38
+ * built on this matches the regular safety ratio whenever no collateral is LTV-0.
39
+ *
40
+ * Intentionally NOT merged with aaveAnyGetEmodeMutableProps (which it otherwise duplicates): the two
41
+ * differ only for an eMode ltv-zero asset — that helper returns liquidationRatio '0', whereas this one
42
+ * keeps the eMode liquidation threshold (to match the contract). aaveAnyGetEmodeMutableProps also feeds
43
+ * liquidationLimitUsd, so unifying them would change healthRatio / liqRatio / liquidationPrice for
44
+ * eMode-ltv-zero positions — to be done as a separate, validated follow-up PR.
45
+ */
46
+ export declare const aaveAnyGetUserReserveLtvAndLltv: ({ eModeCategory, eModeCategoriesData, assetsData, }: AaveHelperCommon, _asset: string) => {
47
+ ltv: string;
48
+ lltv: string;
49
+ };
33
50
  export declare const aaveAnyGetAggregatedPositionData: ({ usedAssets, eModeCategory, assetsData, selectedMarket, network, ...rest }: AaveHelperCommon) => AaveV3AggregatedPositionData;
34
51
  export declare const getApyAfterValuesEstimation: (selectedMarket: AaveMarketInfo, actions: [{
35
52
  action: string;
@@ -23,7 +23,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
23
23
  return (mod && mod.__esModule) ? mod : { "default": mod };
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.getAaveUnderlyingSymbol = exports.getApyAfterValuesEstimation = exports.aaveAnyGetAggregatedPositionData = exports.aaveAnyGetEmodeMutableProps = exports.aaveAnyGetSuppliableAsCollAssets = exports.aaveAnyGetSuppliableAssets = exports.aaveAnyGetCollSuppliedAssets = exports.aaveV3IsInSiloedMode = exports.aaveV3IsInIsolationMode = exports.isAaveV3 = exports.isAaveV2 = exports.AAVE_V3_MARKETS = void 0;
26
+ exports.getAaveUnderlyingSymbol = exports.getApyAfterValuesEstimation = exports.aaveAnyGetAggregatedPositionData = exports.aaveAnyGetUserReserveLtvAndLltv = exports.aaveAnyGetEmodeMutableProps = exports.aaveAnyGetSuppliableAsCollAssets = exports.aaveAnyGetSuppliableAssets = exports.aaveAnyGetCollSuppliedAssets = exports.aaveV3IsInSiloedMode = exports.aaveV3IsInIsolationMode = exports.isAaveV3 = exports.isAaveV2 = exports.AAVE_V3_MARKETS = void 0;
27
27
  const decimal_js_1 = __importDefault(require("decimal.js"));
28
28
  const tokens_1 = require("@defisaver/tokens");
29
29
  const types_1 = require("../../types");
@@ -83,6 +83,39 @@ const aaveAnyGetEmodeMutableProps = ({ eModeCategory, eModeCategoriesData, asset
83
83
  return ({ liquidationRatio, collateralFactor });
84
84
  };
85
85
  exports.aaveAnyGetEmodeMutableProps = aaveAnyGetEmodeMutableProps;
86
+ /**
87
+ * @description Offset subtracted from the liquidation threshold (LLTV) when crediting LTV-0 collateral
88
+ * in the safety-ratio fallback. Matches AaveV3View.getSafetyRatioWithLtvZeroFallback ('LLTV - 5%').
89
+ * Values are fractions (e.g. 0.8), so 5% === 0.05.
90
+ */
91
+ const LTV_ZERO_FALLBACK_LLTV_OFFSET = '0.05';
92
+ /**
93
+ * @description Per-asset effective LTV and liquidation threshold (LLTV) for the user, eMode-aware.
94
+ * Mirrors AaveV3View._getUserReserveLtvAndLltv: an asset in the active eMode's ltv-zero set has
95
+ * ltv 0 but KEEPS the eMode liquidation threshold (liquidations only consider LLTV). The returned
96
+ * `ltv` is identical to aaveAnyGetEmodeMutableProps().collateralFactor in every branch, so a ratio
97
+ * built on this matches the regular safety ratio whenever no collateral is LTV-0.
98
+ *
99
+ * Intentionally NOT merged with aaveAnyGetEmodeMutableProps (which it otherwise duplicates): the two
100
+ * differ only for an eMode ltv-zero asset — that helper returns liquidationRatio '0', whereas this one
101
+ * keeps the eMode liquidation threshold (to match the contract). aaveAnyGetEmodeMutableProps also feeds
102
+ * liquidationLimitUsd, so unifying them would change healthRatio / liqRatio / liquidationPrice for
103
+ * eMode-ltv-zero positions — to be done as a separate, validated follow-up PR.
104
+ */
105
+ const aaveAnyGetUserReserveLtvAndLltv = ({ eModeCategory, eModeCategoriesData, assetsData, }, _asset) => {
106
+ const asset = (0, utils_1.getNativeAssetFromWrapped)(_asset);
107
+ const assetData = assetsData[asset];
108
+ const eModeCategoryData = eModeCategoriesData === null || eModeCategoriesData === void 0 ? void 0 : eModeCategoriesData[eModeCategory];
109
+ if (eModeCategory === 0
110
+ || !eModeCategoryData
111
+ || !eModeCategoryData.collateralAssets.includes(asset)
112
+ || new decimal_js_1.default(eModeCategoryData.collateralFactor || 0).eq(0)) {
113
+ return { ltv: assetData.collateralFactor, lltv: assetData.liquidationRatio };
114
+ }
115
+ const ltv = eModeCategoryData.ltvZeroAssets.includes(asset) ? '0' : eModeCategoryData.collateralFactor;
116
+ return { ltv, lltv: eModeCategoryData.liquidationRatio };
117
+ };
118
+ exports.aaveAnyGetUserReserveLtvAndLltv = aaveAnyGetUserReserveLtvAndLltv;
86
119
  const aaveAnyGetAggregatedPositionData = (_a) => {
87
120
  var { usedAssets, eModeCategory, assetsData, selectedMarket, network } = _a, rest = __rest(_a, ["usedAssets", "eModeCategory", "assetsData", "selectedMarket", "network"]);
88
121
  const data = Object.assign({ usedAssets, eModeCategory, assetsData, selectedMarket, network }, rest);
@@ -96,6 +129,17 @@ const aaveAnyGetAggregatedPositionData = (_a) => {
96
129
  payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
97
130
  payload.ratio = +payload.suppliedUsd ? new decimal_js_1.default(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
98
131
  payload.collRatio = +payload.suppliedUsd ? new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
132
+ // Safety ratio as evaluated by the automation bots: LTV-0 collateral is credited at (LLTV - 5%)
133
+ // instead of 0 (AaveV3View.getSafetyRatioWithLtvZeroFallback). Equals `ratio` when no collateral
134
+ // is LTV-0. Computed off-chain here so it is available for after-value simulations too.
135
+ payload.borrowLimitWithLtvZeroFallbackUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => {
136
+ const { ltv, lltv } = (0, exports.aaveAnyGetUserReserveLtvAndLltv)(data, symbol);
137
+ const effectiveLtv = new decimal_js_1.default(ltv).eq(0)
138
+ ? decimal_js_1.default.max(0, new decimal_js_1.default(lltv).sub(LTV_ZERO_FALLBACK_LLTV_OFFSET))
139
+ : new decimal_js_1.default(ltv);
140
+ return new decimal_js_1.default(suppliedUsd).mul(effectiveLtv);
141
+ });
142
+ payload.safetyRatioWithLtvZeroFallback = +payload.suppliedUsd ? new decimal_js_1.default(payload.borrowLimitWithLtvZeroFallbackUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
99
143
  payload.liqRatio = new decimal_js_1.default(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
100
144
  payload.liqPercent = new decimal_js_1.default(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
101
145
  const { leveragedType, leveragedAsset } = (0, moneymarket_1.isLeveragedPos)(usedAssets);
package/cjs/index.d.ts CHANGED
@@ -22,4 +22,5 @@ import * as portfolio from './portfolio';
22
22
  import * as claiming from './claiming';
23
23
  import * as savings from './savings';
24
24
  export * from './types';
25
+ export { getMerklApiUrl, setMerklApiUrl } from './services/merkl';
25
26
  export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, eulerV2, fluid, portfolio, claiming, savings, };
package/cjs/index.js CHANGED
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.savings = exports.claiming = exports.portfolio = exports.fluid = exports.eulerV2 = exports.llamaLend = exports.morphoBlue = exports.helpers = exports.markets = exports.moneymarket = exports.staking = exports.exchange = exports.maker = exports.liquityV2 = exports.liquity = exports.curveUsd = exports.spark = exports.compoundV3 = exports.compoundV2 = exports.aaveV4 = exports.aaveV3 = exports.aaveV2 = void 0;
39
+ exports.savings = exports.claiming = exports.portfolio = exports.fluid = exports.eulerV2 = exports.llamaLend = exports.morphoBlue = exports.helpers = exports.markets = exports.moneymarket = exports.staking = exports.exchange = exports.maker = exports.liquityV2 = exports.liquity = exports.curveUsd = exports.spark = exports.compoundV3 = exports.compoundV2 = exports.aaveV4 = exports.aaveV3 = exports.aaveV2 = exports.setMerklApiUrl = exports.getMerklApiUrl = void 0;
40
40
  require("./setup");
41
41
  const fluid = __importStar(require("./fluid"));
42
42
  exports.fluid = fluid;
@@ -83,3 +83,6 @@ exports.claiming = claiming;
83
83
  const savings = __importStar(require("./savings"));
84
84
  exports.savings = savings;
85
85
  __exportStar(require("./types"), exports);
86
+ var merkl_1 = require("./services/merkl");
87
+ Object.defineProperty(exports, "getMerklApiUrl", { enumerable: true, get: function () { return merkl_1.getMerklApiUrl; } });
88
+ Object.defineProperty(exports, "setMerklApiUrl", { enumerable: true, get: function () { return merkl_1.setMerklApiUrl; } });
@@ -32,3 +32,4 @@ export declare const AaveV4Spokes: (networkId: NetworkNumber) => {
32
32
  readonly aave_v4_main_spoke: AaveV4SpokeInfo;
33
33
  };
34
34
  export declare const getAaveV4SpokeTypeInfo: (type: AaveV4SpokesType, network?: NetworkNumber) => AaveV4SpokeInfo;
35
+ export declare const findAaveV4SpokeByAddress: (networkId: NetworkNumber, address: string) => AaveV4SpokeInfo | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAaveV4SpokeTypeInfo = exports.AaveV4Spokes = exports.AAVE_V4_MAIN_SPOKE = exports.AAVE_V4_LOMBARD_BTC_SPOKE = exports.AAVE_V4_LIDO_SPOKE = exports.AAVE_V4_KELP_SPOKE = exports.AAVE_V4_GOLD_SPOKE = exports.AAVE_V4_FOREX_SPOKE = exports.AAVE_V4_ETHERFI_SPOKE = exports.AAVE_V4_ETHENA_ECOSYSTEM_SPOKE = exports.AAVE_V4_ETHENA_CORRELATED_SPOKE = exports.AAVE_V4_BLUECHIP_SPOKE = exports.getAaveV4HubByAddress = exports.getAaveV4HubTypeInfo = exports.AaveV4Hubs = exports.AAVE_V4_PRIME_HUB = exports.AAVE_V4_PLUS_HUB = exports.AAVE_V4_CORE_HUB = void 0;
3
+ exports.findAaveV4SpokeByAddress = exports.getAaveV4SpokeTypeInfo = exports.AaveV4Spokes = exports.AAVE_V4_MAIN_SPOKE = exports.AAVE_V4_LOMBARD_BTC_SPOKE = exports.AAVE_V4_LIDO_SPOKE = exports.AAVE_V4_KELP_SPOKE = exports.AAVE_V4_GOLD_SPOKE = exports.AAVE_V4_FOREX_SPOKE = exports.AAVE_V4_ETHERFI_SPOKE = exports.AAVE_V4_ETHENA_ECOSYSTEM_SPOKE = exports.AAVE_V4_ETHENA_CORRELATED_SPOKE = exports.AAVE_V4_BLUECHIP_SPOKE = exports.getAaveV4HubByAddress = exports.getAaveV4HubTypeInfo = exports.AaveV4Hubs = exports.AAVE_V4_PRIME_HUB = exports.AAVE_V4_PLUS_HUB = exports.AAVE_V4_CORE_HUB = void 0;
4
4
  const types_1 = require("../../types");
5
5
  // HUBS
6
6
  const AAVE_V4_CORE_HUB = (networkId) => ({
@@ -180,3 +180,5 @@ const AaveV4Spokes = (networkId) => ({
180
180
  exports.AaveV4Spokes = AaveV4Spokes;
181
181
  const getAaveV4SpokeTypeInfo = (type, network) => (Object.assign({}, (0, exports.AaveV4Spokes)(network !== null && network !== void 0 ? network : types_1.NetworkNumber.Eth))[type]);
182
182
  exports.getAaveV4SpokeTypeInfo = getAaveV4SpokeTypeInfo;
183
+ const findAaveV4SpokeByAddress = (networkId, address) => Object.values((0, exports.AaveV4Spokes)(networkId)).find(spoke => spoke.address.toLowerCase() === address.toLowerCase());
184
+ exports.findAaveV4SpokeByAddress = findAaveV4SpokeByAddress;
@@ -7,4 +7,4 @@ export { LlamaLendMarkets } from './llamaLend';
7
7
  export { LiquityV2Markets, findLiquityV2MarketByAddress } from './liquityV2';
8
8
  export { EulerV2Markets } from './euler';
9
9
  export { FluidMarkets, getFluidVersionsDataForNetwork, getFluidMarketInfoById, getFTokenAddress, getFluidMarketInfoByAddress, } from './fluid';
10
- export { AaveV4Spokes } from './aaveV4';
10
+ export { AaveV4Spokes, findAaveV4SpokeByAddress } from './aaveV4';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AaveV4Spokes = exports.getFluidMarketInfoByAddress = exports.getFTokenAddress = exports.getFluidMarketInfoById = exports.getFluidVersionsDataForNetwork = exports.FluidMarkets = exports.EulerV2Markets = exports.findLiquityV2MarketByAddress = exports.LiquityV2Markets = exports.LlamaLendMarkets = exports.findMorphoBlueMarket = exports.MorphoBlueMarkets = exports.CrvUsdMarkets = exports.SparkMarkets = exports.v3USDTCollAssets = exports.v3USDCeCollAssets = exports.v3USDCCollAssets = exports.v3USDbCCollAssets = exports.v3ETHCollAssets = exports.compoundV2CollateralAssets = exports.CompoundMarkets = exports.getAaveV3MarketByMarketAddress = exports.aaveV3AssetsDefaultMarket = exports.aaveV2AssetsDefaultMarket = exports.aaveV1AssetsDefaultMarket = exports.AaveMarkets = void 0;
3
+ exports.findAaveV4SpokeByAddress = exports.AaveV4Spokes = exports.getFluidMarketInfoByAddress = exports.getFTokenAddress = exports.getFluidMarketInfoById = exports.getFluidVersionsDataForNetwork = exports.FluidMarkets = exports.EulerV2Markets = exports.findLiquityV2MarketByAddress = exports.LiquityV2Markets = exports.LlamaLendMarkets = exports.findMorphoBlueMarket = exports.MorphoBlueMarkets = exports.CrvUsdMarkets = exports.SparkMarkets = exports.v3USDTCollAssets = exports.v3USDCeCollAssets = exports.v3USDCCollAssets = exports.v3USDbCCollAssets = exports.v3ETHCollAssets = exports.compoundV2CollateralAssets = exports.CompoundMarkets = exports.getAaveV3MarketByMarketAddress = exports.aaveV3AssetsDefaultMarket = exports.aaveV2AssetsDefaultMarket = exports.aaveV1AssetsDefaultMarket = exports.AaveMarkets = void 0;
4
4
  var aave_1 = require("./aave");
5
5
  Object.defineProperty(exports, "AaveMarkets", { enumerable: true, get: function () { return aave_1.AaveMarkets; } });
6
6
  Object.defineProperty(exports, "aaveV1AssetsDefaultMarket", { enumerable: true, get: function () { return aave_1.aaveV1AssetsDefaultMarket; } });
@@ -37,3 +37,4 @@ Object.defineProperty(exports, "getFTokenAddress", { enumerable: true, get: func
37
37
  Object.defineProperty(exports, "getFluidMarketInfoByAddress", { enumerable: true, get: function () { return fluid_1.getFluidMarketInfoByAddress; } });
38
38
  var aaveV4_1 = require("./aaveV4");
39
39
  Object.defineProperty(exports, "AaveV4Spokes", { enumerable: true, get: function () { return aaveV4_1.AaveV4Spokes; } });
40
+ Object.defineProperty(exports, "findAaveV4SpokeByAddress", { enumerable: true, get: function () { return aaveV4_1.findAaveV4SpokeByAddress; } });
@@ -0,0 +1,2 @@
1
+ export declare const getMerklApiUrl: () => string;
2
+ export declare const setMerklApiUrl: (url: string) => void;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setMerklApiUrl = exports.getMerklApiUrl = void 0;
4
+ /**
5
+ * Configurable base URL for the Merkl API (https://developers.merkl.xyz).
6
+ *
7
+ * Defaults to Merkl's public host. Consumers can override it via `setMerklApiUrl` — e.g. to route
8
+ * requests through their own backend proxy that attaches a Merkl API key (the key must stay
9
+ * server-side, and a proxy also avoids browser CORS limits on the public host).
10
+ */
11
+ let merklApiUrl = 'https://api.merkl.xyz';
12
+ const getMerklApiUrl = () => merklApiUrl;
13
+ exports.getMerklApiUrl = getMerklApiUrl;
14
+ const setMerklApiUrl = (url) => {
15
+ merklApiUrl = url.replace(/\/+$/, '');
16
+ };
17
+ exports.setMerklApiUrl = setMerklApiUrl;
@@ -125,6 +125,7 @@ export interface AavePositionData extends MMPositionData {
125
125
  ratio: string;
126
126
  minRatio: string;
127
127
  collRatio: string;
128
+ safetyRatioWithLtvZeroFallback?: string;
128
129
  suppliedUsd: string;
129
130
  borrowedUsd: string;
130
131
  borrowLimitUsd: string;
@@ -156,6 +157,8 @@ export interface AaveV3AggregatedPositionData {
156
157
  leftToBorrowUsd: string;
157
158
  ratio: string;
158
159
  collRatio: string;
160
+ borrowLimitWithLtvZeroFallbackUsd: string;
161
+ safetyRatioWithLtvZeroFallback: string;
159
162
  netApy: string;
160
163
  incentiveUsd: string;
161
164
  totalInterestUsd: string;
@@ -8,7 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { aprToApy } from '../moneymarket';
11
- import { DEFAULT_TIMEOUT, wethToEth } from '../services/utils';
11
+ import { LONGER_TIMEOUT, wethToEth } from '../services/utils';
12
+ import { getMerklApiUrl } from '../services/merkl';
12
13
  import { OpportunityAction, OpportunityStatus, } from '../types';
13
14
  export const getAaveUnderlyingSymbol = (_symbol = '') => {
14
15
  let symbol = _symbol
@@ -35,8 +36,8 @@ export const formatAaveAsset = (_symbol) => {
35
36
  };
36
37
  export const getMerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
37
38
  try {
38
- const res = yield fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
39
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
39
+ const res = yield fetch(`${getMerklApiUrl()}/v4/opportunities?mainProtocolId=aave`, {
40
+ signal: AbortSignal.timeout(LONGER_TIMEOUT),
40
41
  });
41
42
  if (!res.ok)
42
43
  throw new Error('Failed to fetch Merkle campaigns');
@@ -1,9 +1,11 @@
1
1
  import { Client } from 'viem';
2
- import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo, EthAddress, EthereumProvider, NetworkNumber } from '../types';
2
+ import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo, EthAddress, EthereumProvider, NetworkNumber, Blockish, PositionBalances } from '../types';
3
3
  export * as lend from './lend';
4
4
  export { getAaveV4MerkleCampaigns } from './merkl';
5
5
  export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
6
6
  export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
7
7
  export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
8
8
  export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
9
+ export declare const _getAaveV4AccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
10
+ export declare const getAaveV4AccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
9
11
  export declare function getAaveV4UnderlyingFromReserveId(provider: EthereumProvider, network: NetworkNumber, spoke: EthAddress, reserveId: number): Promise<any>;
@@ -8,14 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import Dec from 'decimal.js';
11
- import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
11
+ import { assetAmountInEth, assetAmountInWei, getAssetInfoByAddress } from '@defisaver/tokens';
12
12
  import { getViemProvider } from '../services/viem';
13
13
  import { IncentiveKind, } from '../types';
14
14
  import { AaveV4ViewContractViem } from '../contracts';
15
15
  import { getStakingApy, STAKING_ASSETS } from '../staking';
16
- import { isMaxUint, wethToEth } from '../services/utils';
16
+ import { isMaxUint, wethToEth, wethToEthByAddress } from '../services/utils';
17
17
  import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
18
- import { getAaveV4HubByAddress } from '../markets/aaveV4';
18
+ import { findAaveV4SpokeByAddress, getAaveV4HubByAddress } from '../markets/aaveV4';
19
19
  import { aprToApy } from '../moneymarket';
20
20
  import { attachAaveV4MerklIncentives, getAaveV4MerkleCampaigns } from './merkl';
21
21
  export * as lend from './lend';
@@ -240,6 +240,44 @@ export function getAaveV4AccountData(provider_1, network_1, marketData_1, addres
240
240
  return _getAaveV4AccountData(getViemProvider(provider, network), network, marketData, address, blockNumber);
241
241
  });
242
242
  }
243
+ export const _getAaveV4AccountBalances = (provider, network, block, addressMapping, address, spokeAddress, spokeData) => __awaiter(void 0, void 0, void 0, function* () {
244
+ const balances = {
245
+ collateral: {},
246
+ debt: {},
247
+ };
248
+ if (!address || !spokeAddress) {
249
+ return balances;
250
+ }
251
+ const blockNumber = block === 'latest' ? 'latest' : Number(block);
252
+ let resolvedSpokeData = spokeData;
253
+ if (!resolvedSpokeData) {
254
+ const spokeInfo = findAaveV4SpokeByAddress(network, spokeAddress);
255
+ if (!spokeInfo) {
256
+ return balances;
257
+ }
258
+ resolvedSpokeData = yield _getAaveV4SpokeData(provider, network, spokeInfo, blockNumber);
259
+ }
260
+ const accountData = yield _getAaveV4AccountData(provider, network, resolvedSpokeData, address, blockNumber);
261
+ const finalSpokeData = resolvedSpokeData;
262
+ Object.entries(accountData.usedAssets).forEach(([key, asset]) => {
263
+ const reserveData = finalSpokeData.assetsData[key];
264
+ if (!reserveData)
265
+ return;
266
+ const balanceKey = addressMapping
267
+ ? wethToEthByAddress(reserveData.underlying, network).toLowerCase()
268
+ : wethToEth(asset.symbol);
269
+ if (asset.isSupplied && new Dec(asset.supplied || 0).gt(0)) {
270
+ balances.collateral[balanceKey] = assetAmountInWei(asset.supplied, asset.symbol);
271
+ }
272
+ if (asset.isBorrowed && new Dec(asset.borrowed || 0).gt(0)) {
273
+ balances.debt[balanceKey] = assetAmountInWei(asset.borrowed, asset.symbol);
274
+ }
275
+ });
276
+ return balances;
277
+ });
278
+ export const getAaveV4AccountBalances = (provider, network, block, addressMapping, address, spokeAddress, spokeData) => __awaiter(void 0, void 0, void 0, function* () {
279
+ return _getAaveV4AccountBalances(getViemProvider(provider, network), network, block, addressMapping, address, spokeAddress, spokeData);
280
+ });
243
281
  const _getAaveV4UnderlyingFromReserveId = (provider, network, spoke, reserveId) => __awaiter(void 0, void 0, void 0, function* () {
244
282
  const viewContract = AaveV4ViewContractViem(provider, network);
245
283
  const reserveData = yield viewContract.read.getReserveData([spoke, BigInt(reserveId)]);
@@ -8,7 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { aprToApy } from '../moneymarket';
11
- import { DEFAULT_TIMEOUT } from '../services/utils';
11
+ import { LONGER_TIMEOUT } from '../services/utils';
12
+ import { getMerklApiUrl } from '../services/merkl';
12
13
  import { IncentiveKind, OpportunityAction, OpportunityStatus, } from '../types';
13
14
  /**
14
15
  * Merkl tags Aave V4 reward campaigns by scope via the `type` field:
@@ -31,8 +32,8 @@ const buildIncentive = (opportunity) => {
31
32
  export const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
32
33
  const result = { hub: {}, spoke: {} };
33
34
  try {
34
- const res = yield fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
35
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
35
+ const res = yield fetch(`${getMerklApiUrl()}/v4/opportunities?mainProtocolId=aave`, {
36
+ signal: AbortSignal.timeout(LONGER_TIMEOUT),
36
37
  });
37
38
  if (!res.ok)
38
39
  throw new Error('Failed to fetch Aave V4 Merkle campaigns');
@@ -10,8 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import Dec from 'decimal.js';
11
11
  import { ClaimType } from '../types/claiming';
12
12
  import { AaveIncentiveDataProviderV3ContractViem, AaveRewardsControllerViem, } from '../contracts';
13
- import { compareAddresses, getEthAmountForDecimals } from '../services/utils';
13
+ import { compareAddresses, getEthAmountForDecimals, LONGER_TIMEOUT } from '../services/utils';
14
14
  import { getAaveUnderlyingSymbol } from '../helpers/aaveHelpers';
15
+ import { getMerklApiUrl } from '../services/merkl';
15
16
  const mapAaveRewardsToClaimableTokens = (aaveRewards, marketAddress, walletAddress) => aaveRewards.map(reward => ({
16
17
  symbol: reward.symbol,
17
18
  amount: reward.amount,
@@ -92,7 +93,7 @@ export function getMeritUnclaimedRewards(account, network) {
92
93
  return __awaiter(this, void 0, void 0, function* () {
93
94
  let data;
94
95
  try {
95
- const res = yield fetch(`https://api-merkl.angle.money/v4/users/${account}/rewards?chainId=${network}`, { signal: AbortSignal.timeout(3000) });
96
+ const res = yield fetch(`${getMerklApiUrl()}/v4/users/${account}/rewards?chainId=${network}`, { signal: AbortSignal.timeout(LONGER_TIMEOUT) });
96
97
  data = yield res.json();
97
98
  }
98
99
  catch (error) {
@@ -100,6 +101,10 @@ export function getMeritUnclaimedRewards(account, network) {
100
101
  data = [];
101
102
  }
102
103
  const claimableTokens = [];
104
+ // Merkl (or our proxy, on a non-2xx) can return a non-array error body; `fetch` doesn't throw on
105
+ // HTTP errors, so guard before iterating. Mirrors the app-side getMeritUnclaimedRewards.
106
+ if (!Array.isArray(data))
107
+ return claimableTokens;
103
108
  data.forEach((item) => {
104
109
  item.rewards.forEach(reward => {
105
110
  const { token, amount, claimed, proofs, } = reward;
@@ -30,6 +30,23 @@ export declare const aaveAnyGetEmodeMutableProps: ({ eModeCategory, eModeCategor
30
30
  liquidationRatio: any;
31
31
  collateralFactor: any;
32
32
  };
33
+ /**
34
+ * @description Per-asset effective LTV and liquidation threshold (LLTV) for the user, eMode-aware.
35
+ * Mirrors AaveV3View._getUserReserveLtvAndLltv: an asset in the active eMode's ltv-zero set has
36
+ * ltv 0 but KEEPS the eMode liquidation threshold (liquidations only consider LLTV). The returned
37
+ * `ltv` is identical to aaveAnyGetEmodeMutableProps().collateralFactor in every branch, so a ratio
38
+ * built on this matches the regular safety ratio whenever no collateral is LTV-0.
39
+ *
40
+ * Intentionally NOT merged with aaveAnyGetEmodeMutableProps (which it otherwise duplicates): the two
41
+ * differ only for an eMode ltv-zero asset — that helper returns liquidationRatio '0', whereas this one
42
+ * keeps the eMode liquidation threshold (to match the contract). aaveAnyGetEmodeMutableProps also feeds
43
+ * liquidationLimitUsd, so unifying them would change healthRatio / liqRatio / liquidationPrice for
44
+ * eMode-ltv-zero positions — to be done as a separate, validated follow-up PR.
45
+ */
46
+ export declare const aaveAnyGetUserReserveLtvAndLltv: ({ eModeCategory, eModeCategoriesData, assetsData, }: AaveHelperCommon, _asset: string) => {
47
+ ltv: string;
48
+ lltv: string;
49
+ };
33
50
  export declare const aaveAnyGetAggregatedPositionData: ({ usedAssets, eModeCategory, assetsData, selectedMarket, network, ...rest }: AaveHelperCommon) => AaveV3AggregatedPositionData;
34
51
  export declare const getApyAfterValuesEstimation: (selectedMarket: AaveMarketInfo, actions: [{
35
52
  action: string;
@@ -69,6 +69,38 @@ export const aaveAnyGetEmodeMutableProps = ({ eModeCategory, eModeCategoriesData
69
69
  const { liquidationRatio, collateralFactor } = eModeCategoryData;
70
70
  return ({ liquidationRatio, collateralFactor });
71
71
  };
72
+ /**
73
+ * @description Offset subtracted from the liquidation threshold (LLTV) when crediting LTV-0 collateral
74
+ * in the safety-ratio fallback. Matches AaveV3View.getSafetyRatioWithLtvZeroFallback ('LLTV - 5%').
75
+ * Values are fractions (e.g. 0.8), so 5% === 0.05.
76
+ */
77
+ const LTV_ZERO_FALLBACK_LLTV_OFFSET = '0.05';
78
+ /**
79
+ * @description Per-asset effective LTV and liquidation threshold (LLTV) for the user, eMode-aware.
80
+ * Mirrors AaveV3View._getUserReserveLtvAndLltv: an asset in the active eMode's ltv-zero set has
81
+ * ltv 0 but KEEPS the eMode liquidation threshold (liquidations only consider LLTV). The returned
82
+ * `ltv` is identical to aaveAnyGetEmodeMutableProps().collateralFactor in every branch, so a ratio
83
+ * built on this matches the regular safety ratio whenever no collateral is LTV-0.
84
+ *
85
+ * Intentionally NOT merged with aaveAnyGetEmodeMutableProps (which it otherwise duplicates): the two
86
+ * differ only for an eMode ltv-zero asset — that helper returns liquidationRatio '0', whereas this one
87
+ * keeps the eMode liquidation threshold (to match the contract). aaveAnyGetEmodeMutableProps also feeds
88
+ * liquidationLimitUsd, so unifying them would change healthRatio / liqRatio / liquidationPrice for
89
+ * eMode-ltv-zero positions — to be done as a separate, validated follow-up PR.
90
+ */
91
+ export const aaveAnyGetUserReserveLtvAndLltv = ({ eModeCategory, eModeCategoriesData, assetsData, }, _asset) => {
92
+ const asset = getNativeAssetFromWrapped(_asset);
93
+ const assetData = assetsData[asset];
94
+ const eModeCategoryData = eModeCategoriesData === null || eModeCategoriesData === void 0 ? void 0 : eModeCategoriesData[eModeCategory];
95
+ if (eModeCategory === 0
96
+ || !eModeCategoryData
97
+ || !eModeCategoryData.collateralAssets.includes(asset)
98
+ || new Dec(eModeCategoryData.collateralFactor || 0).eq(0)) {
99
+ return { ltv: assetData.collateralFactor, lltv: assetData.liquidationRatio };
100
+ }
101
+ const ltv = eModeCategoryData.ltvZeroAssets.includes(asset) ? '0' : eModeCategoryData.collateralFactor;
102
+ return { ltv, lltv: eModeCategoryData.liquidationRatio };
103
+ };
72
104
  export const aaveAnyGetAggregatedPositionData = (_a) => {
73
105
  var { usedAssets, eModeCategory, assetsData, selectedMarket, network } = _a, rest = __rest(_a, ["usedAssets", "eModeCategory", "assetsData", "selectedMarket", "network"]);
74
106
  const data = Object.assign({ usedAssets, eModeCategory, assetsData, selectedMarket, network }, rest);
@@ -82,6 +114,17 @@ export const aaveAnyGetAggregatedPositionData = (_a) => {
82
114
  payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
83
115
  payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
84
116
  payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
117
+ // Safety ratio as evaluated by the automation bots: LTV-0 collateral is credited at (LLTV - 5%)
118
+ // instead of 0 (AaveV3View.getSafetyRatioWithLtvZeroFallback). Equals `ratio` when no collateral
119
+ // is LTV-0. Computed off-chain here so it is available for after-value simulations too.
120
+ payload.borrowLimitWithLtvZeroFallbackUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => {
121
+ const { ltv, lltv } = aaveAnyGetUserReserveLtvAndLltv(data, symbol);
122
+ const effectiveLtv = new Dec(ltv).eq(0)
123
+ ? Dec.max(0, new Dec(lltv).sub(LTV_ZERO_FALLBACK_LLTV_OFFSET))
124
+ : new Dec(ltv);
125
+ return new Dec(suppliedUsd).mul(effectiveLtv);
126
+ });
127
+ payload.safetyRatioWithLtvZeroFallback = +payload.suppliedUsd ? new Dec(payload.borrowLimitWithLtvZeroFallbackUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
85
128
  payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
86
129
  payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
87
130
  const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
package/esm/index.d.ts CHANGED
@@ -22,4 +22,5 @@ import * as portfolio from './portfolio';
22
22
  import * as claiming from './claiming';
23
23
  import * as savings from './savings';
24
24
  export * from './types';
25
+ export { getMerklApiUrl, setMerklApiUrl } from './services/merkl';
25
26
  export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, eulerV2, fluid, portfolio, claiming, savings, };
package/esm/index.js CHANGED
@@ -22,4 +22,5 @@ import * as portfolio from './portfolio';
22
22
  import * as claiming from './claiming';
23
23
  import * as savings from './savings';
24
24
  export * from './types';
25
+ export { getMerklApiUrl, setMerklApiUrl } from './services/merkl';
25
26
  export { aaveV2, aaveV3, aaveV4, compoundV2, compoundV3, spark, curveUsd, liquity, liquityV2, maker, exchange, staking, moneymarket, markets, helpers, morphoBlue, llamaLend, eulerV2, fluid, portfolio, claiming, savings, };
@@ -32,3 +32,4 @@ export declare const AaveV4Spokes: (networkId: NetworkNumber) => {
32
32
  readonly aave_v4_main_spoke: AaveV4SpokeInfo;
33
33
  };
34
34
  export declare const getAaveV4SpokeTypeInfo: (type: AaveV4SpokesType, network?: NetworkNumber) => AaveV4SpokeInfo;
35
+ export declare const findAaveV4SpokeByAddress: (networkId: NetworkNumber, address: string) => AaveV4SpokeInfo | undefined;
@@ -159,3 +159,4 @@ export const AaveV4Spokes = (networkId) => ({
159
159
  [AaveV4SpokesType.AaveV4MainSpoke]: AAVE_V4_MAIN_SPOKE(networkId),
160
160
  });
161
161
  export const getAaveV4SpokeTypeInfo = (type, network) => (Object.assign({}, AaveV4Spokes(network !== null && network !== void 0 ? network : NetworkNumber.Eth))[type]);
162
+ export const findAaveV4SpokeByAddress = (networkId, address) => Object.values(AaveV4Spokes(networkId)).find(spoke => spoke.address.toLowerCase() === address.toLowerCase());
@@ -7,4 +7,4 @@ export { LlamaLendMarkets } from './llamaLend';
7
7
  export { LiquityV2Markets, findLiquityV2MarketByAddress } from './liquityV2';
8
8
  export { EulerV2Markets } from './euler';
9
9
  export { FluidMarkets, getFluidVersionsDataForNetwork, getFluidMarketInfoById, getFTokenAddress, getFluidMarketInfoByAddress, } from './fluid';
10
- export { AaveV4Spokes } from './aaveV4';
10
+ export { AaveV4Spokes, findAaveV4SpokeByAddress } from './aaveV4';
@@ -7,4 +7,4 @@ export { LlamaLendMarkets } from './llamaLend';
7
7
  export { LiquityV2Markets, findLiquityV2MarketByAddress } from './liquityV2';
8
8
  export { EulerV2Markets } from './euler';
9
9
  export { FluidMarkets, getFluidVersionsDataForNetwork, getFluidMarketInfoById, getFTokenAddress, getFluidMarketInfoByAddress, } from './fluid';
10
- export { AaveV4Spokes } from './aaveV4';
10
+ export { AaveV4Spokes, findAaveV4SpokeByAddress } from './aaveV4';
@@ -0,0 +1,2 @@
1
+ export declare const getMerklApiUrl: () => string;
2
+ export declare const setMerklApiUrl: (url: string) => void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Configurable base URL for the Merkl API (https://developers.merkl.xyz).
3
+ *
4
+ * Defaults to Merkl's public host. Consumers can override it via `setMerklApiUrl` — e.g. to route
5
+ * requests through their own backend proxy that attaches a Merkl API key (the key must stay
6
+ * server-side, and a proxy also avoids browser CORS limits on the public host).
7
+ */
8
+ let merklApiUrl = 'https://api.merkl.xyz';
9
+ export const getMerklApiUrl = () => merklApiUrl;
10
+ export const setMerklApiUrl = (url) => {
11
+ merklApiUrl = url.replace(/\/+$/, '');
12
+ };
@@ -125,6 +125,7 @@ export interface AavePositionData extends MMPositionData {
125
125
  ratio: string;
126
126
  minRatio: string;
127
127
  collRatio: string;
128
+ safetyRatioWithLtvZeroFallback?: string;
128
129
  suppliedUsd: string;
129
130
  borrowedUsd: string;
130
131
  borrowLimitUsd: string;
@@ -156,6 +157,8 @@ export interface AaveV3AggregatedPositionData {
156
157
  leftToBorrowUsd: string;
157
158
  ratio: string;
158
159
  collRatio: string;
160
+ borrowLimitWithLtvZeroFallbackUsd: string;
161
+ safetyRatioWithLtvZeroFallback: string;
159
162
  netApy: string;
160
163
  incentiveUsd: string;
161
164
  totalInterestUsd: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.109",
3
+ "version": "2.1.111-merkl-auth-api-dev",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -1,5 +1,6 @@
1
1
  import { aprToApy } from '../moneymarket';
2
- import { DEFAULT_TIMEOUT, wethToEth } from '../services/utils';
2
+ import { LONGER_TIMEOUT, wethToEth } from '../services/utils';
3
+ import { getMerklApiUrl } from '../services/merkl';
3
4
  import {
4
5
  MerkleRewardMap, MerklOpportunity, OpportunityAction, OpportunityStatus,
5
6
  } from '../types';
@@ -31,8 +32,8 @@ export const formatAaveAsset = (_symbol: string) => {
31
32
 
32
33
  export const getMerkleCampaigns = async (chainId: NetworkNumber): Promise<MerkleRewardMap> => {
33
34
  try {
34
- const res = await fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
35
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
35
+ const res = await fetch(`${getMerklApiUrl()}/v4/opportunities?mainProtocolId=aave`, {
36
+ signal: AbortSignal.timeout(LONGER_TIMEOUT),
36
37
  });
37
38
  if (!res.ok) throw new Error('Failed to fetch Merkle campaigns');
38
39
  const opportunities = await res.json() as MerklOpportunity[];
@@ -1,6 +1,6 @@
1
1
  import { Client } from 'viem';
2
2
  import Dec from 'decimal.js';
3
- import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
3
+ import { assetAmountInEth, assetAmountInWei, getAssetInfoByAddress } from '@defisaver/tokens';
4
4
  import { getViemProvider } from '../services/viem';
5
5
  import {
6
6
  AaveV4AccountData,
@@ -16,12 +16,14 @@ import {
16
16
  IncentiveData,
17
17
  IncentiveKind,
18
18
  NetworkNumber,
19
+ Blockish,
20
+ PositionBalances,
19
21
  } from '../types';
20
22
  import { AaveV4ViewContractViem } from '../contracts';
21
23
  import { getStakingApy, STAKING_ASSETS } from '../staking';
22
- import { isMaxUint, wethToEth } from '../services/utils';
24
+ import { isMaxUint, wethToEth, wethToEthByAddress } from '../services/utils';
23
25
  import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
24
- import { getAaveV4HubByAddress } from '../markets/aaveV4';
26
+ import { findAaveV4SpokeByAddress, getAaveV4HubByAddress } from '../markets/aaveV4';
25
27
  import { aprToApy } from '../moneymarket';
26
28
  import { attachAaveV4MerklIncentives, getAaveV4MerkleCampaigns } from './merkl';
27
29
 
@@ -259,6 +261,74 @@ export async function getAaveV4AccountData(provider: EthereumProvider, network:
259
261
  return _getAaveV4AccountData(getViemProvider(provider, network), network, marketData, address, blockNumber);
260
262
  }
261
263
 
264
+ export const _getAaveV4AccountBalances = async (
265
+ provider: Client,
266
+ network: NetworkNumber,
267
+ block: Blockish,
268
+ addressMapping: boolean,
269
+ address: EthAddress,
270
+ spokeAddress: EthAddress,
271
+ spokeData?: AaveV4SpokeData,
272
+ ): Promise<PositionBalances> => {
273
+ const balances: PositionBalances = {
274
+ collateral: {},
275
+ debt: {},
276
+ };
277
+
278
+ if (!address || !spokeAddress) {
279
+ return balances;
280
+ }
281
+
282
+ const blockNumber = block === 'latest' ? 'latest' : Number(block);
283
+ let resolvedSpokeData = spokeData;
284
+ if (!resolvedSpokeData) {
285
+ const spokeInfo = findAaveV4SpokeByAddress(network, spokeAddress);
286
+ if (!spokeInfo) {
287
+ return balances;
288
+ }
289
+ resolvedSpokeData = await _getAaveV4SpokeData(provider, network, spokeInfo, blockNumber);
290
+ }
291
+
292
+ const accountData = await _getAaveV4AccountData(provider, network, resolvedSpokeData, address, blockNumber);
293
+ const finalSpokeData = resolvedSpokeData;
294
+
295
+ Object.entries(accountData.usedAssets).forEach(([key, asset]) => {
296
+ const reserveData = finalSpokeData.assetsData[key];
297
+ if (!reserveData) return;
298
+
299
+ const balanceKey = addressMapping
300
+ ? wethToEthByAddress(reserveData.underlying, network).toLowerCase()
301
+ : wethToEth(asset.symbol);
302
+
303
+ if (asset.isSupplied && new Dec(asset.supplied || 0).gt(0)) {
304
+ balances.collateral![balanceKey] = assetAmountInWei(asset.supplied, asset.symbol);
305
+ }
306
+ if (asset.isBorrowed && new Dec(asset.borrowed || 0).gt(0)) {
307
+ balances.debt![balanceKey] = assetAmountInWei(asset.borrowed, asset.symbol);
308
+ }
309
+ });
310
+
311
+ return balances;
312
+ };
313
+
314
+ export const getAaveV4AccountBalances = async (
315
+ provider: EthereumProvider,
316
+ network: NetworkNumber,
317
+ block: Blockish,
318
+ addressMapping: boolean,
319
+ address: EthAddress,
320
+ spokeAddress: EthAddress,
321
+ spokeData?: AaveV4SpokeData,
322
+ ): Promise<PositionBalances> => _getAaveV4AccountBalances(
323
+ getViemProvider(provider, network),
324
+ network,
325
+ block,
326
+ addressMapping,
327
+ address,
328
+ spokeAddress,
329
+ spokeData,
330
+ );
331
+
262
332
  const _getAaveV4UnderlyingFromReserveId = async (provider: Client, network: NetworkNumber, spoke: EthAddress, reserveId: number): Promise<any> => {
263
333
  const viewContract = AaveV4ViewContractViem(provider, network);
264
334
 
@@ -1,5 +1,6 @@
1
1
  import { aprToApy } from '../moneymarket';
2
- import { DEFAULT_TIMEOUT } from '../services/utils';
2
+ import { LONGER_TIMEOUT } from '../services/utils';
3
+ import { getMerklApiUrl } from '../services/merkl';
3
4
  import {
4
5
  AaveV4MerklRewardMap,
5
6
  AaveV4ReserveAssetData,
@@ -34,8 +35,8 @@ const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
34
35
  export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<AaveV4MerklRewardMap> => {
35
36
  const result: AaveV4MerklRewardMap = { hub: {}, spoke: {} };
36
37
  try {
37
- const res = await fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
38
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
38
+ const res = await fetch(`${getMerklApiUrl()}/v4/opportunities?mainProtocolId=aave`, {
39
+ signal: AbortSignal.timeout(LONGER_TIMEOUT),
39
40
  });
40
41
  if (!res.ok) throw new Error('Failed to fetch Aave V4 Merkle campaigns');
41
42
  const opportunities = await res.json() as MerklOpportunity[];
@@ -6,8 +6,9 @@ import {
6
6
  AaveIncentiveDataProviderV3ContractViem,
7
7
  AaveRewardsControllerViem,
8
8
  } from '../contracts';
9
- import { compareAddresses, getEthAmountForDecimals } from '../services/utils';
9
+ import { compareAddresses, getEthAmountForDecimals, LONGER_TIMEOUT } from '../services/utils';
10
10
  import { getAaveUnderlyingSymbol } from '../helpers/aaveHelpers';
11
+ import { getMerklApiUrl } from '../services/merkl';
11
12
 
12
13
  type AaveReward = {
13
14
  amount: string;
@@ -106,8 +107,8 @@ export async function getUnclaimedRewardsForAllMarkets(
106
107
  export async function getMeritUnclaimedRewards(account: EthAddress, network: NetworkNumber): Promise<ClaimableToken[]> {
107
108
  let data;
108
109
  try {
109
- const res = await fetch(`https://api-merkl.angle.money/v4/users/${account}/rewards?chainId=${network}`,
110
- { signal: AbortSignal.timeout(3000) });
110
+ const res = await fetch(`${getMerklApiUrl()}/v4/users/${account}/rewards?chainId=${network}`,
111
+ { signal: AbortSignal.timeout(LONGER_TIMEOUT) });
111
112
  data = await res.json();
112
113
  } catch (error) {
113
114
  console.error('External API Failure: Aave Merit', error);
@@ -116,6 +117,10 @@ export async function getMeritUnclaimedRewards(account: EthAddress, network: Net
116
117
 
117
118
  const claimableTokens: ClaimableToken[] = [];
118
119
 
120
+ // Merkl (or our proxy, on a non-2xx) can return a non-array error body; `fetch` doesn't throw on
121
+ // HTTP errors, so guard before iterating. Mirrors the app-side getMeritUnclaimedRewards.
122
+ if (!Array.isArray(data)) return claimableTokens;
123
+
119
124
  data.forEach((item: { rewards: any[]; }) => {
120
125
  item.rewards.forEach(reward => {
121
126
  const {
@@ -80,6 +80,51 @@ export const aaveAnyGetEmodeMutableProps = (
80
80
  return ({ liquidationRatio, collateralFactor });
81
81
  };
82
82
 
83
+ /**
84
+ * @description Offset subtracted from the liquidation threshold (LLTV) when crediting LTV-0 collateral
85
+ * in the safety-ratio fallback. Matches AaveV3View.getSafetyRatioWithLtvZeroFallback ('LLTV - 5%').
86
+ * Values are fractions (e.g. 0.8), so 5% === 0.05.
87
+ */
88
+ const LTV_ZERO_FALLBACK_LLTV_OFFSET = '0.05';
89
+
90
+ /**
91
+ * @description Per-asset effective LTV and liquidation threshold (LLTV) for the user, eMode-aware.
92
+ * Mirrors AaveV3View._getUserReserveLtvAndLltv: an asset in the active eMode's ltv-zero set has
93
+ * ltv 0 but KEEPS the eMode liquidation threshold (liquidations only consider LLTV). The returned
94
+ * `ltv` is identical to aaveAnyGetEmodeMutableProps().collateralFactor in every branch, so a ratio
95
+ * built on this matches the regular safety ratio whenever no collateral is LTV-0.
96
+ *
97
+ * Intentionally NOT merged with aaveAnyGetEmodeMutableProps (which it otherwise duplicates): the two
98
+ * differ only for an eMode ltv-zero asset — that helper returns liquidationRatio '0', whereas this one
99
+ * keeps the eMode liquidation threshold (to match the contract). aaveAnyGetEmodeMutableProps also feeds
100
+ * liquidationLimitUsd, so unifying them would change healthRatio / liqRatio / liquidationPrice for
101
+ * eMode-ltv-zero positions — to be done as a separate, validated follow-up PR.
102
+ */
103
+ export const aaveAnyGetUserReserveLtvAndLltv = (
104
+ {
105
+ eModeCategory,
106
+ eModeCategoriesData,
107
+ assetsData,
108
+ }: AaveHelperCommon,
109
+ _asset: string,
110
+ ): { ltv: string, lltv: string } => {
111
+ const asset = getNativeAssetFromWrapped(_asset);
112
+ const assetData = assetsData[asset];
113
+ const eModeCategoryData = eModeCategoriesData?.[eModeCategory];
114
+
115
+ if (
116
+ eModeCategory === 0
117
+ || !eModeCategoryData
118
+ || !eModeCategoryData.collateralAssets.includes(asset)
119
+ || new Dec(eModeCategoryData.collateralFactor || 0).eq(0)
120
+ ) {
121
+ return { ltv: assetData.collateralFactor, lltv: assetData.liquidationRatio };
122
+ }
123
+
124
+ const ltv = eModeCategoryData.ltvZeroAssets.includes(asset) ? '0' : eModeCategoryData.collateralFactor;
125
+ return { ltv, lltv: eModeCategoryData.liquidationRatio };
126
+ };
127
+
83
128
  export const aaveAnyGetAggregatedPositionData = ({
84
129
  usedAssets,
85
130
  eModeCategory,
@@ -109,6 +154,21 @@ export const aaveAnyGetAggregatedPositionData = ({
109
154
  payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
110
155
  payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
111
156
  payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
157
+ // Safety ratio as evaluated by the automation bots: LTV-0 collateral is credited at (LLTV - 5%)
158
+ // instead of 0 (AaveV3View.getSafetyRatioWithLtvZeroFallback). Equals `ratio` when no collateral
159
+ // is LTV-0. Computed off-chain here so it is available for after-value simulations too.
160
+ payload.borrowLimitWithLtvZeroFallbackUsd = getAssetsTotal(
161
+ usedAssets,
162
+ ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
163
+ ({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => {
164
+ const { ltv, lltv } = aaveAnyGetUserReserveLtvAndLltv(data, symbol);
165
+ const effectiveLtv = new Dec(ltv).eq(0)
166
+ ? Dec.max(0, new Dec(lltv).sub(LTV_ZERO_FALLBACK_LLTV_OFFSET))
167
+ : new Dec(ltv);
168
+ return new Dec(suppliedUsd).mul(effectiveLtv);
169
+ },
170
+ );
171
+ payload.safetyRatioWithLtvZeroFallback = +payload.suppliedUsd ? new Dec(payload.borrowLimitWithLtvZeroFallbackUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
112
172
  payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
113
173
  payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
114
174
  const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
package/src/index.ts CHANGED
@@ -25,6 +25,8 @@ import * as savings from './savings';
25
25
 
26
26
  export * from './types';
27
27
 
28
+ export { getMerklApiUrl, setMerklApiUrl } from './services/merkl';
29
+
28
30
  export {
29
31
  aaveV2,
30
32
  aaveV3,
@@ -188,4 +188,8 @@ export const AaveV4Spokes = (networkId: NetworkNumber) => ({
188
188
 
189
189
  export const getAaveV4SpokeTypeInfo = (type: AaveV4SpokesType, network?: NetworkNumber) => ({ ...AaveV4Spokes(network ?? NetworkNumber.Eth) }[type]);
190
190
 
191
+ export const findAaveV4SpokeByAddress = (networkId: NetworkNumber, address: string): AaveV4SpokeInfo | undefined => Object.values(AaveV4Spokes(networkId)).find(
192
+ spoke => spoke.address.toLowerCase() === address.toLowerCase(),
193
+ );
194
+
191
195
 
@@ -27,4 +27,4 @@ export {
27
27
  getFTokenAddress,
28
28
  getFluidMarketInfoByAddress,
29
29
  } from './fluid';
30
- export { AaveV4Spokes } from './aaveV4';
30
+ export { AaveV4Spokes, findAaveV4SpokeByAddress } from './aaveV4';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Configurable base URL for the Merkl API (https://developers.merkl.xyz).
3
+ *
4
+ * Defaults to Merkl's public host. Consumers can override it via `setMerklApiUrl` — e.g. to route
5
+ * requests through their own backend proxy that attaches a Merkl API key (the key must stay
6
+ * server-side, and a proxy also avoids browser CORS limits on the public host).
7
+ */
8
+ let merklApiUrl = 'https://api.merkl.xyz';
9
+
10
+ export const getMerklApiUrl = (): string => merklApiUrl;
11
+
12
+ export const setMerklApiUrl = (url: string): void => {
13
+ merklApiUrl = url.replace(/\/+$/, '');
14
+ };
package/src/types/aave.ts CHANGED
@@ -141,6 +141,8 @@ export interface AavePositionData extends MMPositionData {
141
141
  ratio: string,
142
142
  minRatio: string,
143
143
  collRatio: string,
144
+ // Safety ratio as evaluated by automation bots (LTV-0 collateral credited at LLTV - 5%). Aave v3 only.
145
+ safetyRatioWithLtvZeroFallback?: string,
144
146
  suppliedUsd: string,
145
147
  borrowedUsd: string,
146
148
  borrowLimitUsd: string,
@@ -173,6 +175,8 @@ export interface AaveV3AggregatedPositionData {
173
175
  leftToBorrowUsd: string,
174
176
  ratio: string,
175
177
  collRatio: string,
178
+ borrowLimitWithLtvZeroFallbackUsd: string,
179
+ safetyRatioWithLtvZeroFallback: string,
176
180
  netApy: string,
177
181
  incentiveUsd: string,
178
182
  totalInterestUsd: string,