@defisaver/positions-sdk 2.1.155 → 2.1.157-pos-history-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/aaveV3/index.d.ts +28 -0
- package/cjs/aaveV3/index.js +150 -1
- package/cjs/aaveV4/merkl.js +28 -10
- package/cjs/portfolio/index.js +6 -1
- package/cjs/types/merkl.d.ts +10 -0
- package/esm/aaveV3/index.d.ts +28 -0
- package/esm/aaveV3/index.js +146 -1
- package/esm/aaveV4/merkl.js +25 -10
- package/esm/portfolio/index.js +6 -1
- package/esm/types/merkl.d.ts +10 -0
- package/package.json +1 -1
- package/src/aaveV3/index.ts +190 -0
- package/src/aaveV4/merkl.ts +29 -7
- package/src/portfolio/index.ts +6 -1
- package/src/types/merkl.ts +10 -0
package/cjs/aaveV3/index.d.ts
CHANGED
|
@@ -32,6 +32,34 @@ export declare const EMPTY_AAVE_DATA: {
|
|
|
32
32
|
};
|
|
33
33
|
export declare const _getAaveV3AccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress) => Promise<PositionBalances>;
|
|
34
34
|
export declare const getAaveV3AccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress) => Promise<PositionBalances>;
|
|
35
|
+
export interface AaveV3ReserveTokenAddresses {
|
|
36
|
+
[symbol: string]: {
|
|
37
|
+
symbol: string;
|
|
38
|
+
underlyingAddress: EthAddress;
|
|
39
|
+
aTokenAddress: EthAddress;
|
|
40
|
+
stableDebtTokenAddress: EthAddress;
|
|
41
|
+
variableDebtTokenAddress: EthAddress;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface AaveV3HistoricalBalance {
|
|
45
|
+
block: number;
|
|
46
|
+
suppliedUsd: string;
|
|
47
|
+
borrowedUsd: string;
|
|
48
|
+
netUsd: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fetches the aToken / stable-debt / variable-debt token addresses for every asset in the market.
|
|
52
|
+
* These are effectively immutable per reserve, so fetch once and reuse across all history points.
|
|
53
|
+
*/
|
|
54
|
+
export declare const _getAaveV3ReserveTokenAddresses: (provider: Client, network: NetworkNumber, market: AaveMarketInfo) => Promise<AaveV3ReserveTokenAddresses>;
|
|
55
|
+
export declare const getAaveV3ReserveTokenAddresses: (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo) => Promise<AaveV3ReserveTokenAddresses>;
|
|
56
|
+
/**
|
|
57
|
+
* Computes a user's Aave v3 net USD balance (supplied collateral - borrowed debt) at a historical block,
|
|
58
|
+
* without touching the AaveV3View contract. Pass `reserveTokens` (from getAaveV3ReserveTokenAddresses)
|
|
59
|
+
* to avoid refetching token addresses for every point.
|
|
60
|
+
*/
|
|
61
|
+
export declare const _getAaveV3HistoricalBalance: (provider: Client, network: NetworkNumber, market: AaveMarketInfo, address: EthAddress, block: number, reserveTokens?: AaveV3ReserveTokenAddresses) => Promise<AaveV3HistoricalBalance>;
|
|
62
|
+
export declare const getAaveV3HistoricalBalance: (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo, address: EthAddress, block: number, reserveTokens?: AaveV3ReserveTokenAddresses) => Promise<AaveV3HistoricalBalance>;
|
|
35
63
|
export declare const _getAaveV3AccountData: (provider: Client, network: NetworkNumber, address: EthAddress, extractedState: any, blockNumber?: "latest" | number) => Promise<AaveV3PositionData>;
|
|
36
64
|
export declare const getAaveV3AccountData: (provider: EthereumProvider, network: NetworkNumber, address: EthAddress, extractedState: any, blockNumber?: "latest" | number) => Promise<AaveV3PositionData>;
|
|
37
65
|
export declare const getAaveV3FullPositionData: (provider: EthereumProvider, network: NetworkNumber, address: EthAddress, market: AaveMarketInfo) => Promise<AaveV3PositionData>;
|
package/cjs/aaveV3/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.getSghoData = exports.getMerkleCampaigns = exports.getMeritCampaigns = exports.getStakeAaveData = exports.fetchYearlyMeritApyForStakingGho = exports.REWARDABLE_ASSETS = exports.getAaveV3FullPositionData = exports.getAaveV3AccountData = exports._getAaveV3AccountData = exports.getAaveV3AccountBalances = exports._getAaveV3AccountBalances = exports.EMPTY_AAVE_DATA = exports.aaveV3EmodeCategoriesMapping = void 0;
|
|
15
|
+
exports.getSghoData = exports.getMerkleCampaigns = exports.getMeritCampaigns = exports.getStakeAaveData = exports.fetchYearlyMeritApyForStakingGho = exports.REWARDABLE_ASSETS = exports.getAaveV3FullPositionData = exports.getAaveV3AccountData = exports._getAaveV3AccountData = exports.getAaveV3HistoricalBalance = exports._getAaveV3HistoricalBalance = exports.getAaveV3ReserveTokenAddresses = exports._getAaveV3ReserveTokenAddresses = exports.getAaveV3AccountBalances = exports._getAaveV3AccountBalances = exports.EMPTY_AAVE_DATA = exports.aaveV3EmodeCategoriesMapping = void 0;
|
|
16
16
|
exports._getAaveV3MarketData = _getAaveV3MarketData;
|
|
17
17
|
exports.getAaveV3MarketData = getAaveV3MarketData;
|
|
18
18
|
const tokens_1 = require("@defisaver/tokens");
|
|
@@ -375,6 +375,155 @@ const _getAaveV3AccountBalances = (provider, network, block, addressMapping, add
|
|
|
375
375
|
exports._getAaveV3AccountBalances = _getAaveV3AccountBalances;
|
|
376
376
|
const getAaveV3AccountBalances = (provider, network, block, addressMapping, address) => __awaiter(void 0, void 0, void 0, function* () { return (0, exports._getAaveV3AccountBalances)((0, viem_1.getViemProvider)(provider, network), network, block, addressMapping, address); });
|
|
377
377
|
exports.getAaveV3AccountBalances = getAaveV3AccountBalances;
|
|
378
|
+
/**
|
|
379
|
+
* Historical net-balance helpers that bypass the AaveV3View contract.
|
|
380
|
+
*
|
|
381
|
+
* The View contract (and therefore `getAaveV3AccountData` / `getAaveV3AccountBalances`) can only be
|
|
382
|
+
* queried from its deployment block onwards, so it cannot read balances for positions older than that.
|
|
383
|
+
* The aTokens/debt tokens, the ProtocolDataProvider and the Aave price oracle all exist from Aave v3
|
|
384
|
+
* launch, so reading `balanceOf` on those tokens + the oracle price directly reaches much further back
|
|
385
|
+
* and costs ~1 multicall per point. Used to build a position balance-history chart.
|
|
386
|
+
*/
|
|
387
|
+
// Minimal Aave price oracle ABI (getAssetPrice returns the asset price in the market base currency).
|
|
388
|
+
const AAVE_ORACLE_ABI = [
|
|
389
|
+
{
|
|
390
|
+
inputs: [{ internalType: 'address', name: 'asset', type: 'address' }],
|
|
391
|
+
name: 'getAssetPrice',
|
|
392
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
393
|
+
stateMutability: 'view',
|
|
394
|
+
type: 'function',
|
|
395
|
+
},
|
|
396
|
+
];
|
|
397
|
+
/**
|
|
398
|
+
* Fetches the aToken / stable-debt / variable-debt token addresses for every asset in the market.
|
|
399
|
+
* These are effectively immutable per reserve, so fetch once and reuse across all history points.
|
|
400
|
+
*/
|
|
401
|
+
const _getAaveV3ReserveTokenAddresses = (provider, network, market) => __awaiter(void 0, void 0, void 0, function* () {
|
|
402
|
+
const symbols = market.assets;
|
|
403
|
+
const underlyingAddresses = symbols.map((a) => (0, tokens_1.getAssetInfo)((0, utils_1.getWrappedNativeAssetFromUnwrapped)(a), network).address);
|
|
404
|
+
// @ts-ignore market.protocolData is a valid config key at runtime
|
|
405
|
+
const dataProviderAbi = (0, contracts_1.getConfigContractAbi)(market.protocolData, network);
|
|
406
|
+
const contracts = underlyingAddresses.map((underlying) => ({
|
|
407
|
+
address: market.protocolDataAddress,
|
|
408
|
+
abi: dataProviderAbi,
|
|
409
|
+
functionName: 'getReserveTokensAddresses',
|
|
410
|
+
args: [underlying],
|
|
411
|
+
}));
|
|
412
|
+
// @ts-ignore
|
|
413
|
+
const results = yield provider.multicall({ contracts, allowFailure: true });
|
|
414
|
+
const mapping = {};
|
|
415
|
+
results.forEach((res, i) => {
|
|
416
|
+
if (res.status !== 'success' || !res.result)
|
|
417
|
+
return;
|
|
418
|
+
// outputs order: [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress]
|
|
419
|
+
const [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress] = res.result;
|
|
420
|
+
mapping[symbols[i]] = {
|
|
421
|
+
symbol: symbols[i],
|
|
422
|
+
underlyingAddress: underlyingAddresses[i],
|
|
423
|
+
aTokenAddress,
|
|
424
|
+
stableDebtTokenAddress,
|
|
425
|
+
variableDebtTokenAddress,
|
|
426
|
+
};
|
|
427
|
+
});
|
|
428
|
+
return mapping;
|
|
429
|
+
});
|
|
430
|
+
exports._getAaveV3ReserveTokenAddresses = _getAaveV3ReserveTokenAddresses;
|
|
431
|
+
const getAaveV3ReserveTokenAddresses = (provider, network, market) => __awaiter(void 0, void 0, void 0, function* () { return (0, exports._getAaveV3ReserveTokenAddresses)((0, viem_1.getViemProvider)(provider, network, { batch: { multicall: true } }), network, market); });
|
|
432
|
+
exports.getAaveV3ReserveTokenAddresses = getAaveV3ReserveTokenAddresses;
|
|
433
|
+
/**
|
|
434
|
+
* Computes a user's Aave v3 net USD balance (supplied collateral - borrowed debt) at a historical block,
|
|
435
|
+
* without touching the AaveV3View contract. Pass `reserveTokens` (from getAaveV3ReserveTokenAddresses)
|
|
436
|
+
* to avoid refetching token addresses for every point.
|
|
437
|
+
*/
|
|
438
|
+
const _getAaveV3HistoricalBalance = (provider, network, market, address, block, reserveTokens) => __awaiter(void 0, void 0, void 0, function* () {
|
|
439
|
+
const empty = {
|
|
440
|
+
block, suppliedUsd: '0', borrowedUsd: '0', netUsd: '0',
|
|
441
|
+
};
|
|
442
|
+
if (!address)
|
|
443
|
+
return empty;
|
|
444
|
+
const tokens = reserveTokens || (yield (0, exports._getAaveV3ReserveTokenAddresses)(provider, network, market));
|
|
445
|
+
const entries = Object.values(tokens);
|
|
446
|
+
if (!entries.length)
|
|
447
|
+
return empty;
|
|
448
|
+
const erc20Abi = (0, contracts_1.getConfigContractAbi)('Erc20');
|
|
449
|
+
const blockNumber = BigInt(block);
|
|
450
|
+
// supply = aToken.balanceOf, debt = variableDebtToken.balanceOf + stableDebtToken.balanceOf, all at the block.
|
|
451
|
+
const balanceContracts = entries.flatMap((e) => ([
|
|
452
|
+
{
|
|
453
|
+
address: e.aTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
address: e.variableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
address: e.stableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
460
|
+
},
|
|
461
|
+
]));
|
|
462
|
+
// @ts-ignore market.provider is a valid config key at runtime
|
|
463
|
+
const providerAbi = (0, contracts_1.getConfigContractAbi)(market.provider, network);
|
|
464
|
+
const [balanceResults, oracleAddress] = yield Promise.all([
|
|
465
|
+
// @ts-ignore
|
|
466
|
+
provider.multicall({ contracts: balanceContracts, allowFailure: true, blockNumber }),
|
|
467
|
+
// resolve the oracle that was active at the block (Aave can rotate the oracle over time).
|
|
468
|
+
// No catch: if this read fails (bad archive node, rate limit, ...) we must surface the failure
|
|
469
|
+
// rather than silently returning $0 — the caller renders a gap instead of a misleading zero.
|
|
470
|
+
// @ts-ignore readContract exists on the public client returned by getViemProvider
|
|
471
|
+
provider.readContract({
|
|
472
|
+
address: market.providerAddress,
|
|
473
|
+
abi: providerAbi,
|
|
474
|
+
functionName: 'getPriceOracle',
|
|
475
|
+
blockNumber,
|
|
476
|
+
}),
|
|
477
|
+
]);
|
|
478
|
+
// A totally failed balance read must not masquerade as a real $0 balance — throw so the caller
|
|
479
|
+
// can distinguish "fetch failed" (gap) from "position was empty" (genuine 0).
|
|
480
|
+
const anyBalanceRead = balanceResults.some((r) => (r === null || r === void 0 ? void 0 : r.status) === 'success');
|
|
481
|
+
if (!anyBalanceRead)
|
|
482
|
+
throw new Error(`AaveV3 historical balance: all balance reads failed at block ${block}`);
|
|
483
|
+
if (!oracleAddress)
|
|
484
|
+
throw new Error(`AaveV3 historical balance: oracle unavailable at block ${block}`);
|
|
485
|
+
const activeAssets = entries.map((e, i) => {
|
|
486
|
+
const supplyRes = balanceResults[i * 3];
|
|
487
|
+
const varDebtRes = balanceResults[(i * 3) + 1];
|
|
488
|
+
const stableDebtRes = balanceResults[(i * 3) + 2];
|
|
489
|
+
const supplied = (supplyRes === null || supplyRes === void 0 ? void 0 : supplyRes.status) === 'success' ? supplyRes.result.toString() : '0';
|
|
490
|
+
const varDebt = (varDebtRes === null || varDebtRes === void 0 ? void 0 : varDebtRes.status) === 'success' ? varDebtRes.result.toString() : '0';
|
|
491
|
+
const stableDebt = (stableDebtRes === null || stableDebtRes === void 0 ? void 0 : stableDebtRes.status) === 'success' ? stableDebtRes.result.toString() : '0';
|
|
492
|
+
const debt = new decimal_js_1.default(varDebt).add(stableDebt).toString();
|
|
493
|
+
return Object.assign(Object.assign({}, e), { supplied, debt });
|
|
494
|
+
}).filter((a) => a.supplied !== '0' || a.debt !== '0');
|
|
495
|
+
if (!activeAssets.length)
|
|
496
|
+
return empty;
|
|
497
|
+
const priceContracts = activeAssets.map((a) => ({
|
|
498
|
+
address: oracleAddress,
|
|
499
|
+
abi: AAVE_ORACLE_ABI,
|
|
500
|
+
functionName: 'getAssetPrice',
|
|
501
|
+
args: [a.underlyingAddress],
|
|
502
|
+
}));
|
|
503
|
+
// @ts-ignore
|
|
504
|
+
const priceResults = yield provider.multicall({ contracts: priceContracts, allowFailure: true, blockNumber });
|
|
505
|
+
let suppliedUsd = new decimal_js_1.default(0);
|
|
506
|
+
let borrowedUsd = new decimal_js_1.default(0);
|
|
507
|
+
activeAssets.forEach((a, i) => {
|
|
508
|
+
const priceRes = priceResults[i];
|
|
509
|
+
if ((priceRes === null || priceRes === void 0 ? void 0 : priceRes.status) !== 'success')
|
|
510
|
+
return;
|
|
511
|
+
const priceUsd = new decimal_js_1.default(priceRes.result.toString()).div(1e8); // Aave v3 base currency is USD with 8 decimals
|
|
512
|
+
if (a.supplied !== '0')
|
|
513
|
+
suppliedUsd = suppliedUsd.add(new decimal_js_1.default((0, tokens_1.assetAmountInEth)(a.supplied, a.symbol)).mul(priceUsd));
|
|
514
|
+
if (a.debt !== '0')
|
|
515
|
+
borrowedUsd = borrowedUsd.add(new decimal_js_1.default((0, tokens_1.assetAmountInEth)(a.debt, a.symbol)).mul(priceUsd));
|
|
516
|
+
});
|
|
517
|
+
return {
|
|
518
|
+
block,
|
|
519
|
+
suppliedUsd: suppliedUsd.toString(),
|
|
520
|
+
borrowedUsd: borrowedUsd.toString(),
|
|
521
|
+
netUsd: suppliedUsd.minus(borrowedUsd).toString(),
|
|
522
|
+
};
|
|
523
|
+
});
|
|
524
|
+
exports._getAaveV3HistoricalBalance = _getAaveV3HistoricalBalance;
|
|
525
|
+
const getAaveV3HistoricalBalance = (provider, network, market, address, block, reserveTokens) => __awaiter(void 0, void 0, void 0, function* () { return (0, exports._getAaveV3HistoricalBalance)((0, viem_1.getViemProvider)(provider, network, { batch: { multicall: true } }), network, market, address, block, reserveTokens); });
|
|
526
|
+
exports.getAaveV3HistoricalBalance = getAaveV3HistoricalBalance;
|
|
378
527
|
const _getAaveV3AccountData = (provider_1, network_1, address_1, extractedState_1, ...args_1) => __awaiter(void 0, [provider_1, network_1, address_1, extractedState_1, ...args_1], void 0, function* (provider, network, address, extractedState, blockNumber = 'latest') {
|
|
379
528
|
const { selectedMarket: market, assetsData, eModeCategoriesData, } = extractedState;
|
|
380
529
|
let payload = Object.assign(Object.assign({}, exports.EMPTY_AAVE_DATA), { lastUpdated: Date.now() });
|
package/cjs/aaveV4/merkl.js
CHANGED
|
@@ -8,14 +8,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.attachAaveV4MerklIncentives = exports.getAaveV4MerkleCampaigns = exports.buildAaveV4MerklRewardMap = void 0;
|
|
16
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
13
17
|
const moneymarket_1 = require("../moneymarket");
|
|
14
18
|
const merkl_1 = require("../services/merkl");
|
|
15
19
|
const types_1 = require("../types");
|
|
16
20
|
/**
|
|
17
21
|
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
18
|
-
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW
|
|
22
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW / AAVE_V4_HUB_NET_LENDING → hub asset
|
|
19
23
|
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke reserve
|
|
20
24
|
* Embedded campaign params provide the exact on-chain identifiers. Token addresses cannot safely
|
|
21
25
|
* identify Aave V4 rewards because one spoke can expose the same underlying from multiple hubs.
|
|
@@ -42,9 +46,19 @@ const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
|
42
46
|
.filter((o) => o.status === types_1.OpportunityStatus.LIVE)
|
|
43
47
|
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
44
48
|
.forEach((o) => {
|
|
45
|
-
var _a, _b;
|
|
49
|
+
var _a, _b, _c;
|
|
46
50
|
const side = o.action === types_1.OpportunityAction.BORROW ? types_1.IncentiveSide.Borrow : types_1.IncentiveSide.Supply;
|
|
47
|
-
const
|
|
51
|
+
const now = Date.now() / 1000;
|
|
52
|
+
const methods = (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.filter((c) => (c.startTimestamp === undefined || c.startTimestamp <= now)
|
|
53
|
+
&& (c.endTimestamp === undefined || c.endTimestamp > now)).map((c) => { var _a, _b; return (_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.distributionMethodParameters) === null || _b === void 0 ? void 0 : _b.distributionMethod; });
|
|
54
|
+
// An opportunity-level APR cannot be split between simultaneous net and additive campaigns.
|
|
55
|
+
if (side === types_1.IncentiveSide.Supply && (methods === null || methods === void 0 ? void 0 : methods.includes('AAVE_V4_NET_APR'))
|
|
56
|
+
&& methods.some((method) => !!method && method !== 'AAVE_V4_NET_APR'))
|
|
57
|
+
return;
|
|
58
|
+
const incentive = Object.assign(Object.assign({}, buildIncentive(o)), {
|
|
59
|
+
// Missing methods keep the existing target-yield behavior.
|
|
60
|
+
isAdditiveReward: side === types_1.IncentiveSide.Supply && !!(methods === null || methods === void 0 ? void 0 : methods.length)
|
|
61
|
+
&& methods.every((method) => !!method && method !== 'AAVE_V4_NET_APR') });
|
|
48
62
|
// one opportunity can span several campaigns (e.g. renewed periods), so campaign identity is
|
|
49
63
|
// collected per scope key before the reward entries are written
|
|
50
64
|
const idsByKey = {};
|
|
@@ -57,11 +71,13 @@ const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
|
57
71
|
idsByKey[key].parentCampaignIds.add(campaign.parentCampaignId);
|
|
58
72
|
};
|
|
59
73
|
if (o.type.includes('HUB')) {
|
|
60
|
-
(
|
|
61
|
-
var _a;
|
|
62
|
-
|
|
74
|
+
(_b = o.campaigns) === null || _b === void 0 ? void 0 : _b.forEach((c) => {
|
|
75
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
76
|
+
const hubAddress = (_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.hubAddress) !== null && _b !== void 0 ? _b : (_e = (_d = (_c = c.params) === null || _c === void 0 ? void 0 : _c.distributionMethodParameters) === null || _d === void 0 ? void 0 : _d.distributionSettings) === null || _e === void 0 ? void 0 : _e.hubAddress;
|
|
77
|
+
const assetId = (_g = (_f = c.params) === null || _f === void 0 ? void 0 : _f.assetId) !== null && _g !== void 0 ? _g : (_k = (_j = (_h = c.params) === null || _h === void 0 ? void 0 : _h.distributionMethodParameters) === null || _j === void 0 ? void 0 : _j.distributionSettings) === null || _k === void 0 ? void 0 : _k.assetId;
|
|
78
|
+
if (!hubAddress || assetId === undefined || assetId === null)
|
|
63
79
|
return;
|
|
64
|
-
collect(scopeKey(
|
|
80
|
+
collect(scopeKey(hubAddress, assetId), c);
|
|
65
81
|
});
|
|
66
82
|
Object.entries(idsByKey).forEach(([key, ids]) => {
|
|
67
83
|
if (!result.hub[key])
|
|
@@ -70,7 +86,7 @@ const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
|
70
86
|
});
|
|
71
87
|
}
|
|
72
88
|
else if (o.type.includes('SPOKE')) {
|
|
73
|
-
(
|
|
89
|
+
(_c = o.campaigns) === null || _c === void 0 ? void 0 : _c.forEach((c) => {
|
|
74
90
|
var _a;
|
|
75
91
|
if (!((_a = c.params) === null || _a === void 0 ? void 0 : _a.spokeAddress) || c.params.reserveId === undefined || c.params.reserveId === null)
|
|
76
92
|
return;
|
|
@@ -90,7 +106,7 @@ const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0,
|
|
|
90
106
|
try {
|
|
91
107
|
const opportunities = yield (0, merkl_1.fetchAllMerklOpportunities)({
|
|
92
108
|
mainProtocolId: 'aave',
|
|
93
|
-
type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
|
|
109
|
+
type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_HUB_NET_LENDING,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
|
|
94
110
|
status: types_1.OpportunityStatus.LIVE,
|
|
95
111
|
campaigns: 'true',
|
|
96
112
|
});
|
|
@@ -112,6 +128,8 @@ const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
|
|
|
112
128
|
const baseBorrow = asset.borrowIncentives || [];
|
|
113
129
|
const spokeScoped = spokeAddress ? campaigns.spoke[scopeKey(spokeAddress, asset.reserveId)] : undefined;
|
|
114
130
|
const hubScoped = asset.hub ? campaigns.hub[scopeKey(asset.hub, asset.assetId)] : undefined;
|
|
115
|
-
|
|
131
|
+
// Net-APR campaigns top up native yield; additive campaigns pay their APR on top.
|
|
132
|
+
const supplyRewards = (rewards = []) => rewards.map((reward) => (Object.assign(Object.assign({}, reward), { apy: reward.isAdditiveReward ? reward.apy : decimal_js_1.default.max(0, new decimal_js_1.default(reward.apy).minus(asset.supplyRate || 0)).toString() })));
|
|
133
|
+
return Object.assign(Object.assign({}, asset), { spokeSupplyIncentives: ((_a = spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.supply) === null || _a === void 0 ? void 0 : _a.length) ? [...baseSupply, ...supplyRewards(spokeScoped.supply)] : baseSupply, spokeBorrowIncentives: ((_b = spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.borrow) === null || _b === void 0 ? void 0 : _b.length) ? [...baseBorrow, ...spokeScoped.borrow] : baseBorrow, hubSupplyIncentives: ((_c = hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.supply) === null || _c === void 0 ? void 0 : _c.length) ? [...baseSupply, ...supplyRewards(hubScoped.supply)] : baseSupply, hubBorrowIncentives: ((_d = hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.borrow) === null || _d === void 0 ? void 0 : _d.length) ? [...baseBorrow, ...hubScoped.borrow] : baseBorrow });
|
|
116
134
|
};
|
|
117
135
|
exports.attachAaveV4MerklIncentives = attachAaveV4MerklIncentives;
|
package/cjs/portfolio/index.js
CHANGED
|
@@ -69,7 +69,12 @@ function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1)
|
|
|
69
69
|
const liquityV2Markets = [common_1.NetworkNumber.Eth].includes(network) ? Object.values((0, markets_1.LiquityV2Markets)(network)) : [];
|
|
70
70
|
const liquityV2MarketsStaking = [common_1.NetworkNumber.Eth].includes(network) ? Object.values((0, markets_1.LiquityV2Markets)(network)).filter(market => !market.isLegacy) : [];
|
|
71
71
|
const aaveV4Spokes = Object.values((0, markets_1.AaveV4Spokes)(network)).filter((market) => market.chainIds.includes(network));
|
|
72
|
-
|
|
72
|
+
// batchSize is viem's cap on a batch's raw subcall calldata (bytes); the JSON-RPC body ends up
|
|
73
|
+
// ~4x larger (hex + aggregate3 ABI + JSON overhead) and RPC providers reject bodies over
|
|
74
|
+
// ~2.5MB with HTTP 413, so keep this small enough that no single body gets near that.
|
|
75
|
+
// 250k gives the largest body of around 1.26MB - if we bump, we can save maybe 2-3 rpc calls but scaling takes a hit
|
|
76
|
+
// (e.g. adding new markets may result in body size going over alchemy body size limit)
|
|
77
|
+
const args = [network, { batch: { multicall: { batchSize: isSim ? 2000 : 250000 } } }];
|
|
73
78
|
const client = (0, viem_1.getViemProvider)(provider, ...args);
|
|
74
79
|
const defaultClient = (0, viem_1.getViemProvider)(defaultProvider, ...args);
|
|
75
80
|
const morphoMarketsData = {};
|
package/cjs/types/merkl.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare enum OpportunityStatus {
|
|
|
11
11
|
export type MerklCampaign = {
|
|
12
12
|
id: string;
|
|
13
13
|
campaignId: string;
|
|
14
|
+
startTimestamp?: number;
|
|
15
|
+
endTimestamp?: number;
|
|
14
16
|
/**
|
|
15
17
|
* Merkl-internal `id` of the parent campaign — set on child campaigns, which re-publish a hub
|
|
16
18
|
* (parent) campaign's reward scoped to a single spoke reserve. Absent on standalone campaigns,
|
|
@@ -24,6 +26,13 @@ export type MerklCampaign = {
|
|
|
24
26
|
hubAddress?: EthAddress;
|
|
25
27
|
hubAssetId?: string | number;
|
|
26
28
|
assetId?: string | number;
|
|
29
|
+
distributionMethodParameters?: {
|
|
30
|
+
distributionMethod?: string;
|
|
31
|
+
distributionSettings?: {
|
|
32
|
+
hubAddress?: EthAddress;
|
|
33
|
+
assetId?: string | number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
27
36
|
};
|
|
28
37
|
};
|
|
29
38
|
export type MerklOpportunity = {
|
|
@@ -102,6 +111,7 @@ export type MerkleRewardMap = Record<EthAddress, {
|
|
|
102
111
|
export type AaveV4MerklIncentive = IncentiveData & {
|
|
103
112
|
campaignIds?: string[];
|
|
104
113
|
parentCampaignIds?: string[];
|
|
114
|
+
isAdditiveReward?: boolean;
|
|
105
115
|
};
|
|
106
116
|
export type AaveV4MerklScopedReward = {
|
|
107
117
|
[side in IncentiveSide]?: AaveV4MerklIncentive[];
|
package/esm/aaveV3/index.d.ts
CHANGED
|
@@ -32,6 +32,34 @@ export declare const EMPTY_AAVE_DATA: {
|
|
|
32
32
|
};
|
|
33
33
|
export declare const _getAaveV3AccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress) => Promise<PositionBalances>;
|
|
34
34
|
export declare const getAaveV3AccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress) => Promise<PositionBalances>;
|
|
35
|
+
export interface AaveV3ReserveTokenAddresses {
|
|
36
|
+
[symbol: string]: {
|
|
37
|
+
symbol: string;
|
|
38
|
+
underlyingAddress: EthAddress;
|
|
39
|
+
aTokenAddress: EthAddress;
|
|
40
|
+
stableDebtTokenAddress: EthAddress;
|
|
41
|
+
variableDebtTokenAddress: EthAddress;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface AaveV3HistoricalBalance {
|
|
45
|
+
block: number;
|
|
46
|
+
suppliedUsd: string;
|
|
47
|
+
borrowedUsd: string;
|
|
48
|
+
netUsd: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fetches the aToken / stable-debt / variable-debt token addresses for every asset in the market.
|
|
52
|
+
* These are effectively immutable per reserve, so fetch once and reuse across all history points.
|
|
53
|
+
*/
|
|
54
|
+
export declare const _getAaveV3ReserveTokenAddresses: (provider: Client, network: NetworkNumber, market: AaveMarketInfo) => Promise<AaveV3ReserveTokenAddresses>;
|
|
55
|
+
export declare const getAaveV3ReserveTokenAddresses: (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo) => Promise<AaveV3ReserveTokenAddresses>;
|
|
56
|
+
/**
|
|
57
|
+
* Computes a user's Aave v3 net USD balance (supplied collateral - borrowed debt) at a historical block,
|
|
58
|
+
* without touching the AaveV3View contract. Pass `reserveTokens` (from getAaveV3ReserveTokenAddresses)
|
|
59
|
+
* to avoid refetching token addresses for every point.
|
|
60
|
+
*/
|
|
61
|
+
export declare const _getAaveV3HistoricalBalance: (provider: Client, network: NetworkNumber, market: AaveMarketInfo, address: EthAddress, block: number, reserveTokens?: AaveV3ReserveTokenAddresses) => Promise<AaveV3HistoricalBalance>;
|
|
62
|
+
export declare const getAaveV3HistoricalBalance: (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo, address: EthAddress, block: number, reserveTokens?: AaveV3ReserveTokenAddresses) => Promise<AaveV3HistoricalBalance>;
|
|
35
63
|
export declare const _getAaveV3AccountData: (provider: Client, network: NetworkNumber, address: EthAddress, extractedState: any, blockNumber?: "latest" | number) => Promise<AaveV3PositionData>;
|
|
36
64
|
export declare const getAaveV3AccountData: (provider: EthereumProvider, network: NetworkNumber, address: EthAddress, extractedState: any, blockNumber?: "latest" | number) => Promise<AaveV3PositionData>;
|
|
37
65
|
export declare const getAaveV3FullPositionData: (provider: EthereumProvider, network: NetworkNumber, address: EthAddress, market: AaveMarketInfo) => Promise<AaveV3PositionData>;
|
package/esm/aaveV3/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { assetAmountInEth, assetAmountInWei, getAssetInfo } from '@defisaver/tokens';
|
|
11
11
|
import Dec from 'decimal.js';
|
|
12
|
-
import { AaveIncentiveDataProviderV3ContractViem, AaveIncentivesControllerViem, AaveV3ViewContractViem, createViemContractFromConfigFunc, StkAAVEViem, } from '../contracts';
|
|
12
|
+
import { AaveIncentiveDataProviderV3ContractViem, AaveIncentivesControllerViem, AaveV3ViewContractViem, createViemContractFromConfigFunc, getConfigContractAbi, StkAAVEViem, } from '../contracts';
|
|
13
13
|
import { aaveAnyGetAggregatedPositionData, aaveV3IsInIsolationMode, aaveV3IsInSiloedMode } from '../helpers/aaveHelpers';
|
|
14
14
|
import { AAVE_V3 } from '../markets/aave';
|
|
15
15
|
import { aprToApy, calculateBorrowingAssetLimit } from '../moneymarket';
|
|
@@ -362,6 +362,151 @@ export const _getAaveV3AccountBalances = (provider, network, block, addressMappi
|
|
|
362
362
|
return balances;
|
|
363
363
|
});
|
|
364
364
|
export const getAaveV3AccountBalances = (provider, network, block, addressMapping, address) => __awaiter(void 0, void 0, void 0, function* () { return _getAaveV3AccountBalances(getViemProvider(provider, network), network, block, addressMapping, address); });
|
|
365
|
+
/**
|
|
366
|
+
* Historical net-balance helpers that bypass the AaveV3View contract.
|
|
367
|
+
*
|
|
368
|
+
* The View contract (and therefore `getAaveV3AccountData` / `getAaveV3AccountBalances`) can only be
|
|
369
|
+
* queried from its deployment block onwards, so it cannot read balances for positions older than that.
|
|
370
|
+
* The aTokens/debt tokens, the ProtocolDataProvider and the Aave price oracle all exist from Aave v3
|
|
371
|
+
* launch, so reading `balanceOf` on those tokens + the oracle price directly reaches much further back
|
|
372
|
+
* and costs ~1 multicall per point. Used to build a position balance-history chart.
|
|
373
|
+
*/
|
|
374
|
+
// Minimal Aave price oracle ABI (getAssetPrice returns the asset price in the market base currency).
|
|
375
|
+
const AAVE_ORACLE_ABI = [
|
|
376
|
+
{
|
|
377
|
+
inputs: [{ internalType: 'address', name: 'asset', type: 'address' }],
|
|
378
|
+
name: 'getAssetPrice',
|
|
379
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
380
|
+
stateMutability: 'view',
|
|
381
|
+
type: 'function',
|
|
382
|
+
},
|
|
383
|
+
];
|
|
384
|
+
/**
|
|
385
|
+
* Fetches the aToken / stable-debt / variable-debt token addresses for every asset in the market.
|
|
386
|
+
* These are effectively immutable per reserve, so fetch once and reuse across all history points.
|
|
387
|
+
*/
|
|
388
|
+
export const _getAaveV3ReserveTokenAddresses = (provider, network, market) => __awaiter(void 0, void 0, void 0, function* () {
|
|
389
|
+
const symbols = market.assets;
|
|
390
|
+
const underlyingAddresses = symbols.map((a) => getAssetInfo(getWrappedNativeAssetFromUnwrapped(a), network).address);
|
|
391
|
+
// @ts-ignore market.protocolData is a valid config key at runtime
|
|
392
|
+
const dataProviderAbi = getConfigContractAbi(market.protocolData, network);
|
|
393
|
+
const contracts = underlyingAddresses.map((underlying) => ({
|
|
394
|
+
address: market.protocolDataAddress,
|
|
395
|
+
abi: dataProviderAbi,
|
|
396
|
+
functionName: 'getReserveTokensAddresses',
|
|
397
|
+
args: [underlying],
|
|
398
|
+
}));
|
|
399
|
+
// @ts-ignore
|
|
400
|
+
const results = yield provider.multicall({ contracts, allowFailure: true });
|
|
401
|
+
const mapping = {};
|
|
402
|
+
results.forEach((res, i) => {
|
|
403
|
+
if (res.status !== 'success' || !res.result)
|
|
404
|
+
return;
|
|
405
|
+
// outputs order: [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress]
|
|
406
|
+
const [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress] = res.result;
|
|
407
|
+
mapping[symbols[i]] = {
|
|
408
|
+
symbol: symbols[i],
|
|
409
|
+
underlyingAddress: underlyingAddresses[i],
|
|
410
|
+
aTokenAddress,
|
|
411
|
+
stableDebtTokenAddress,
|
|
412
|
+
variableDebtTokenAddress,
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
return mapping;
|
|
416
|
+
});
|
|
417
|
+
export const getAaveV3ReserveTokenAddresses = (provider, network, market) => __awaiter(void 0, void 0, void 0, function* () { return _getAaveV3ReserveTokenAddresses(getViemProvider(provider, network, { batch: { multicall: true } }), network, market); });
|
|
418
|
+
/**
|
|
419
|
+
* Computes a user's Aave v3 net USD balance (supplied collateral - borrowed debt) at a historical block,
|
|
420
|
+
* without touching the AaveV3View contract. Pass `reserveTokens` (from getAaveV3ReserveTokenAddresses)
|
|
421
|
+
* to avoid refetching token addresses for every point.
|
|
422
|
+
*/
|
|
423
|
+
export const _getAaveV3HistoricalBalance = (provider, network, market, address, block, reserveTokens) => __awaiter(void 0, void 0, void 0, function* () {
|
|
424
|
+
const empty = {
|
|
425
|
+
block, suppliedUsd: '0', borrowedUsd: '0', netUsd: '0',
|
|
426
|
+
};
|
|
427
|
+
if (!address)
|
|
428
|
+
return empty;
|
|
429
|
+
const tokens = reserveTokens || (yield _getAaveV3ReserveTokenAddresses(provider, network, market));
|
|
430
|
+
const entries = Object.values(tokens);
|
|
431
|
+
if (!entries.length)
|
|
432
|
+
return empty;
|
|
433
|
+
const erc20Abi = getConfigContractAbi('Erc20');
|
|
434
|
+
const blockNumber = BigInt(block);
|
|
435
|
+
// supply = aToken.balanceOf, debt = variableDebtToken.balanceOf + stableDebtToken.balanceOf, all at the block.
|
|
436
|
+
const balanceContracts = entries.flatMap((e) => ([
|
|
437
|
+
{
|
|
438
|
+
address: e.aTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
address: e.variableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
address: e.stableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
445
|
+
},
|
|
446
|
+
]));
|
|
447
|
+
// @ts-ignore market.provider is a valid config key at runtime
|
|
448
|
+
const providerAbi = getConfigContractAbi(market.provider, network);
|
|
449
|
+
const [balanceResults, oracleAddress] = yield Promise.all([
|
|
450
|
+
// @ts-ignore
|
|
451
|
+
provider.multicall({ contracts: balanceContracts, allowFailure: true, blockNumber }),
|
|
452
|
+
// resolve the oracle that was active at the block (Aave can rotate the oracle over time).
|
|
453
|
+
// No catch: if this read fails (bad archive node, rate limit, ...) we must surface the failure
|
|
454
|
+
// rather than silently returning $0 — the caller renders a gap instead of a misleading zero.
|
|
455
|
+
// @ts-ignore readContract exists on the public client returned by getViemProvider
|
|
456
|
+
provider.readContract({
|
|
457
|
+
address: market.providerAddress,
|
|
458
|
+
abi: providerAbi,
|
|
459
|
+
functionName: 'getPriceOracle',
|
|
460
|
+
blockNumber,
|
|
461
|
+
}),
|
|
462
|
+
]);
|
|
463
|
+
// A totally failed balance read must not masquerade as a real $0 balance — throw so the caller
|
|
464
|
+
// can distinguish "fetch failed" (gap) from "position was empty" (genuine 0).
|
|
465
|
+
const anyBalanceRead = balanceResults.some((r) => (r === null || r === void 0 ? void 0 : r.status) === 'success');
|
|
466
|
+
if (!anyBalanceRead)
|
|
467
|
+
throw new Error(`AaveV3 historical balance: all balance reads failed at block ${block}`);
|
|
468
|
+
if (!oracleAddress)
|
|
469
|
+
throw new Error(`AaveV3 historical balance: oracle unavailable at block ${block}`);
|
|
470
|
+
const activeAssets = entries.map((e, i) => {
|
|
471
|
+
const supplyRes = balanceResults[i * 3];
|
|
472
|
+
const varDebtRes = balanceResults[(i * 3) + 1];
|
|
473
|
+
const stableDebtRes = balanceResults[(i * 3) + 2];
|
|
474
|
+
const supplied = (supplyRes === null || supplyRes === void 0 ? void 0 : supplyRes.status) === 'success' ? supplyRes.result.toString() : '0';
|
|
475
|
+
const varDebt = (varDebtRes === null || varDebtRes === void 0 ? void 0 : varDebtRes.status) === 'success' ? varDebtRes.result.toString() : '0';
|
|
476
|
+
const stableDebt = (stableDebtRes === null || stableDebtRes === void 0 ? void 0 : stableDebtRes.status) === 'success' ? stableDebtRes.result.toString() : '0';
|
|
477
|
+
const debt = new Dec(varDebt).add(stableDebt).toString();
|
|
478
|
+
return Object.assign(Object.assign({}, e), { supplied, debt });
|
|
479
|
+
}).filter((a) => a.supplied !== '0' || a.debt !== '0');
|
|
480
|
+
if (!activeAssets.length)
|
|
481
|
+
return empty;
|
|
482
|
+
const priceContracts = activeAssets.map((a) => ({
|
|
483
|
+
address: oracleAddress,
|
|
484
|
+
abi: AAVE_ORACLE_ABI,
|
|
485
|
+
functionName: 'getAssetPrice',
|
|
486
|
+
args: [a.underlyingAddress],
|
|
487
|
+
}));
|
|
488
|
+
// @ts-ignore
|
|
489
|
+
const priceResults = yield provider.multicall({ contracts: priceContracts, allowFailure: true, blockNumber });
|
|
490
|
+
let suppliedUsd = new Dec(0);
|
|
491
|
+
let borrowedUsd = new Dec(0);
|
|
492
|
+
activeAssets.forEach((a, i) => {
|
|
493
|
+
const priceRes = priceResults[i];
|
|
494
|
+
if ((priceRes === null || priceRes === void 0 ? void 0 : priceRes.status) !== 'success')
|
|
495
|
+
return;
|
|
496
|
+
const priceUsd = new Dec(priceRes.result.toString()).div(1e8); // Aave v3 base currency is USD with 8 decimals
|
|
497
|
+
if (a.supplied !== '0')
|
|
498
|
+
suppliedUsd = suppliedUsd.add(new Dec(assetAmountInEth(a.supplied, a.symbol)).mul(priceUsd));
|
|
499
|
+
if (a.debt !== '0')
|
|
500
|
+
borrowedUsd = borrowedUsd.add(new Dec(assetAmountInEth(a.debt, a.symbol)).mul(priceUsd));
|
|
501
|
+
});
|
|
502
|
+
return {
|
|
503
|
+
block,
|
|
504
|
+
suppliedUsd: suppliedUsd.toString(),
|
|
505
|
+
borrowedUsd: borrowedUsd.toString(),
|
|
506
|
+
netUsd: suppliedUsd.minus(borrowedUsd).toString(),
|
|
507
|
+
};
|
|
508
|
+
});
|
|
509
|
+
export const getAaveV3HistoricalBalance = (provider, network, market, address, block, reserveTokens) => __awaiter(void 0, void 0, void 0, function* () { return _getAaveV3HistoricalBalance(getViemProvider(provider, network, { batch: { multicall: true } }), network, market, address, block, reserveTokens); });
|
|
365
510
|
export const _getAaveV3AccountData = (provider_1, network_1, address_1, extractedState_1, ...args_1) => __awaiter(void 0, [provider_1, network_1, address_1, extractedState_1, ...args_1], void 0, function* (provider, network, address, extractedState, blockNumber = 'latest') {
|
|
366
511
|
const { selectedMarket: market, assetsData, eModeCategoriesData, } = extractedState;
|
|
367
512
|
let payload = Object.assign(Object.assign({}, EMPTY_AAVE_DATA), { lastUpdated: Date.now() });
|
package/esm/aaveV4/merkl.js
CHANGED
|
@@ -7,12 +7,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import Dec from 'decimal.js';
|
|
10
11
|
import { aprToApy } from '../moneymarket';
|
|
11
12
|
import { fetchAllMerklOpportunities } from '../services/merkl';
|
|
12
13
|
import { IncentiveKind, IncentiveSide, OpportunityAction, OpportunityStatus, } from '../types';
|
|
13
14
|
/**
|
|
14
15
|
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
15
|
-
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW
|
|
16
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW / AAVE_V4_HUB_NET_LENDING → hub asset
|
|
16
17
|
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke reserve
|
|
17
18
|
* Embedded campaign params provide the exact on-chain identifiers. Token addresses cannot safely
|
|
18
19
|
* identify Aave V4 rewards because one spoke can expose the same underlying from multiple hubs.
|
|
@@ -39,9 +40,19 @@ export const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
|
39
40
|
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
40
41
|
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
41
42
|
.forEach((o) => {
|
|
42
|
-
var _a, _b;
|
|
43
|
+
var _a, _b, _c;
|
|
43
44
|
const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
|
|
44
|
-
const
|
|
45
|
+
const now = Date.now() / 1000;
|
|
46
|
+
const methods = (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.filter((c) => (c.startTimestamp === undefined || c.startTimestamp <= now)
|
|
47
|
+
&& (c.endTimestamp === undefined || c.endTimestamp > now)).map((c) => { var _a, _b; return (_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.distributionMethodParameters) === null || _b === void 0 ? void 0 : _b.distributionMethod; });
|
|
48
|
+
// An opportunity-level APR cannot be split between simultaneous net and additive campaigns.
|
|
49
|
+
if (side === IncentiveSide.Supply && (methods === null || methods === void 0 ? void 0 : methods.includes('AAVE_V4_NET_APR'))
|
|
50
|
+
&& methods.some((method) => !!method && method !== 'AAVE_V4_NET_APR'))
|
|
51
|
+
return;
|
|
52
|
+
const incentive = Object.assign(Object.assign({}, buildIncentive(o)), {
|
|
53
|
+
// Missing methods keep the existing target-yield behavior.
|
|
54
|
+
isAdditiveReward: side === IncentiveSide.Supply && !!(methods === null || methods === void 0 ? void 0 : methods.length)
|
|
55
|
+
&& methods.every((method) => !!method && method !== 'AAVE_V4_NET_APR') });
|
|
45
56
|
// one opportunity can span several campaigns (e.g. renewed periods), so campaign identity is
|
|
46
57
|
// collected per scope key before the reward entries are written
|
|
47
58
|
const idsByKey = {};
|
|
@@ -54,11 +65,13 @@ export const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
|
54
65
|
idsByKey[key].parentCampaignIds.add(campaign.parentCampaignId);
|
|
55
66
|
};
|
|
56
67
|
if (o.type.includes('HUB')) {
|
|
57
|
-
(
|
|
58
|
-
var _a;
|
|
59
|
-
|
|
68
|
+
(_b = o.campaigns) === null || _b === void 0 ? void 0 : _b.forEach((c) => {
|
|
69
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
70
|
+
const hubAddress = (_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.hubAddress) !== null && _b !== void 0 ? _b : (_e = (_d = (_c = c.params) === null || _c === void 0 ? void 0 : _c.distributionMethodParameters) === null || _d === void 0 ? void 0 : _d.distributionSettings) === null || _e === void 0 ? void 0 : _e.hubAddress;
|
|
71
|
+
const assetId = (_g = (_f = c.params) === null || _f === void 0 ? void 0 : _f.assetId) !== null && _g !== void 0 ? _g : (_k = (_j = (_h = c.params) === null || _h === void 0 ? void 0 : _h.distributionMethodParameters) === null || _j === void 0 ? void 0 : _j.distributionSettings) === null || _k === void 0 ? void 0 : _k.assetId;
|
|
72
|
+
if (!hubAddress || assetId === undefined || assetId === null)
|
|
60
73
|
return;
|
|
61
|
-
collect(scopeKey(
|
|
74
|
+
collect(scopeKey(hubAddress, assetId), c);
|
|
62
75
|
});
|
|
63
76
|
Object.entries(idsByKey).forEach(([key, ids]) => {
|
|
64
77
|
if (!result.hub[key])
|
|
@@ -67,7 +80,7 @@ export const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
|
|
|
67
80
|
});
|
|
68
81
|
}
|
|
69
82
|
else if (o.type.includes('SPOKE')) {
|
|
70
|
-
(
|
|
83
|
+
(_c = o.campaigns) === null || _c === void 0 ? void 0 : _c.forEach((c) => {
|
|
71
84
|
var _a;
|
|
72
85
|
if (!((_a = c.params) === null || _a === void 0 ? void 0 : _a.spokeAddress) || c.params.reserveId === undefined || c.params.reserveId === null)
|
|
73
86
|
return;
|
|
@@ -86,7 +99,7 @@ export const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, v
|
|
|
86
99
|
try {
|
|
87
100
|
const opportunities = yield fetchAllMerklOpportunities({
|
|
88
101
|
mainProtocolId: 'aave',
|
|
89
|
-
type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
|
|
102
|
+
type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_HUB_NET_LENDING,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
|
|
90
103
|
status: OpportunityStatus.LIVE,
|
|
91
104
|
campaigns: 'true',
|
|
92
105
|
});
|
|
@@ -107,5 +120,7 @@ export const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
|
|
|
107
120
|
const baseBorrow = asset.borrowIncentives || [];
|
|
108
121
|
const spokeScoped = spokeAddress ? campaigns.spoke[scopeKey(spokeAddress, asset.reserveId)] : undefined;
|
|
109
122
|
const hubScoped = asset.hub ? campaigns.hub[scopeKey(asset.hub, asset.assetId)] : undefined;
|
|
110
|
-
|
|
123
|
+
// Net-APR campaigns top up native yield; additive campaigns pay their APR on top.
|
|
124
|
+
const supplyRewards = (rewards = []) => rewards.map((reward) => (Object.assign(Object.assign({}, reward), { apy: reward.isAdditiveReward ? reward.apy : Dec.max(0, new Dec(reward.apy).minus(asset.supplyRate || 0)).toString() })));
|
|
125
|
+
return Object.assign(Object.assign({}, asset), { spokeSupplyIncentives: ((_a = spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.supply) === null || _a === void 0 ? void 0 : _a.length) ? [...baseSupply, ...supplyRewards(spokeScoped.supply)] : baseSupply, spokeBorrowIncentives: ((_b = spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.borrow) === null || _b === void 0 ? void 0 : _b.length) ? [...baseBorrow, ...spokeScoped.borrow] : baseBorrow, hubSupplyIncentives: ((_c = hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.supply) === null || _c === void 0 ? void 0 : _c.length) ? [...baseSupply, ...supplyRewards(hubScoped.supply)] : baseSupply, hubBorrowIncentives: ((_d = hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.borrow) === null || _d === void 0 ? void 0 : _d.length) ? [...baseBorrow, ...hubScoped.borrow] : baseBorrow });
|
|
111
126
|
};
|
package/esm/portfolio/index.js
CHANGED
|
@@ -49,7 +49,12 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
|
|
|
49
49
|
const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
|
|
50
50
|
const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
|
|
51
51
|
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
52
|
-
|
|
52
|
+
// batchSize is viem's cap on a batch's raw subcall calldata (bytes); the JSON-RPC body ends up
|
|
53
|
+
// ~4x larger (hex + aggregate3 ABI + JSON overhead) and RPC providers reject bodies over
|
|
54
|
+
// ~2.5MB with HTTP 413, so keep this small enough that no single body gets near that.
|
|
55
|
+
// 250k gives the largest body of around 1.26MB - if we bump, we can save maybe 2-3 rpc calls but scaling takes a hit
|
|
56
|
+
// (e.g. adding new markets may result in body size going over alchemy body size limit)
|
|
57
|
+
const args = [network, { batch: { multicall: { batchSize: isSim ? 2000 : 250000 } } }];
|
|
53
58
|
const client = getViemProvider(provider, ...args);
|
|
54
59
|
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
55
60
|
const morphoMarketsData = {};
|
package/esm/types/merkl.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export declare enum OpportunityStatus {
|
|
|
11
11
|
export type MerklCampaign = {
|
|
12
12
|
id: string;
|
|
13
13
|
campaignId: string;
|
|
14
|
+
startTimestamp?: number;
|
|
15
|
+
endTimestamp?: number;
|
|
14
16
|
/**
|
|
15
17
|
* Merkl-internal `id` of the parent campaign — set on child campaigns, which re-publish a hub
|
|
16
18
|
* (parent) campaign's reward scoped to a single spoke reserve. Absent on standalone campaigns,
|
|
@@ -24,6 +26,13 @@ export type MerklCampaign = {
|
|
|
24
26
|
hubAddress?: EthAddress;
|
|
25
27
|
hubAssetId?: string | number;
|
|
26
28
|
assetId?: string | number;
|
|
29
|
+
distributionMethodParameters?: {
|
|
30
|
+
distributionMethod?: string;
|
|
31
|
+
distributionSettings?: {
|
|
32
|
+
hubAddress?: EthAddress;
|
|
33
|
+
assetId?: string | number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
27
36
|
};
|
|
28
37
|
};
|
|
29
38
|
export type MerklOpportunity = {
|
|
@@ -102,6 +111,7 @@ export type MerkleRewardMap = Record<EthAddress, {
|
|
|
102
111
|
export type AaveV4MerklIncentive = IncentiveData & {
|
|
103
112
|
campaignIds?: string[];
|
|
104
113
|
parentCampaignIds?: string[];
|
|
114
|
+
isAdditiveReward?: boolean;
|
|
105
115
|
};
|
|
106
116
|
export type AaveV4MerklScopedReward = {
|
|
107
117
|
[side in IncentiveSide]?: AaveV4MerklIncentive[];
|
package/package.json
CHANGED
package/src/aaveV3/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AaveIncentivesControllerViem,
|
|
7
7
|
AaveV3ViewContractViem,
|
|
8
8
|
createViemContractFromConfigFunc,
|
|
9
|
+
getConfigContractAbi,
|
|
9
10
|
StkAAVEViem,
|
|
10
11
|
} from '../contracts';
|
|
11
12
|
import { aaveAnyGetAggregatedPositionData, aaveV3IsInIsolationMode, aaveV3IsInSiloedMode } from '../helpers/aaveHelpers';
|
|
@@ -430,6 +431,195 @@ export const _getAaveV3AccountBalances = async (provider: Client, network: Netwo
|
|
|
430
431
|
|
|
431
432
|
export const getAaveV3AccountBalances = async (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress): Promise<PositionBalances> => _getAaveV3AccountBalances(getViemProvider(provider, network), network, block, addressMapping, address);
|
|
432
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Historical net-balance helpers that bypass the AaveV3View contract.
|
|
436
|
+
*
|
|
437
|
+
* The View contract (and therefore `getAaveV3AccountData` / `getAaveV3AccountBalances`) can only be
|
|
438
|
+
* queried from its deployment block onwards, so it cannot read balances for positions older than that.
|
|
439
|
+
* The aTokens/debt tokens, the ProtocolDataProvider and the Aave price oracle all exist from Aave v3
|
|
440
|
+
* launch, so reading `balanceOf` on those tokens + the oracle price directly reaches much further back
|
|
441
|
+
* and costs ~1 multicall per point. Used to build a position balance-history chart.
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
// Minimal Aave price oracle ABI (getAssetPrice returns the asset price in the market base currency).
|
|
445
|
+
const AAVE_ORACLE_ABI = [
|
|
446
|
+
{
|
|
447
|
+
inputs: [{ internalType: 'address', name: 'asset', type: 'address' }],
|
|
448
|
+
name: 'getAssetPrice',
|
|
449
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
450
|
+
stateMutability: 'view',
|
|
451
|
+
type: 'function',
|
|
452
|
+
},
|
|
453
|
+
] as const;
|
|
454
|
+
|
|
455
|
+
export interface AaveV3ReserveTokenAddresses {
|
|
456
|
+
[symbol: string]: {
|
|
457
|
+
symbol: string,
|
|
458
|
+
underlyingAddress: EthAddress,
|
|
459
|
+
aTokenAddress: EthAddress,
|
|
460
|
+
stableDebtTokenAddress: EthAddress,
|
|
461
|
+
variableDebtTokenAddress: EthAddress,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export interface AaveV3HistoricalBalance {
|
|
466
|
+
block: number,
|
|
467
|
+
suppliedUsd: string,
|
|
468
|
+
borrowedUsd: string,
|
|
469
|
+
netUsd: string,
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Fetches the aToken / stable-debt / variable-debt token addresses for every asset in the market.
|
|
474
|
+
* These are effectively immutable per reserve, so fetch once and reuse across all history points.
|
|
475
|
+
*/
|
|
476
|
+
export const _getAaveV3ReserveTokenAddresses = async (provider: Client, network: NetworkNumber, market: AaveMarketInfo): Promise<AaveV3ReserveTokenAddresses> => {
|
|
477
|
+
const symbols = market.assets;
|
|
478
|
+
const underlyingAddresses = symbols.map((a: string) => getAssetInfo(getWrappedNativeAssetFromUnwrapped(a), network).address as EthAddress);
|
|
479
|
+
// @ts-ignore market.protocolData is a valid config key at runtime
|
|
480
|
+
const dataProviderAbi = getConfigContractAbi(market.protocolData, network);
|
|
481
|
+
|
|
482
|
+
const contracts = underlyingAddresses.map((underlying) => ({
|
|
483
|
+
address: market.protocolDataAddress as EthAddress,
|
|
484
|
+
abi: dataProviderAbi,
|
|
485
|
+
functionName: 'getReserveTokensAddresses',
|
|
486
|
+
args: [underlying],
|
|
487
|
+
}));
|
|
488
|
+
|
|
489
|
+
// @ts-ignore
|
|
490
|
+
const results = await provider.multicall({ contracts, allowFailure: true });
|
|
491
|
+
|
|
492
|
+
const mapping: AaveV3ReserveTokenAddresses = {};
|
|
493
|
+
results.forEach((res: any, i: number) => {
|
|
494
|
+
if (res.status !== 'success' || !res.result) return;
|
|
495
|
+
// outputs order: [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress]
|
|
496
|
+
const [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress] = res.result as [EthAddress, EthAddress, EthAddress];
|
|
497
|
+
mapping[symbols[i]] = {
|
|
498
|
+
symbol: symbols[i],
|
|
499
|
+
underlyingAddress: underlyingAddresses[i],
|
|
500
|
+
aTokenAddress,
|
|
501
|
+
stableDebtTokenAddress,
|
|
502
|
+
variableDebtTokenAddress,
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
return mapping;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
export const getAaveV3ReserveTokenAddresses = async (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo): Promise<AaveV3ReserveTokenAddresses> => _getAaveV3ReserveTokenAddresses(getViemProvider(provider, network, { batch: { multicall: true } }), network, market);
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Computes a user's Aave v3 net USD balance (supplied collateral - borrowed debt) at a historical block,
|
|
513
|
+
* without touching the AaveV3View contract. Pass `reserveTokens` (from getAaveV3ReserveTokenAddresses)
|
|
514
|
+
* to avoid refetching token addresses for every point.
|
|
515
|
+
*/
|
|
516
|
+
export const _getAaveV3HistoricalBalance = async (
|
|
517
|
+
provider: Client,
|
|
518
|
+
network: NetworkNumber,
|
|
519
|
+
market: AaveMarketInfo,
|
|
520
|
+
address: EthAddress,
|
|
521
|
+
block: number,
|
|
522
|
+
reserveTokens?: AaveV3ReserveTokenAddresses,
|
|
523
|
+
): Promise<AaveV3HistoricalBalance> => {
|
|
524
|
+
const empty: AaveV3HistoricalBalance = {
|
|
525
|
+
block, suppliedUsd: '0', borrowedUsd: '0', netUsd: '0',
|
|
526
|
+
};
|
|
527
|
+
if (!address) return empty;
|
|
528
|
+
|
|
529
|
+
const tokens = reserveTokens || await _getAaveV3ReserveTokenAddresses(provider, network, market);
|
|
530
|
+
const entries = Object.values(tokens);
|
|
531
|
+
if (!entries.length) return empty;
|
|
532
|
+
|
|
533
|
+
const erc20Abi = getConfigContractAbi('Erc20');
|
|
534
|
+
const blockNumber = BigInt(block);
|
|
535
|
+
|
|
536
|
+
// supply = aToken.balanceOf, debt = variableDebtToken.balanceOf + stableDebtToken.balanceOf, all at the block.
|
|
537
|
+
const balanceContracts = entries.flatMap((e) => ([
|
|
538
|
+
{
|
|
539
|
+
address: e.aTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
address: e.variableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
address: e.stableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
546
|
+
},
|
|
547
|
+
]));
|
|
548
|
+
|
|
549
|
+
// @ts-ignore market.provider is a valid config key at runtime
|
|
550
|
+
const providerAbi = getConfigContractAbi(market.provider, network);
|
|
551
|
+
|
|
552
|
+
const [balanceResults, oracleAddress] = await Promise.all([
|
|
553
|
+
// @ts-ignore
|
|
554
|
+
provider.multicall({ contracts: balanceContracts, allowFailure: true, blockNumber }),
|
|
555
|
+
// resolve the oracle that was active at the block (Aave can rotate the oracle over time).
|
|
556
|
+
// No catch: if this read fails (bad archive node, rate limit, ...) we must surface the failure
|
|
557
|
+
// rather than silently returning $0 — the caller renders a gap instead of a misleading zero.
|
|
558
|
+
// @ts-ignore readContract exists on the public client returned by getViemProvider
|
|
559
|
+
provider.readContract({
|
|
560
|
+
address: market.providerAddress as EthAddress,
|
|
561
|
+
abi: providerAbi as any,
|
|
562
|
+
functionName: 'getPriceOracle',
|
|
563
|
+
blockNumber,
|
|
564
|
+
}),
|
|
565
|
+
]);
|
|
566
|
+
|
|
567
|
+
// A totally failed balance read must not masquerade as a real $0 balance — throw so the caller
|
|
568
|
+
// can distinguish "fetch failed" (gap) from "position was empty" (genuine 0).
|
|
569
|
+
const anyBalanceRead = (balanceResults as any[]).some((r) => r?.status === 'success');
|
|
570
|
+
if (!anyBalanceRead) throw new Error(`AaveV3 historical balance: all balance reads failed at block ${block}`);
|
|
571
|
+
if (!oracleAddress) throw new Error(`AaveV3 historical balance: oracle unavailable at block ${block}`);
|
|
572
|
+
|
|
573
|
+
const activeAssets = entries.map((e, i) => {
|
|
574
|
+
const supplyRes = balanceResults[i * 3];
|
|
575
|
+
const varDebtRes = balanceResults[(i * 3) + 1];
|
|
576
|
+
const stableDebtRes = balanceResults[(i * 3) + 2];
|
|
577
|
+
const supplied = supplyRes?.status === 'success' ? (supplyRes.result as bigint).toString() : '0';
|
|
578
|
+
const varDebt = varDebtRes?.status === 'success' ? (varDebtRes.result as bigint).toString() : '0';
|
|
579
|
+
const stableDebt = stableDebtRes?.status === 'success' ? (stableDebtRes.result as bigint).toString() : '0';
|
|
580
|
+
const debt = new Dec(varDebt).add(stableDebt).toString();
|
|
581
|
+
return { ...e, supplied, debt };
|
|
582
|
+
}).filter((a) => a.supplied !== '0' || a.debt !== '0');
|
|
583
|
+
|
|
584
|
+
if (!activeAssets.length) return empty;
|
|
585
|
+
|
|
586
|
+
const priceContracts = activeAssets.map((a) => ({
|
|
587
|
+
address: oracleAddress as EthAddress,
|
|
588
|
+
abi: AAVE_ORACLE_ABI,
|
|
589
|
+
functionName: 'getAssetPrice',
|
|
590
|
+
args: [a.underlyingAddress],
|
|
591
|
+
}));
|
|
592
|
+
|
|
593
|
+
// @ts-ignore
|
|
594
|
+
const priceResults = await provider.multicall({ contracts: priceContracts, allowFailure: true, blockNumber });
|
|
595
|
+
|
|
596
|
+
let suppliedUsd = new Dec(0);
|
|
597
|
+
let borrowedUsd = new Dec(0);
|
|
598
|
+
activeAssets.forEach((a, i) => {
|
|
599
|
+
const priceRes = priceResults[i];
|
|
600
|
+
if (priceRes?.status !== 'success') return;
|
|
601
|
+
const priceUsd = new Dec((priceRes.result as bigint).toString()).div(1e8); // Aave v3 base currency is USD with 8 decimals
|
|
602
|
+
if (a.supplied !== '0') suppliedUsd = suppliedUsd.add(new Dec(assetAmountInEth(a.supplied, a.symbol)).mul(priceUsd));
|
|
603
|
+
if (a.debt !== '0') borrowedUsd = borrowedUsd.add(new Dec(assetAmountInEth(a.debt, a.symbol)).mul(priceUsd));
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
return {
|
|
607
|
+
block,
|
|
608
|
+
suppliedUsd: suppliedUsd.toString(),
|
|
609
|
+
borrowedUsd: borrowedUsd.toString(),
|
|
610
|
+
netUsd: suppliedUsd.minus(borrowedUsd).toString(),
|
|
611
|
+
};
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
export const getAaveV3HistoricalBalance = async (
|
|
615
|
+
provider: EthereumProvider,
|
|
616
|
+
network: NetworkNumber,
|
|
617
|
+
market: AaveMarketInfo,
|
|
618
|
+
address: EthAddress,
|
|
619
|
+
block: number,
|
|
620
|
+
reserveTokens?: AaveV3ReserveTokenAddresses,
|
|
621
|
+
): Promise<AaveV3HistoricalBalance> => _getAaveV3HistoricalBalance(getViemProvider(provider, network, { batch: { multicall: true } }), network, market, address, block, reserveTokens);
|
|
622
|
+
|
|
433
623
|
export const _getAaveV3AccountData = async (provider: Client, network: NetworkNumber, address: EthAddress, extractedState: any, blockNumber: 'latest' | number = 'latest'): Promise<AaveV3PositionData> => {
|
|
434
624
|
const {
|
|
435
625
|
selectedMarket: market, assetsData, eModeCategoriesData,
|
package/src/aaveV4/merkl.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
1
2
|
import { aprToApy } from '../moneymarket';
|
|
2
3
|
import { fetchAllMerklOpportunities } from '../services/merkl';
|
|
3
4
|
import {
|
|
4
5
|
AaveV4MerklRewardMap,
|
|
6
|
+
AaveV4MerklIncentive,
|
|
5
7
|
AaveV4ReserveAssetData,
|
|
6
8
|
IncentiveData,
|
|
7
9
|
IncentiveKind,
|
|
@@ -15,7 +17,7 @@ import {
|
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
18
|
-
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW
|
|
20
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW / AAVE_V4_HUB_NET_LENDING → hub asset
|
|
19
21
|
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke reserve
|
|
20
22
|
* Embedded campaign params provide the exact on-chain identifiers. Token addresses cannot safely
|
|
21
23
|
* identify Aave V4 rewards because one spoke can expose the same underlying from multiple hubs.
|
|
@@ -46,7 +48,20 @@ export const buildAaveV4MerklRewardMap = (opportunities: MerklOpportunity[], cha
|
|
|
46
48
|
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
47
49
|
.forEach((o) => {
|
|
48
50
|
const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
|
|
49
|
-
const
|
|
51
|
+
const now = Date.now() / 1000;
|
|
52
|
+
const methods = o.campaigns
|
|
53
|
+
?.filter((c) => (c.startTimestamp === undefined || c.startTimestamp <= now)
|
|
54
|
+
&& (c.endTimestamp === undefined || c.endTimestamp > now))
|
|
55
|
+
.map((c) => c.params?.distributionMethodParameters?.distributionMethod);
|
|
56
|
+
// An opportunity-level APR cannot be split between simultaneous net and additive campaigns.
|
|
57
|
+
if (side === IncentiveSide.Supply && methods?.includes('AAVE_V4_NET_APR')
|
|
58
|
+
&& methods.some((method) => !!method && method !== 'AAVE_V4_NET_APR')) return;
|
|
59
|
+
const incentive = {
|
|
60
|
+
...buildIncentive(o),
|
|
61
|
+
// Missing methods keep the existing target-yield behavior.
|
|
62
|
+
isAdditiveReward: side === IncentiveSide.Supply && !!methods?.length
|
|
63
|
+
&& methods.every((method) => !!method && method !== 'AAVE_V4_NET_APR'),
|
|
64
|
+
};
|
|
50
65
|
// one opportunity can span several campaigns (e.g. renewed periods), so campaign identity is
|
|
51
66
|
// collected per scope key before the reward entries are written
|
|
52
67
|
const idsByKey: Record<string, { campaignIds: Set<string>, parentCampaignIds: Set<string> }> = {};
|
|
@@ -58,8 +73,10 @@ export const buildAaveV4MerklRewardMap = (opportunities: MerklOpportunity[], cha
|
|
|
58
73
|
|
|
59
74
|
if (o.type.includes('HUB')) {
|
|
60
75
|
o.campaigns?.forEach((c) => {
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
const hubAddress = c.params?.hubAddress ?? c.params?.distributionMethodParameters?.distributionSettings?.hubAddress;
|
|
77
|
+
const assetId = c.params?.assetId ?? c.params?.distributionMethodParameters?.distributionSettings?.assetId;
|
|
78
|
+
if (!hubAddress || assetId === undefined || assetId === null) return;
|
|
79
|
+
collect(scopeKey(hubAddress, assetId), c);
|
|
63
80
|
});
|
|
64
81
|
Object.entries(idsByKey).forEach(([key, ids]) => {
|
|
65
82
|
if (!result.hub[key]) result.hub[key] = {};
|
|
@@ -84,7 +101,7 @@ export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<
|
|
|
84
101
|
try {
|
|
85
102
|
const opportunities = await fetchAllMerklOpportunities({
|
|
86
103
|
mainProtocolId: 'aave',
|
|
87
|
-
type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
|
|
104
|
+
type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_HUB_NET_LENDING,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
|
|
88
105
|
status: OpportunityStatus.LIVE,
|
|
89
106
|
campaigns: 'true',
|
|
90
107
|
});
|
|
@@ -105,12 +122,17 @@ export const attachAaveV4MerklIncentives = (asset: AaveV4ReserveAssetData, spoke
|
|
|
105
122
|
|
|
106
123
|
const spokeScoped = spokeAddress ? campaigns.spoke[scopeKey(spokeAddress, asset.reserveId)] : undefined;
|
|
107
124
|
const hubScoped = asset.hub ? campaigns.hub[scopeKey(asset.hub, asset.assetId)] : undefined;
|
|
125
|
+
// Net-APR campaigns top up native yield; additive campaigns pay their APR on top.
|
|
126
|
+
const supplyRewards = (rewards: AaveV4MerklIncentive[] = []) => rewards.map((reward) => ({
|
|
127
|
+
...reward,
|
|
128
|
+
apy: reward.isAdditiveReward ? reward.apy : Dec.max(0, new Dec(reward.apy).minus(asset.supplyRate || 0)).toString(),
|
|
129
|
+
}));
|
|
108
130
|
|
|
109
131
|
return {
|
|
110
132
|
...asset,
|
|
111
|
-
spokeSupplyIncentives: spokeScoped?.supply?.length ? [...baseSupply, ...spokeScoped.supply] : baseSupply,
|
|
133
|
+
spokeSupplyIncentives: spokeScoped?.supply?.length ? [...baseSupply, ...supplyRewards(spokeScoped.supply)] : baseSupply,
|
|
112
134
|
spokeBorrowIncentives: spokeScoped?.borrow?.length ? [...baseBorrow, ...spokeScoped.borrow] : baseBorrow,
|
|
113
|
-
hubSupplyIncentives: hubScoped?.supply?.length ? [...baseSupply, ...hubScoped.supply] : baseSupply,
|
|
135
|
+
hubSupplyIncentives: hubScoped?.supply?.length ? [...baseSupply, ...supplyRewards(hubScoped.supply)] : baseSupply,
|
|
114
136
|
hubBorrowIncentives: hubScoped?.borrow?.length ? [...baseBorrow, ...hubScoped.borrow] : baseBorrow,
|
|
115
137
|
};
|
|
116
138
|
};
|
package/src/portfolio/index.ts
CHANGED
|
@@ -74,7 +74,12 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
74
74
|
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
// batchSize is viem's cap on a batch's raw subcall calldata (bytes); the JSON-RPC body ends up
|
|
78
|
+
// ~4x larger (hex + aggregate3 ABI + JSON overhead) and RPC providers reject bodies over
|
|
79
|
+
// ~2.5MB with HTTP 413, so keep this small enough that no single body gets near that.
|
|
80
|
+
// 250k gives the largest body of around 1.26MB - if we bump, we can save maybe 2-3 rpc calls but scaling takes a hit
|
|
81
|
+
// (e.g. adding new markets may result in body size going over alchemy body size limit)
|
|
82
|
+
const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 250_000 } } }];
|
|
78
83
|
const client = getViemProvider(provider, ...args);
|
|
79
84
|
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
80
85
|
|
package/src/types/merkl.ts
CHANGED
|
@@ -14,6 +14,8 @@ export enum OpportunityStatus {
|
|
|
14
14
|
export type MerklCampaign = {
|
|
15
15
|
id: string;
|
|
16
16
|
campaignId: string;
|
|
17
|
+
startTimestamp?: number;
|
|
18
|
+
endTimestamp?: number;
|
|
17
19
|
/**
|
|
18
20
|
* Merkl-internal `id` of the parent campaign — set on child campaigns, which re-publish a hub
|
|
19
21
|
* (parent) campaign's reward scoped to a single spoke reserve. Absent on standalone campaigns,
|
|
@@ -27,6 +29,13 @@ export type MerklCampaign = {
|
|
|
27
29
|
hubAddress?: EthAddress;
|
|
28
30
|
hubAssetId?: string | number;
|
|
29
31
|
assetId?: string | number;
|
|
32
|
+
distributionMethodParameters?: {
|
|
33
|
+
distributionMethod?: string;
|
|
34
|
+
distributionSettings?: {
|
|
35
|
+
hubAddress?: EthAddress;
|
|
36
|
+
assetId?: string | number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
30
39
|
};
|
|
31
40
|
};
|
|
32
41
|
|
|
@@ -100,6 +109,7 @@ export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; bo
|
|
|
100
109
|
export type AaveV4MerklIncentive = IncentiveData & {
|
|
101
110
|
campaignIds?: string[];
|
|
102
111
|
parentCampaignIds?: string[];
|
|
112
|
+
isAdditiveReward?: boolean;
|
|
103
113
|
};
|
|
104
114
|
|
|
105
115
|
export type AaveV4MerklScopedReward = { [side in IncentiveSide]?: AaveV4MerklIncentive[] };
|