@defisaver/positions-sdk 2.1.124 → 2.1.125

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.
@@ -5,6 +5,12 @@ 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
+ /**
9
+ * Lightweight existence check for Loan Shifter / UI discovery.
10
+ * Reads only `getLoanData(spoke, user)` and returns whether any reserve has
11
+ * non-zero supplied or debt, avoiding full spoke/market data fetches.
12
+ */
13
+ export declare const _getAaveV4AccountHasAnyBalance: (provider: Client, network: NetworkNumber, block: Blockish, address: EthAddress, spokeAddress: EthAddress) => Promise<boolean>;
8
14
  export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
9
15
  export declare const _getAaveV4AccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
10
16
  export declare const getAaveV4AccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
@@ -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.getAaveV4AccountBalances = exports._getAaveV4AccountBalances = exports.getAaveV4MerkleCampaigns = exports.lend = void 0;
48
+ exports.getAaveV4AccountBalances = exports._getAaveV4AccountBalances = exports._getAaveV4AccountHasAnyBalance = exports.getAaveV4MerkleCampaigns = exports.lend = void 0;
49
49
  exports._getAaveV4SpokeData = _getAaveV4SpokeData;
50
50
  exports.getAaveV4SpokeData = getAaveV4SpokeData;
51
51
  exports._getAaveV4AccountData = _getAaveV4AccountData;
@@ -280,6 +280,26 @@ function _getAaveV4AccountData(provider_1, network_1, spokeData_1, address_1) {
280
280
  riskPremiumBps });
281
281
  });
282
282
  }
283
+ /**
284
+ * Lightweight existence check for Loan Shifter / UI discovery.
285
+ * Reads only `getLoanData(spoke, user)` and returns whether any reserve has
286
+ * non-zero supplied or debt, avoiding full spoke/market data fetches.
287
+ */
288
+ const _getAaveV4AccountHasAnyBalance = (provider, network, block, address, spokeAddress) => __awaiter(void 0, void 0, void 0, function* () {
289
+ if (!address || !spokeAddress)
290
+ return false;
291
+ const blockNumber = block === 'latest' ? 'latest' : Number(block);
292
+ const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network, blockNumber);
293
+ const loanData = yield viewContract.read.getLoanData([spokeAddress, address]);
294
+ const reserves = (loanData === null || loanData === void 0 ? void 0 : loanData.reserves) || [];
295
+ return reserves.some((reserveAsset) => {
296
+ var _a, _b, _c, _d, _e, _f, _g, _h;
297
+ const suppliedRaw = (_d = (_c = (_b = (_a = reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.supplied) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.supplied) !== null && _d !== void 0 ? _d : '0';
298
+ const debtRaw = (_h = (_g = (_f = (_e = reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.totalDebt) === null || _e === void 0 ? void 0 : _e.toString) === null || _f === void 0 ? void 0 : _f.call(_e)) !== null && _g !== void 0 ? _g : reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.totalDebt) !== null && _h !== void 0 ? _h : '0';
299
+ return new decimal_js_1.default(suppliedRaw).gt(0) || new decimal_js_1.default(debtRaw).gt(0);
300
+ });
301
+ });
302
+ exports._getAaveV4AccountHasAnyBalance = _getAaveV4AccountHasAnyBalance;
283
303
  function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
284
304
  return __awaiter(this, arguments, void 0, function* (provider, network, marketData, address, blockNumber = 'latest') {
285
305
  return _getAaveV4AccountData((0, viem_1.getViemProvider)(provider, network), network, marketData, address, blockNumber);
@@ -116,13 +116,11 @@ const _getCompoundV3AccountBalances = (provider, network, block, addressMapping,
116
116
  if (!address) {
117
117
  return balances;
118
118
  }
119
- const market = ({
120
- [(0, compound_1.COMPOUND_V3_ETH)(network).baseMarketAddress.toLowerCase()]: (0, compound_1.COMPOUND_V3_ETH)(network),
121
- [(0, compound_1.COMPOUND_V3_USDC)(network).baseMarketAddress.toLowerCase()]: (0, compound_1.COMPOUND_V3_USDC)(network),
122
- [(0, compound_1.COMPOUND_V3_USDBC)(network).baseMarketAddress.toLowerCase()]: (0, compound_1.COMPOUND_V3_USDBC)(network),
123
- [(0, compound_1.COMPOUND_V3_USDT)(network).baseMarketAddress.toLowerCase()]: (0, compound_1.COMPOUND_V3_USDT)(network),
124
- [(0, compound_1.COMPOUND_V3_USDCe)(network).baseMarketAddress.toLowerCase()]: (0, compound_1.COMPOUND_V3_USDCe)(network),
125
- })[marketAddress.toLowerCase()];
119
+ const market = Object.values((0, compound_1.CompoundMarkets)(network)).find((m) => (m.baseMarketAddress
120
+ && m.baseMarketAddress !== constants_1.ZERO_ADDRESS
121
+ && m.baseMarketAddress.toLowerCase() === marketAddress.toLowerCase()));
122
+ if (!market)
123
+ throw new Error(`Unsupported Compound V3 market address ${marketAddress} on network ${network}`);
126
124
  const loanInfoContract = (0, contracts_1.CompV3ViewContractViem)(provider, network, block);
127
125
  const loanInfo = yield loanInfoContract.read.getLoanData([market.baseMarketAddress, address], (0, viem_1.setViemBlockNumber)(block));
128
126
  const baseAssetInfo = (0, tokens_1.getAssetInfo)((0, utils_1.wethToEth)(market.baseAsset), network);
@@ -0,0 +1,13 @@
1
+ import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
2
+ /**
3
+ * Cheap "does this address have a position here" check across all money-market protocols,
4
+ * keyed by each market/spoke's `.value`. Built for UI flows (e.g. Loan Shifter) that need to
5
+ * know *which* markets to show, without paying for a full manage-data fetch of every market.
6
+ *
7
+ * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
8
+ * per-market reads collapse into a handful of RPC round trips.
9
+ *
10
+ * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
11
+ * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
12
+ */
13
+ export declare function getUserPositionsExistence(provider: EthereumProvider, network: NetworkNumber, address: EthAddress, isSim?: boolean): Promise<Record<string, boolean>>;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getUserPositionsExistence = getUserPositionsExistence;
16
+ const decimal_js_1 = __importDefault(require("decimal.js"));
17
+ const common_1 = require("../types/common");
18
+ const viem_1 = require("../services/viem");
19
+ const markets_1 = require("../markets");
20
+ const types_1 = require("../types");
21
+ const aaveV4_1 = require("../aaveV4");
22
+ const compoundV2_1 = require("../compoundV2");
23
+ const compoundV3_1 = require("../compoundV3");
24
+ const curveUsd_1 = require("../curveUsd");
25
+ const llamaLend_1 = require("../llamaLend");
26
+ const morphoBlue_1 = require("../morphoBlue");
27
+ const contracts_1 = require("../contracts");
28
+ const hasAnyBalance = (balances) => ([balances.collateral, balances.debt].some((group) => Object.values(group || {}).some((amount) => new decimal_js_1.default(amount || '0').gt(0))));
29
+ /**
30
+ * Cheap "does this address have a position here" check across all money-market protocols,
31
+ * keyed by each market/spoke's `.value`. Built for UI flows (e.g. Loan Shifter) that need to
32
+ * know *which* markets to show, without paying for a full manage-data fetch of every market.
33
+ *
34
+ * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
35
+ * per-market reads collapse into a handful of RPC round trips.
36
+ *
37
+ * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
38
+ * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
39
+ */
40
+ function getUserPositionsExistence(provider_1, network_1, address_1) {
41
+ return __awaiter(this, arguments, void 0, function* (provider, network, address, isSim = false) {
42
+ const existence = {};
43
+ if (!address)
44
+ return existence;
45
+ const client = (0, viem_1.getViemProvider)(provider, network, { batch: { multicall: { batchSize: isSim ? 500000 : 2500000 } } });
46
+ const block = 'latest';
47
+ const tasks = [];
48
+ const aaveV3Markets = [types_1.AaveVersions.AaveV2, types_1.AaveVersions.AaveV3, types_1.AaveVersions.AaveV3Lido, types_1.AaveVersions.AaveV3Etherfi]
49
+ .map((version) => (0, markets_1.AaveMarkets)(network)[version])
50
+ .filter((market) => market.chainIds.includes(network) && market.lendingPoolAddress);
51
+ const sparkMarkets = Object.values((0, markets_1.SparkMarkets)(network)).filter((market) => market.chainIds.includes(network));
52
+ [...aaveV3Markets, ...sparkMarkets].forEach((market) => {
53
+ if (!market.lendingPool)
54
+ return;
55
+ const lendingPoolContract = (0, contracts_1.createViemContractFromConfigFunc)(market.lendingPool, market.lendingPoolAddress)(client, network, block);
56
+ tasks.push(lendingPoolContract.read.getUserAccountData([address], (0, viem_1.setViemBlockNumber)(block))
57
+ .then(([totalCollateral, totalDebt]) => {
58
+ existence[market.value] = new decimal_js_1.default(totalCollateral.toString()).gt(0) || new decimal_js_1.default(totalDebt.toString()).gt(0);
59
+ })
60
+ .catch(() => { existence[market.value] = false; }));
61
+ });
62
+ const balanceTask = (key, getBalances) => getBalances()
63
+ .then((balances) => { existence[key] = hasAnyBalance(balances); })
64
+ .catch(() => { existence[key] = false; });
65
+ Object.values((0, markets_1.AaveV4Spokes)(network)).filter((spoke) => spoke.chainIds.includes(network)).forEach((spoke) => {
66
+ tasks.push((0, aaveV4_1._getAaveV4AccountHasAnyBalance)(client, network, block, address, spoke.address)
67
+ .then((hasAny) => { existence[spoke.value] = hasAny; })
68
+ .catch(() => { existence[spoke.value] = false; }));
69
+ });
70
+ const compoundV3Markets = Object.values((0, markets_1.CompoundMarkets)(network)).filter((market) => market.chainIds.includes(network) && market.value !== types_1.CompoundVersions.CompoundV2);
71
+ const compoundV2Markets = [types_1.CompoundVersions.CompoundV2].map((version) => (0, markets_1.CompoundMarkets)(network)[version]).filter((market) => market.chainIds.includes(network));
72
+ compoundV2Markets.forEach((market) => {
73
+ tasks.push(balanceTask(market.value, () => (0, compoundV2_1._getCompoundV2AccountBalances)(client, network, block, false, address)));
74
+ });
75
+ compoundV3Markets.forEach((market) => {
76
+ tasks.push(balanceTask(market.value, () => (0, compoundV3_1._getCompoundV3AccountBalances)(client, network, block, false, address, market.baseMarketAddress)));
77
+ });
78
+ if (network === common_1.NetworkNumber.Eth) {
79
+ Object.values((0, markets_1.CrvUsdMarkets)(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
80
+ tasks.push(balanceTask(market.value, () => (0, curveUsd_1._getCrvUsdAccountBalances)(client, network, block, false, address, market.controllerAddress)));
81
+ });
82
+ }
83
+ const llamaLendMarkets = [common_1.NetworkNumber.Eth, common_1.NetworkNumber.Arb].includes(network) ? Object.values((0, markets_1.LlamaLendMarkets)(network)).filter((market) => market.chainIds.includes(network)) : [];
84
+ llamaLendMarkets.forEach((market) => {
85
+ tasks.push(balanceTask(market.value, () => (0, llamaLend_1._getLlamaLendAccountBalances)(client, network, block, false, address, market.controllerAddress)));
86
+ });
87
+ Object.values((0, markets_1.MorphoBlueMarkets)(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
88
+ tasks.push(balanceTask(market.value, () => (0, morphoBlue_1._getMorphoBlueAccountBalances)(client, network, block, false, address, market)));
89
+ });
90
+ yield Promise.all(tasks);
91
+ return existence;
92
+ });
93
+ }
@@ -6,3 +6,4 @@ export declare function getPortfolioData(provider: EthereumProvider, network: Ne
6
6
  rewardsData: any;
7
7
  markets: any;
8
8
  }>;
9
+ export * from './discovery';
@@ -1,4 +1,18 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
17
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
18
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -592,3 +606,4 @@ function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1)
592
606
  };
593
607
  });
594
608
  }
609
+ __exportStar(require("./discovery"), exports);
@@ -5,6 +5,12 @@ 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
+ /**
9
+ * Lightweight existence check for Loan Shifter / UI discovery.
10
+ * Reads only `getLoanData(spoke, user)` and returns whether any reserve has
11
+ * non-zero supplied or debt, avoiding full spoke/market data fetches.
12
+ */
13
+ export declare const _getAaveV4AccountHasAnyBalance: (provider: Client, network: NetworkNumber, block: Blockish, address: EthAddress, spokeAddress: EthAddress) => Promise<boolean>;
8
14
  export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
9
15
  export declare const _getAaveV4AccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
10
16
  export declare const getAaveV4AccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, spokeAddress: EthAddress, spokeData?: AaveV4SpokeData) => Promise<PositionBalances>;
@@ -235,6 +235,25 @@ export function _getAaveV4AccountData(provider_1, network_1, spokeData_1, addres
235
235
  riskPremiumBps });
236
236
  });
237
237
  }
238
+ /**
239
+ * Lightweight existence check for Loan Shifter / UI discovery.
240
+ * Reads only `getLoanData(spoke, user)` and returns whether any reserve has
241
+ * non-zero supplied or debt, avoiding full spoke/market data fetches.
242
+ */
243
+ export const _getAaveV4AccountHasAnyBalance = (provider, network, block, address, spokeAddress) => __awaiter(void 0, void 0, void 0, function* () {
244
+ if (!address || !spokeAddress)
245
+ return false;
246
+ const blockNumber = block === 'latest' ? 'latest' : Number(block);
247
+ const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
248
+ const loanData = yield viewContract.read.getLoanData([spokeAddress, address]);
249
+ const reserves = (loanData === null || loanData === void 0 ? void 0 : loanData.reserves) || [];
250
+ return reserves.some((reserveAsset) => {
251
+ var _a, _b, _c, _d, _e, _f, _g, _h;
252
+ const suppliedRaw = (_d = (_c = (_b = (_a = reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.supplied) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.supplied) !== null && _d !== void 0 ? _d : '0';
253
+ const debtRaw = (_h = (_g = (_f = (_e = reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.totalDebt) === null || _e === void 0 ? void 0 : _e.toString) === null || _f === void 0 ? void 0 : _f.call(_e)) !== null && _g !== void 0 ? _g : reserveAsset === null || reserveAsset === void 0 ? void 0 : reserveAsset.totalDebt) !== null && _h !== void 0 ? _h : '0';
254
+ return new Dec(suppliedRaw).gt(0) || new Dec(debtRaw).gt(0);
255
+ });
256
+ });
238
257
  export function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
239
258
  return __awaiter(this, arguments, void 0, function* (provider, network, marketData, address, blockNumber = 'latest') {
240
259
  return _getAaveV4AccountData(getViemProvider(provider, network), network, marketData, address, blockNumber);
@@ -16,7 +16,7 @@ import { ethToWeth, wethToEth } from '../services/utils';
16
16
  import { ZERO_ADDRESS } from '../constants';
17
17
  import { calculateBorrowingAssetLimit } from '../moneymarket';
18
18
  import { formatBaseData, formatMarketData, getCompoundV3AggregatedData, getIncentiveApys, } from '../helpers/compoundHelpers';
19
- import { COMPOUND_V3_ETH, COMPOUND_V3_USDBC, COMPOUND_V3_USDC, COMPOUND_V3_USDCe, COMPOUND_V3_USDT, } from '../markets/compound';
19
+ import { CompoundMarkets } from '../markets/compound';
20
20
  import { getEthPrice, getCompPrice, getUSDCPrice, getWstETHPrice, } from '../services/priceService';
21
21
  import { getViemProvider, setViemBlockNumber } from '../services/viem';
22
22
  const getSupportedAssetsAddressesForMarket = (selectedMarket, network) => selectedMarket.collAssets.map(asset => getAssetInfo(ethToWeth(asset), network)).map(addr => addr.address.toLowerCase());
@@ -108,13 +108,11 @@ export const _getCompoundV3AccountBalances = (provider, network, block, addressM
108
108
  if (!address) {
109
109
  return balances;
110
110
  }
111
- const market = ({
112
- [COMPOUND_V3_ETH(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_ETH(network),
113
- [COMPOUND_V3_USDC(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDC(network),
114
- [COMPOUND_V3_USDBC(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDBC(network),
115
- [COMPOUND_V3_USDT(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDT(network),
116
- [COMPOUND_V3_USDCe(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDCe(network),
117
- })[marketAddress.toLowerCase()];
111
+ const market = Object.values(CompoundMarkets(network)).find((m) => (m.baseMarketAddress
112
+ && m.baseMarketAddress !== ZERO_ADDRESS
113
+ && m.baseMarketAddress.toLowerCase() === marketAddress.toLowerCase()));
114
+ if (!market)
115
+ throw new Error(`Unsupported Compound V3 market address ${marketAddress} on network ${network}`);
118
116
  const loanInfoContract = CompV3ViewContractViem(provider, network, block);
119
117
  const loanInfo = yield loanInfoContract.read.getLoanData([market.baseMarketAddress, address], setViemBlockNumber(block));
120
118
  const baseAssetInfo = getAssetInfo(wethToEth(market.baseAsset), network);
@@ -0,0 +1,13 @@
1
+ import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
2
+ /**
3
+ * Cheap "does this address have a position here" check across all money-market protocols,
4
+ * keyed by each market/spoke's `.value`. Built for UI flows (e.g. Loan Shifter) that need to
5
+ * know *which* markets to show, without paying for a full manage-data fetch of every market.
6
+ *
7
+ * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
8
+ * per-market reads collapse into a handful of RPC round trips.
9
+ *
10
+ * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
11
+ * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
12
+ */
13
+ export declare function getUserPositionsExistence(provider: EthereumProvider, network: NetworkNumber, address: EthAddress, isSim?: boolean): Promise<Record<string, boolean>>;
@@ -0,0 +1,87 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import Dec from 'decimal.js';
11
+ import { NetworkNumber, } from '../types/common';
12
+ import { getViemProvider, setViemBlockNumber } from '../services/viem';
13
+ import { AaveMarkets, AaveV4Spokes, CompoundMarkets, CrvUsdMarkets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
14
+ import { AaveVersions, CompoundVersions } from '../types';
15
+ import { _getAaveV4AccountHasAnyBalance } from '../aaveV4';
16
+ import { _getCompoundV2AccountBalances } from '../compoundV2';
17
+ import { _getCompoundV3AccountBalances } from '../compoundV3';
18
+ import { _getCrvUsdAccountBalances } from '../curveUsd';
19
+ import { _getLlamaLendAccountBalances } from '../llamaLend';
20
+ import { _getMorphoBlueAccountBalances } from '../morphoBlue';
21
+ import { createViemContractFromConfigFunc } from '../contracts';
22
+ const hasAnyBalance = (balances) => ([balances.collateral, balances.debt].some((group) => Object.values(group || {}).some((amount) => new Dec(amount || '0').gt(0))));
23
+ /**
24
+ * Cheap "does this address have a position here" check across all money-market protocols,
25
+ * keyed by each market/spoke's `.value`. Built for UI flows (e.g. Loan Shifter) that need to
26
+ * know *which* markets to show, without paying for a full manage-data fetch of every market.
27
+ *
28
+ * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
29
+ * per-market reads collapse into a handful of RPC round trips.
30
+ *
31
+ * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
32
+ * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
33
+ */
34
+ export function getUserPositionsExistence(provider_1, network_1, address_1) {
35
+ return __awaiter(this, arguments, void 0, function* (provider, network, address, isSim = false) {
36
+ const existence = {};
37
+ if (!address)
38
+ return existence;
39
+ const client = getViemProvider(provider, network, { batch: { multicall: { batchSize: isSim ? 500000 : 2500000 } } });
40
+ const block = 'latest';
41
+ const tasks = [];
42
+ const aaveV3Markets = [AaveVersions.AaveV2, AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi]
43
+ .map((version) => AaveMarkets(network)[version])
44
+ .filter((market) => market.chainIds.includes(network) && market.lendingPoolAddress);
45
+ const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
46
+ [...aaveV3Markets, ...sparkMarkets].forEach((market) => {
47
+ if (!market.lendingPool)
48
+ return;
49
+ const lendingPoolContract = createViemContractFromConfigFunc(market.lendingPool, market.lendingPoolAddress)(client, network, block);
50
+ tasks.push(lendingPoolContract.read.getUserAccountData([address], setViemBlockNumber(block))
51
+ .then(([totalCollateral, totalDebt]) => {
52
+ existence[market.value] = new Dec(totalCollateral.toString()).gt(0) || new Dec(totalDebt.toString()).gt(0);
53
+ })
54
+ .catch(() => { existence[market.value] = false; }));
55
+ });
56
+ const balanceTask = (key, getBalances) => getBalances()
57
+ .then((balances) => { existence[key] = hasAnyBalance(balances); })
58
+ .catch(() => { existence[key] = false; });
59
+ Object.values(AaveV4Spokes(network)).filter((spoke) => spoke.chainIds.includes(network)).forEach((spoke) => {
60
+ tasks.push(_getAaveV4AccountHasAnyBalance(client, network, block, address, spoke.address)
61
+ .then((hasAny) => { existence[spoke.value] = hasAny; })
62
+ .catch(() => { existence[spoke.value] = false; }));
63
+ });
64
+ const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
65
+ const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
66
+ compoundV2Markets.forEach((market) => {
67
+ tasks.push(balanceTask(market.value, () => _getCompoundV2AccountBalances(client, network, block, false, address)));
68
+ });
69
+ compoundV3Markets.forEach((market) => {
70
+ tasks.push(balanceTask(market.value, () => _getCompoundV3AccountBalances(client, network, block, false, address, market.baseMarketAddress)));
71
+ });
72
+ if (network === NetworkNumber.Eth) {
73
+ Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
74
+ tasks.push(balanceTask(market.value, () => _getCrvUsdAccountBalances(client, network, block, false, address, market.controllerAddress)));
75
+ });
76
+ }
77
+ const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
78
+ llamaLendMarkets.forEach((market) => {
79
+ tasks.push(balanceTask(market.value, () => _getLlamaLendAccountBalances(client, network, block, false, address, market.controllerAddress)));
80
+ });
81
+ Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
82
+ tasks.push(balanceTask(market.value, () => _getMorphoBlueAccountBalances(client, network, block, false, address, market)));
83
+ });
84
+ yield Promise.all(tasks);
85
+ return existence;
86
+ });
87
+ }
@@ -6,3 +6,4 @@ export declare function getPortfolioData(provider: EthereumProvider, network: Ne
6
6
  rewardsData: any;
7
7
  markets: any;
8
8
  }>;
9
+ export * from './discovery';
@@ -586,3 +586,4 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
586
586
  };
587
587
  });
588
588
  }
589
+ export * from './discovery';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.124",
3
+ "version": "2.1.125",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -257,6 +257,33 @@ export async function _getAaveV4AccountData(provider: Client, network: NetworkNu
257
257
  };
258
258
  }
259
259
 
260
+ /**
261
+ * Lightweight existence check for Loan Shifter / UI discovery.
262
+ * Reads only `getLoanData(spoke, user)` and returns whether any reserve has
263
+ * non-zero supplied or debt, avoiding full spoke/market data fetches.
264
+ */
265
+ export const _getAaveV4AccountHasAnyBalance = async (
266
+ provider: Client,
267
+ network: NetworkNumber,
268
+ block: Blockish,
269
+ address: EthAddress,
270
+ spokeAddress: EthAddress,
271
+ ): Promise<boolean> => {
272
+ if (!address || !spokeAddress) return false;
273
+
274
+ const blockNumber = block === 'latest' ? 'latest' : Number(block);
275
+ const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
276
+
277
+ const loanData = await viewContract.read.getLoanData([spokeAddress, address]);
278
+ const reserves = loanData?.reserves || [];
279
+
280
+ return reserves.some((reserveAsset) => {
281
+ const suppliedRaw = reserveAsset?.supplied?.toString?.() ?? reserveAsset?.supplied ?? '0';
282
+ const debtRaw = reserveAsset?.totalDebt?.toString?.() ?? reserveAsset?.totalDebt ?? '0';
283
+ return new Dec(suppliedRaw).gt(0) || new Dec(debtRaw).gt(0);
284
+ });
285
+ };
286
+
260
287
  export async function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<any> {
261
288
  return _getAaveV4AccountData(getViemProvider(provider, network), network, marketData, address, blockNumber);
262
289
  }
@@ -19,9 +19,7 @@ import { calculateBorrowingAssetLimit } from '../moneymarket';
19
19
  import {
20
20
  formatBaseData, formatMarketData, getCompoundV3AggregatedData, getIncentiveApys,
21
21
  } from '../helpers/compoundHelpers';
22
- import {
23
- COMPOUND_V3_ETH, COMPOUND_V3_USDBC, COMPOUND_V3_USDC, COMPOUND_V3_USDCe, COMPOUND_V3_USDT,
24
- } from '../markets/compound';
22
+ import { CompoundMarkets } from '../markets/compound';
25
23
  import {
26
24
  getEthPrice, getCompPrice, getUSDCPrice, getWstETHPrice,
27
25
  } from '../services/priceService';
@@ -134,13 +132,13 @@ export const _getCompoundV3AccountBalances = async (provider: Client, network: N
134
132
  return balances;
135
133
  }
136
134
 
137
- const market = ({
138
- [COMPOUND_V3_ETH(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_ETH(network),
139
- [COMPOUND_V3_USDC(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDC(network),
140
- [COMPOUND_V3_USDBC(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDBC(network),
141
- [COMPOUND_V3_USDT(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDT(network),
142
- [COMPOUND_V3_USDCe(network).baseMarketAddress.toLowerCase()]: COMPOUND_V3_USDCe(network),
143
- })[marketAddress.toLowerCase()];
135
+ const market = Object.values(CompoundMarkets(network)).find((m) => (
136
+ m.baseMarketAddress
137
+ && m.baseMarketAddress !== ZERO_ADDRESS
138
+ && m.baseMarketAddress.toLowerCase() === marketAddress.toLowerCase()
139
+ ));
140
+
141
+ if (!market) throw new Error(`Unsupported Compound V3 market address ${marketAddress} on network ${network}`);
144
142
 
145
143
  const loanInfoContract = CompV3ViewContractViem(provider, network, block);
146
144
  const loanInfo = await loanInfoContract.read.getLoanData([market.baseMarketAddress, address], setViemBlockNumber(block));
@@ -0,0 +1,114 @@
1
+ import Dec from 'decimal.js';
2
+ import {
3
+ Blockish, EthAddress, EthereumProvider, NetworkNumber, PositionBalances,
4
+ } from '../types/common';
5
+ import { getViemProvider, setViemBlockNumber } from '../services/viem';
6
+ import {
7
+ AaveMarkets,
8
+ AaveV4Spokes,
9
+ CompoundMarkets,
10
+ CrvUsdMarkets,
11
+ LlamaLendMarkets,
12
+ MorphoBlueMarkets,
13
+ SparkMarkets,
14
+ } from '../markets';
15
+ import { AaveVersions, CompoundVersions } from '../types';
16
+ import { _getAaveV4AccountHasAnyBalance } from '../aaveV4';
17
+ import { _getCompoundV2AccountBalances } from '../compoundV2';
18
+ import { _getCompoundV3AccountBalances } from '../compoundV3';
19
+ import { _getCrvUsdAccountBalances } from '../curveUsd';
20
+ import { _getLlamaLendAccountBalances } from '../llamaLend';
21
+ import { _getMorphoBlueAccountBalances } from '../morphoBlue';
22
+ import { createViemContractFromConfigFunc } from '../contracts';
23
+
24
+ const hasAnyBalance = (balances: PositionBalances): boolean => (
25
+ [balances.collateral, balances.debt].some(
26
+ (group) => Object.values(group || {}).some((amount) => new Dec((amount as string) || '0').gt(0)),
27
+ )
28
+ );
29
+
30
+ /**
31
+ * Cheap "does this address have a position here" check across all money-market protocols,
32
+ * keyed by each market/spoke's `.value`. Built for UI flows (e.g. Loan Shifter) that need to
33
+ * know *which* markets to show, without paying for a full manage-data fetch of every market.
34
+ *
35
+ * Uses one shared, multicall-batched viem client (same pattern as getPortfolioData) so all
36
+ * per-market reads collapse into a handful of RPC round trips.
37
+ *
38
+ * Not covered: Maker, Liquity, Liquity V2, Fluid, Euler V2 — these already expose cheap,
39
+ * user-scoped discovery entry points elsewhere in the SDK (e.g. _getUserCdps, _getUserPositionsPortfolio).
40
+ */
41
+ export async function getUserPositionsExistence(
42
+ provider: EthereumProvider,
43
+ network: NetworkNumber,
44
+ address: EthAddress,
45
+ isSim = false,
46
+ ): Promise<Record<string, boolean>> {
47
+ const existence: Record<string, boolean> = {};
48
+ if (!address) return existence;
49
+
50
+ const client = getViemProvider(provider, network, { batch: { multicall: { batchSize: isSim ? 500_000 : 2_500_000 } } });
51
+ const block: Blockish = 'latest';
52
+
53
+ const tasks: Promise<void>[] = [];
54
+
55
+ const aaveV3Markets = [AaveVersions.AaveV2, AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi]
56
+ .map((version) => AaveMarkets(network)[version])
57
+ .filter((market) => market.chainIds.includes(network) && market.lendingPoolAddress);
58
+ const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
59
+
60
+ [...aaveV3Markets, ...sparkMarkets].forEach((market) => {
61
+ if (!market.lendingPool) return;
62
+ const lendingPoolContract = createViemContractFromConfigFunc(
63
+ market.lendingPool,
64
+ market.lendingPoolAddress as EthAddress,
65
+ )(client, network, block);
66
+ tasks.push(
67
+ lendingPoolContract.read.getUserAccountData([address], setViemBlockNumber(block))
68
+ .then(([totalCollateral, totalDebt]) => {
69
+ existence[market.value] = new Dec(totalCollateral.toString()).gt(0) || new Dec(totalDebt.toString()).gt(0);
70
+ })
71
+ .catch(() => { existence[market.value] = false; }),
72
+ );
73
+ });
74
+
75
+ const balanceTask = (key: string, getBalances: () => Promise<PositionBalances>) => getBalances()
76
+ .then((balances) => { existence[key] = hasAnyBalance(balances); })
77
+ .catch(() => { existence[key] = false; });
78
+
79
+ Object.values(AaveV4Spokes(network)).filter((spoke) => spoke.chainIds.includes(network)).forEach((spoke) => {
80
+ tasks.push(
81
+ _getAaveV4AccountHasAnyBalance(client, network, block, address, spoke.address)
82
+ .then((hasAny) => { existence[spoke.value] = hasAny; })
83
+ .catch(() => { existence[spoke.value] = false; }),
84
+ );
85
+ });
86
+
87
+ const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
88
+ const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
89
+
90
+ compoundV2Markets.forEach((market) => {
91
+ tasks.push(balanceTask(market.value, () => _getCompoundV2AccountBalances(client, network, block, false, address)));
92
+ });
93
+ compoundV3Markets.forEach((market) => {
94
+ tasks.push(balanceTask(market.value, () => _getCompoundV3AccountBalances(client, network, block, false, address, market.baseMarketAddress)));
95
+ });
96
+
97
+ if (network === NetworkNumber.Eth) {
98
+ Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
99
+ tasks.push(balanceTask(market.value, () => _getCrvUsdAccountBalances(client, network, block, false, address, market.controllerAddress)));
100
+ });
101
+ }
102
+
103
+ const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
104
+ llamaLendMarkets.forEach((market) => {
105
+ tasks.push(balanceTask(market.value, () => _getLlamaLendAccountBalances(client, network, block, false, address, market.controllerAddress)));
106
+ });
107
+
108
+ Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
109
+ tasks.push(balanceTask(market.value, () => _getMorphoBlueAccountBalances(client, network, block, false, address, market)));
110
+ });
111
+
112
+ await Promise.all(tasks);
113
+ return existence;
114
+ }
@@ -579,3 +579,5 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
579
579
  markets,
580
580
  };
581
581
  }
582
+
583
+ export * from './discovery';