@defisaver/positions-sdk 2.1.103 → 2.1.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/aaveV4/index.d.ts +1 -0
- package/cjs/aaveV4/index.js +8 -3
- package/cjs/aaveV4/merkl.d.ts +8 -0
- package/cjs/aaveV4/merkl.js +90 -0
- package/cjs/types/aaveV4.d.ts +10 -0
- package/cjs/types/merkl.d.ts +14 -1
- package/esm/aaveV4/index.d.ts +1 -0
- package/esm/aaveV4/index.js +6 -2
- package/esm/aaveV4/merkl.d.ts +8 -0
- package/esm/aaveV4/merkl.js +85 -0
- package/esm/types/aaveV4.d.ts +10 -0
- package/esm/types/merkl.d.ts +14 -1
- package/package.json +1 -1
- package/src/aaveV4/index.ts +7 -2
- package/src/aaveV4/merkl.ts +92 -0
- package/src/types/aaveV4.ts +10 -0
- package/src/types/merkl.ts +14 -2
package/cjs/aaveV4/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo, EthAddress, EthereumProvider, NetworkNumber } from '../types';
|
|
3
3
|
export * as lend from './lend';
|
|
4
|
+
export { getAaveV4MerkleCampaigns } from './merkl';
|
|
4
5
|
export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
5
6
|
export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
6
7
|
export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
|
package/cjs/aaveV4/index.js
CHANGED
|
@@ -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.lend = void 0;
|
|
48
|
+
exports.getAaveV4MerkleCampaigns = exports.lend = void 0;
|
|
49
49
|
exports._getAaveV4SpokeData = _getAaveV4SpokeData;
|
|
50
50
|
exports.getAaveV4SpokeData = getAaveV4SpokeData;
|
|
51
51
|
exports._getAaveV4AccountData = _getAaveV4AccountData;
|
|
@@ -61,7 +61,10 @@ const utils_1 = require("../services/utils");
|
|
|
61
61
|
const aaveV4Helpers_1 = require("../helpers/aaveV4Helpers");
|
|
62
62
|
const aaveV4_1 = require("../markets/aaveV4");
|
|
63
63
|
const moneymarket_1 = require("../moneymarket");
|
|
64
|
+
const merkl_1 = require("./merkl");
|
|
64
65
|
exports.lend = __importStar(require("./lend"));
|
|
66
|
+
var merkl_2 = require("./merkl");
|
|
67
|
+
Object.defineProperty(exports, "getAaveV4MerkleCampaigns", { enumerable: true, get: function () { return merkl_2.getAaveV4MerkleCampaigns; } });
|
|
65
68
|
const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
69
|
const hubData = yield viewContract.read.getHubAllAssetsData([hubAddress]);
|
|
67
70
|
return {
|
|
@@ -173,15 +176,17 @@ function _getAaveV4SpokeData(provider_1, network_1, market_1) {
|
|
|
173
176
|
return __awaiter(this, arguments, void 0, function* (provider, network, market, blockNumber = 'latest') {
|
|
174
177
|
const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network, blockNumber);
|
|
175
178
|
const hubsData = {};
|
|
176
|
-
const [spokeData] = yield Promise.all([
|
|
179
|
+
const [spokeData, merklCampaigns] = yield Promise.all([
|
|
177
180
|
viewContract.read.getSpokeDataFull([market.address]),
|
|
181
|
+
(0, merkl_1.getAaveV4MerkleCampaigns)(network),
|
|
178
182
|
...market.hubs.map((hubAddress) => __awaiter(this, void 0, void 0, function* () {
|
|
179
183
|
hubsData[hubAddress] = yield fetchHubData(viewContract, hubAddress);
|
|
180
184
|
})),
|
|
181
185
|
]);
|
|
182
186
|
const reserveAssetsArray = yield Promise.all(spokeData[1].map((reserveAssetOnChain, index) => __awaiter(this, void 0, void 0, function* () { return formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], index, +spokeData[0].oracleDecimals.toString(), network); })));
|
|
187
|
+
const enrichedAssets = reserveAssetsArray.map((asset) => (0, merkl_1.attachAaveV4MerklIncentives)(asset, market.address, merklCampaigns));
|
|
183
188
|
return {
|
|
184
|
-
assetsData:
|
|
189
|
+
assetsData: enrichedAssets.reduce((acc, reserveAsset) => {
|
|
185
190
|
acc[`${reserveAsset.symbol}-${reserveAsset.reserveId}`] = reserveAsset;
|
|
186
191
|
return acc;
|
|
187
192
|
}, {}),
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AaveV4MerklRewardMap, AaveV4ReserveAssetData } from '../types';
|
|
2
|
+
import { NetworkNumber } from '../types/common';
|
|
3
|
+
export declare const getAaveV4MerkleCampaigns: (chainId: NetworkNumber) => Promise<AaveV4MerklRewardMap>;
|
|
4
|
+
/**
|
|
5
|
+
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
|
6
|
+
* intrinsic (staking) incentives, so each surface can render base yield + the rewards that apply to it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const attachAaveV4MerklIncentives: (asset: AaveV4ReserveAssetData, spokeAddress: string, campaigns: AaveV4MerklRewardMap) => AaveV4ReserveAssetData;
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.attachAaveV4MerklIncentives = exports.getAaveV4MerkleCampaigns = void 0;
|
|
13
|
+
const moneymarket_1 = require("../moneymarket");
|
|
14
|
+
const utils_1 = require("../services/utils");
|
|
15
|
+
const types_1 = require("../types");
|
|
16
|
+
/**
|
|
17
|
+
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
18
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying token)
|
|
19
|
+
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
|
|
20
|
+
* Hub campaigns identify the underlying via `tokens[0]`; spoke campaigns identify the spoke via `explorerAddress`.
|
|
21
|
+
*/
|
|
22
|
+
const spokeKey = (spokeAddress, underlying) => `${spokeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
|
|
23
|
+
const buildIncentive = (opportunity) => {
|
|
24
|
+
var _a, _b, _c, _d, _e;
|
|
25
|
+
const rewardToken = (_c = (_b = (_a = opportunity.rewardsRecord) === null || _a === void 0 ? void 0 : _a.breakdowns) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.token;
|
|
26
|
+
const token = (rewardToken === null || rewardToken === void 0 ? void 0 : rewardToken.symbol) || ((_e = (_d = opportunity.tokens) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.symbol) || '';
|
|
27
|
+
return {
|
|
28
|
+
apy: (0, moneymarket_1.aprToApy)(opportunity.apr),
|
|
29
|
+
token,
|
|
30
|
+
incentiveKind: types_1.IncentiveKind.Reward,
|
|
31
|
+
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
+
const result = { hub: {}, spoke: {} };
|
|
36
|
+
try {
|
|
37
|
+
const res = yield fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
|
|
38
|
+
signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok)
|
|
41
|
+
throw new Error('Failed to fetch Aave V4 Merkle campaigns');
|
|
42
|
+
const opportunities = yield res.json();
|
|
43
|
+
opportunities
|
|
44
|
+
.filter((o) => o.chainId === chainId)
|
|
45
|
+
.filter((o) => o.status === types_1.OpportunityStatus.LIVE)
|
|
46
|
+
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
47
|
+
.forEach((o) => {
|
|
48
|
+
var _a, _b, _c, _d;
|
|
49
|
+
const underlying = (_c = (_b = (_a = o.tokens) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
50
|
+
if (!underlying)
|
|
51
|
+
return;
|
|
52
|
+
const side = o.action === types_1.OpportunityAction.BORROW ? 'borrow' : 'supply';
|
|
53
|
+
const incentive = buildIncentive(o);
|
|
54
|
+
if (o.type.includes('HUB')) {
|
|
55
|
+
if (!result.hub[underlying])
|
|
56
|
+
result.hub[underlying] = {};
|
|
57
|
+
result.hub[underlying][side] = incentive;
|
|
58
|
+
}
|
|
59
|
+
else if (o.type.includes('SPOKE')) {
|
|
60
|
+
const spokeAddress = (_d = o.explorerAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
61
|
+
if (!spokeAddress)
|
|
62
|
+
return;
|
|
63
|
+
const key = spokeKey(spokeAddress, underlying);
|
|
64
|
+
if (!result.spoke[key])
|
|
65
|
+
result.spoke[key] = {};
|
|
66
|
+
result.spoke[key][side] = incentive;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
console.error('Failed to fetch Aave V4 Merkle campaigns', e);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
exports.getAaveV4MerkleCampaigns = getAaveV4MerkleCampaigns;
|
|
77
|
+
/**
|
|
78
|
+
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
|
79
|
+
* intrinsic (staking) incentives, so each surface can render base yield + the rewards that apply to it.
|
|
80
|
+
*/
|
|
81
|
+
const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
|
|
82
|
+
var _a;
|
|
83
|
+
const underlying = (_a = asset.underlying) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
84
|
+
const baseSupply = asset.supplyIncentives || [];
|
|
85
|
+
const baseBorrow = asset.borrowIncentives || [];
|
|
86
|
+
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[spokeKey(spokeAddress, underlying)] : undefined;
|
|
87
|
+
const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
|
|
88
|
+
return Object.assign(Object.assign({}, asset), { spokeSupplyIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.supply) ? [...baseSupply, spokeScoped.supply] : baseSupply, spokeBorrowIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.borrow) ? [...baseBorrow, spokeScoped.borrow] : baseBorrow, hubSupplyIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.supply) ? [...baseSupply, hubScoped.supply] : baseSupply, hubBorrowIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.borrow) ? [...baseBorrow, hubScoped.borrow] : baseBorrow });
|
|
89
|
+
};
|
|
90
|
+
exports.attachAaveV4MerklIncentives = attachAaveV4MerklIncentives;
|
package/cjs/types/aaveV4.d.ts
CHANGED
|
@@ -100,6 +100,16 @@ export interface AaveV4ReserveAssetData {
|
|
|
100
100
|
borrowRate: string;
|
|
101
101
|
supplyIncentives: IncentiveData[];
|
|
102
102
|
borrowIncentives: IncentiveData[];
|
|
103
|
+
/**
|
|
104
|
+
* Intrinsic incentives pre-combined with scope-specific Merkl rewards, ready to render per surface:
|
|
105
|
+
* - spoke* → spoke supply/borrow (Create page + dashboard market table)
|
|
106
|
+
* - hub* → hub supply/borrow (Lend page)
|
|
107
|
+
* `supplyIncentives`/`borrowIncentives` above remain intrinsic-only so the net-APY calc is unaffected.
|
|
108
|
+
*/
|
|
109
|
+
spokeSupplyIncentives?: IncentiveData[];
|
|
110
|
+
spokeBorrowIncentives?: IncentiveData[];
|
|
111
|
+
hubSupplyIncentives?: IncentiveData[];
|
|
112
|
+
hubBorrowIncentives?: IncentiveData[];
|
|
103
113
|
canBeBorrowed: boolean;
|
|
104
114
|
canBeSupplied: boolean;
|
|
105
115
|
canBeWithdrawn: boolean;
|
package/cjs/types/merkl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData } from './common';
|
|
2
2
|
export declare enum OpportunityAction {
|
|
3
3
|
LEND = "LEND",
|
|
4
4
|
BORROW = "BORROW"
|
|
@@ -73,3 +73,16 @@ export type MerkleRewardMap = Record<EthAddress, {
|
|
|
73
73
|
supply?: MerkleRewardInfo;
|
|
74
74
|
borrow?: MerkleRewardInfo;
|
|
75
75
|
}>;
|
|
76
|
+
export type AaveV4MerklScopedReward = {
|
|
77
|
+
supply?: IncentiveData;
|
|
78
|
+
borrow?: IncentiveData;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Aave V4 Merkl reward campaigns split by scope:
|
|
82
|
+
* - `hub`: keyed by underlying token address (lowercase) — rewards for supplying to a hub
|
|
83
|
+
* - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
|
|
84
|
+
*/
|
|
85
|
+
export type AaveV4MerklRewardMap = {
|
|
86
|
+
hub: Record<string, AaveV4MerklScopedReward>;
|
|
87
|
+
spoke: Record<string, AaveV4MerklScopedReward>;
|
|
88
|
+
};
|
package/esm/aaveV4/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo, EthAddress, EthereumProvider, NetworkNumber } from '../types';
|
|
3
3
|
export * as lend from './lend';
|
|
4
|
+
export { getAaveV4MerkleCampaigns } from './merkl';
|
|
4
5
|
export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
5
6
|
export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
6
7
|
export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
|
package/esm/aaveV4/index.js
CHANGED
|
@@ -17,7 +17,9 @@ import { isMaxUint, wethToEth } from '../services/utils';
|
|
|
17
17
|
import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
|
|
18
18
|
import { getAaveV4HubByAddress } from '../markets/aaveV4';
|
|
19
19
|
import { aprToApy } from '../moneymarket';
|
|
20
|
+
import { attachAaveV4MerklIncentives, getAaveV4MerkleCampaigns } from './merkl';
|
|
20
21
|
export * as lend from './lend';
|
|
22
|
+
export { getAaveV4MerkleCampaigns } from './merkl';
|
|
21
23
|
const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
24
|
const hubData = yield viewContract.read.getHubAllAssetsData([hubAddress]);
|
|
23
25
|
return {
|
|
@@ -129,15 +131,17 @@ export function _getAaveV4SpokeData(provider_1, network_1, market_1) {
|
|
|
129
131
|
return __awaiter(this, arguments, void 0, function* (provider, network, market, blockNumber = 'latest') {
|
|
130
132
|
const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
|
|
131
133
|
const hubsData = {};
|
|
132
|
-
const [spokeData] = yield Promise.all([
|
|
134
|
+
const [spokeData, merklCampaigns] = yield Promise.all([
|
|
133
135
|
viewContract.read.getSpokeDataFull([market.address]),
|
|
136
|
+
getAaveV4MerkleCampaigns(network),
|
|
134
137
|
...market.hubs.map((hubAddress) => __awaiter(this, void 0, void 0, function* () {
|
|
135
138
|
hubsData[hubAddress] = yield fetchHubData(viewContract, hubAddress);
|
|
136
139
|
})),
|
|
137
140
|
]);
|
|
138
141
|
const reserveAssetsArray = yield Promise.all(spokeData[1].map((reserveAssetOnChain, index) => __awaiter(this, void 0, void 0, function* () { return formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], index, +spokeData[0].oracleDecimals.toString(), network); })));
|
|
142
|
+
const enrichedAssets = reserveAssetsArray.map((asset) => attachAaveV4MerklIncentives(asset, market.address, merklCampaigns));
|
|
139
143
|
return {
|
|
140
|
-
assetsData:
|
|
144
|
+
assetsData: enrichedAssets.reduce((acc, reserveAsset) => {
|
|
141
145
|
acc[`${reserveAsset.symbol}-${reserveAsset.reserveId}`] = reserveAsset;
|
|
142
146
|
return acc;
|
|
143
147
|
}, {}),
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AaveV4MerklRewardMap, AaveV4ReserveAssetData } from '../types';
|
|
2
|
+
import { NetworkNumber } from '../types/common';
|
|
3
|
+
export declare const getAaveV4MerkleCampaigns: (chainId: NetworkNumber) => Promise<AaveV4MerklRewardMap>;
|
|
4
|
+
/**
|
|
5
|
+
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
|
6
|
+
* intrinsic (staking) incentives, so each surface can render base yield + the rewards that apply to it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const attachAaveV4MerklIncentives: (asset: AaveV4ReserveAssetData, spokeAddress: string, campaigns: AaveV4MerklRewardMap) => AaveV4ReserveAssetData;
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { aprToApy } from '../moneymarket';
|
|
11
|
+
import { DEFAULT_TIMEOUT } from '../services/utils';
|
|
12
|
+
import { IncentiveKind, OpportunityAction, OpportunityStatus, } from '../types';
|
|
13
|
+
/**
|
|
14
|
+
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
15
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying token)
|
|
16
|
+
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
|
|
17
|
+
* Hub campaigns identify the underlying via `tokens[0]`; spoke campaigns identify the spoke via `explorerAddress`.
|
|
18
|
+
*/
|
|
19
|
+
const spokeKey = (spokeAddress, underlying) => `${spokeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
|
|
20
|
+
const buildIncentive = (opportunity) => {
|
|
21
|
+
var _a, _b, _c, _d, _e;
|
|
22
|
+
const rewardToken = (_c = (_b = (_a = opportunity.rewardsRecord) === null || _a === void 0 ? void 0 : _a.breakdowns) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.token;
|
|
23
|
+
const token = (rewardToken === null || rewardToken === void 0 ? void 0 : rewardToken.symbol) || ((_e = (_d = opportunity.tokens) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.symbol) || '';
|
|
24
|
+
return {
|
|
25
|
+
apy: aprToApy(opportunity.apr),
|
|
26
|
+
token,
|
|
27
|
+
incentiveKind: IncentiveKind.Reward,
|
|
28
|
+
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
const result = { hub: {}, spoke: {} };
|
|
33
|
+
try {
|
|
34
|
+
const res = yield fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
|
|
35
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
36
|
+
});
|
|
37
|
+
if (!res.ok)
|
|
38
|
+
throw new Error('Failed to fetch Aave V4 Merkle campaigns');
|
|
39
|
+
const opportunities = yield res.json();
|
|
40
|
+
opportunities
|
|
41
|
+
.filter((o) => o.chainId === chainId)
|
|
42
|
+
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
43
|
+
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
44
|
+
.forEach((o) => {
|
|
45
|
+
var _a, _b, _c, _d;
|
|
46
|
+
const underlying = (_c = (_b = (_a = o.tokens) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
47
|
+
if (!underlying)
|
|
48
|
+
return;
|
|
49
|
+
const side = o.action === OpportunityAction.BORROW ? 'borrow' : 'supply';
|
|
50
|
+
const incentive = buildIncentive(o);
|
|
51
|
+
if (o.type.includes('HUB')) {
|
|
52
|
+
if (!result.hub[underlying])
|
|
53
|
+
result.hub[underlying] = {};
|
|
54
|
+
result.hub[underlying][side] = incentive;
|
|
55
|
+
}
|
|
56
|
+
else if (o.type.includes('SPOKE')) {
|
|
57
|
+
const spokeAddress = (_d = o.explorerAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase();
|
|
58
|
+
if (!spokeAddress)
|
|
59
|
+
return;
|
|
60
|
+
const key = spokeKey(spokeAddress, underlying);
|
|
61
|
+
if (!result.spoke[key])
|
|
62
|
+
result.spoke[key] = {};
|
|
63
|
+
result.spoke[key][side] = incentive;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
console.error('Failed to fetch Aave V4 Merkle campaigns', e);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
|
75
|
+
* intrinsic (staking) incentives, so each surface can render base yield + the rewards that apply to it.
|
|
76
|
+
*/
|
|
77
|
+
export const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
|
|
78
|
+
var _a;
|
|
79
|
+
const underlying = (_a = asset.underlying) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
80
|
+
const baseSupply = asset.supplyIncentives || [];
|
|
81
|
+
const baseBorrow = asset.borrowIncentives || [];
|
|
82
|
+
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[spokeKey(spokeAddress, underlying)] : undefined;
|
|
83
|
+
const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
|
|
84
|
+
return Object.assign(Object.assign({}, asset), { spokeSupplyIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.supply) ? [...baseSupply, spokeScoped.supply] : baseSupply, spokeBorrowIncentives: (spokeScoped === null || spokeScoped === void 0 ? void 0 : spokeScoped.borrow) ? [...baseBorrow, spokeScoped.borrow] : baseBorrow, hubSupplyIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.supply) ? [...baseSupply, hubScoped.supply] : baseSupply, hubBorrowIncentives: (hubScoped === null || hubScoped === void 0 ? void 0 : hubScoped.borrow) ? [...baseBorrow, hubScoped.borrow] : baseBorrow });
|
|
85
|
+
};
|
package/esm/types/aaveV4.d.ts
CHANGED
|
@@ -100,6 +100,16 @@ export interface AaveV4ReserveAssetData {
|
|
|
100
100
|
borrowRate: string;
|
|
101
101
|
supplyIncentives: IncentiveData[];
|
|
102
102
|
borrowIncentives: IncentiveData[];
|
|
103
|
+
/**
|
|
104
|
+
* Intrinsic incentives pre-combined with scope-specific Merkl rewards, ready to render per surface:
|
|
105
|
+
* - spoke* → spoke supply/borrow (Create page + dashboard market table)
|
|
106
|
+
* - hub* → hub supply/borrow (Lend page)
|
|
107
|
+
* `supplyIncentives`/`borrowIncentives` above remain intrinsic-only so the net-APY calc is unaffected.
|
|
108
|
+
*/
|
|
109
|
+
spokeSupplyIncentives?: IncentiveData[];
|
|
110
|
+
spokeBorrowIncentives?: IncentiveData[];
|
|
111
|
+
hubSupplyIncentives?: IncentiveData[];
|
|
112
|
+
hubBorrowIncentives?: IncentiveData[];
|
|
103
113
|
canBeBorrowed: boolean;
|
|
104
114
|
canBeSupplied: boolean;
|
|
105
115
|
canBeWithdrawn: boolean;
|
package/esm/types/merkl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData } from './common';
|
|
2
2
|
export declare enum OpportunityAction {
|
|
3
3
|
LEND = "LEND",
|
|
4
4
|
BORROW = "BORROW"
|
|
@@ -73,3 +73,16 @@ export type MerkleRewardMap = Record<EthAddress, {
|
|
|
73
73
|
supply?: MerkleRewardInfo;
|
|
74
74
|
borrow?: MerkleRewardInfo;
|
|
75
75
|
}>;
|
|
76
|
+
export type AaveV4MerklScopedReward = {
|
|
77
|
+
supply?: IncentiveData;
|
|
78
|
+
borrow?: IncentiveData;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Aave V4 Merkl reward campaigns split by scope:
|
|
82
|
+
* - `hub`: keyed by underlying token address (lowercase) — rewards for supplying to a hub
|
|
83
|
+
* - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
|
|
84
|
+
*/
|
|
85
|
+
export type AaveV4MerklRewardMap = {
|
|
86
|
+
hub: Record<string, AaveV4MerklScopedReward>;
|
|
87
|
+
spoke: Record<string, AaveV4MerklScopedReward>;
|
|
88
|
+
};
|
package/package.json
CHANGED
package/src/aaveV4/index.ts
CHANGED
|
@@ -23,8 +23,10 @@ import { isMaxUint, wethToEth } from '../services/utils';
|
|
|
23
23
|
import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
|
|
24
24
|
import { getAaveV4HubByAddress } from '../markets/aaveV4';
|
|
25
25
|
import { aprToApy } from '../moneymarket';
|
|
26
|
+
import { attachAaveV4MerklIncentives, getAaveV4MerkleCampaigns } from './merkl';
|
|
26
27
|
|
|
27
28
|
export * as lend from './lend';
|
|
29
|
+
export { getAaveV4MerkleCampaigns } from './merkl';
|
|
28
30
|
|
|
29
31
|
const fetchHubData = async (viewContract: ReturnType<typeof AaveV4ViewContractViem>, hubAddress: EthAddress): Promise<AaveV4HubOnChainData> => {
|
|
30
32
|
const hubData = await viewContract.read.getHubAllAssetsData([hubAddress]);
|
|
@@ -145,8 +147,9 @@ export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumb
|
|
|
145
147
|
const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
|
|
146
148
|
|
|
147
149
|
const hubsData: Record<EthAddress, AaveV4HubOnChainData> = {};
|
|
148
|
-
const [spokeData] = await Promise.all([
|
|
150
|
+
const [spokeData, merklCampaigns] = await Promise.all([
|
|
149
151
|
viewContract.read.getSpokeDataFull([market.address]),
|
|
152
|
+
getAaveV4MerkleCampaigns(network),
|
|
150
153
|
...market.hubs.map(async (hubAddress) => {
|
|
151
154
|
hubsData[hubAddress] = await fetchHubData(viewContract, hubAddress);
|
|
152
155
|
}),
|
|
@@ -154,8 +157,10 @@ export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumb
|
|
|
154
157
|
|
|
155
158
|
const reserveAssetsArray = await Promise.all(spokeData[1].map(async (reserveAssetOnChain: AaveV4ReserveAssetOnChain, index: number) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], index, +spokeData[0].oracleDecimals.toString(), network)));
|
|
156
159
|
|
|
160
|
+
const enrichedAssets = reserveAssetsArray.map((asset) => attachAaveV4MerklIncentives(asset, market.address, merklCampaigns));
|
|
161
|
+
|
|
157
162
|
return {
|
|
158
|
-
assetsData:
|
|
163
|
+
assetsData: enrichedAssets.reduce((acc: Record<string, AaveV4ReserveAssetData>, reserveAsset: AaveV4ReserveAssetData) => {
|
|
159
164
|
acc[`${reserveAsset.symbol}-${reserveAsset.reserveId}`] = reserveAsset;
|
|
160
165
|
return acc;
|
|
161
166
|
}, {}),
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { aprToApy } from '../moneymarket';
|
|
2
|
+
import { DEFAULT_TIMEOUT } from '../services/utils';
|
|
3
|
+
import {
|
|
4
|
+
AaveV4MerklRewardMap,
|
|
5
|
+
AaveV4ReserveAssetData,
|
|
6
|
+
IncentiveData,
|
|
7
|
+
IncentiveKind,
|
|
8
|
+
MerklOpportunity,
|
|
9
|
+
OpportunityAction,
|
|
10
|
+
OpportunityStatus,
|
|
11
|
+
} from '../types';
|
|
12
|
+
import { NetworkNumber } from '../types/common';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Merkl tags Aave V4 reward campaigns by scope via the `type` field:
|
|
16
|
+
* - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub (matched per underlying token)
|
|
17
|
+
* - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke (matched per spoke contract + underlying)
|
|
18
|
+
* Hub campaigns identify the underlying via `tokens[0]`; spoke campaigns identify the spoke via `explorerAddress`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const spokeKey = (spokeAddress: string, underlying: string) => `${spokeAddress.toLowerCase()}_${underlying.toLowerCase()}`;
|
|
22
|
+
|
|
23
|
+
const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
|
|
24
|
+
const rewardToken = opportunity.rewardsRecord?.breakdowns?.[0]?.token;
|
|
25
|
+
const token = rewardToken?.symbol || opportunity.tokens?.[0]?.symbol || '';
|
|
26
|
+
return {
|
|
27
|
+
apy: aprToApy(opportunity.apr),
|
|
28
|
+
token,
|
|
29
|
+
incentiveKind: IncentiveKind.Reward,
|
|
30
|
+
description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<AaveV4MerklRewardMap> => {
|
|
35
|
+
const result: AaveV4MerklRewardMap = { hub: {}, spoke: {} };
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch('https://api-merkl.angle.money/v4/opportunities?mainProtocolId=aave', {
|
|
38
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok) throw new Error('Failed to fetch Aave V4 Merkle campaigns');
|
|
41
|
+
const opportunities = await res.json() as MerklOpportunity[];
|
|
42
|
+
|
|
43
|
+
opportunities
|
|
44
|
+
.filter((o) => o.chainId === chainId)
|
|
45
|
+
.filter((o) => o.status === OpportunityStatus.LIVE)
|
|
46
|
+
.filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
|
|
47
|
+
.forEach((o) => {
|
|
48
|
+
const underlying = o.tokens?.[0]?.address?.toLowerCase();
|
|
49
|
+
if (!underlying) return;
|
|
50
|
+
|
|
51
|
+
const side: 'supply' | 'borrow' = o.action === OpportunityAction.BORROW ? 'borrow' : 'supply';
|
|
52
|
+
const incentive = buildIncentive(o);
|
|
53
|
+
|
|
54
|
+
if (o.type.includes('HUB')) {
|
|
55
|
+
if (!result.hub[underlying]) result.hub[underlying] = {};
|
|
56
|
+
result.hub[underlying][side] = incentive;
|
|
57
|
+
} else if (o.type.includes('SPOKE')) {
|
|
58
|
+
const spokeAddress = o.explorerAddress?.toLowerCase();
|
|
59
|
+
if (!spokeAddress) return;
|
|
60
|
+
const key = spokeKey(spokeAddress, underlying);
|
|
61
|
+
if (!result.spoke[key]) result.spoke[key] = {};
|
|
62
|
+
result.spoke[key][side] = incentive;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.error('Failed to fetch Aave V4 Merkle campaigns', e);
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Returns a copy of the asset with scope-specific incentive arrays pre-combined with the asset's
|
|
75
|
+
* intrinsic (staking) incentives, so each surface can render base yield + the rewards that apply to it.
|
|
76
|
+
*/
|
|
77
|
+
export const attachAaveV4MerklIncentives = (asset: AaveV4ReserveAssetData, spokeAddress: string, campaigns: AaveV4MerklRewardMap): AaveV4ReserveAssetData => {
|
|
78
|
+
const underlying = asset.underlying?.toLowerCase();
|
|
79
|
+
const baseSupply = asset.supplyIncentives || [];
|
|
80
|
+
const baseBorrow = asset.borrowIncentives || [];
|
|
81
|
+
|
|
82
|
+
const spokeScoped = (spokeAddress && underlying) ? campaigns.spoke[spokeKey(spokeAddress, underlying)] : undefined;
|
|
83
|
+
const hubScoped = underlying ? campaigns.hub[underlying] : undefined;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
...asset,
|
|
87
|
+
spokeSupplyIncentives: spokeScoped?.supply ? [...baseSupply, spokeScoped.supply] : baseSupply,
|
|
88
|
+
spokeBorrowIncentives: spokeScoped?.borrow ? [...baseBorrow, spokeScoped.borrow] : baseBorrow,
|
|
89
|
+
hubSupplyIncentives: hubScoped?.supply ? [...baseSupply, hubScoped.supply] : baseSupply,
|
|
90
|
+
hubBorrowIncentives: hubScoped?.borrow ? [...baseBorrow, hubScoped.borrow] : baseBorrow,
|
|
91
|
+
};
|
|
92
|
+
};
|
package/src/types/aaveV4.ts
CHANGED
|
@@ -111,6 +111,16 @@ export interface AaveV4ReserveAssetData {
|
|
|
111
111
|
borrowRate: string,
|
|
112
112
|
supplyIncentives: IncentiveData[];
|
|
113
113
|
borrowIncentives: IncentiveData[];
|
|
114
|
+
/**
|
|
115
|
+
* Intrinsic incentives pre-combined with scope-specific Merkl rewards, ready to render per surface:
|
|
116
|
+
* - spoke* → spoke supply/borrow (Create page + dashboard market table)
|
|
117
|
+
* - hub* → hub supply/borrow (Lend page)
|
|
118
|
+
* `supplyIncentives`/`borrowIncentives` above remain intrinsic-only so the net-APY calc is unaffected.
|
|
119
|
+
*/
|
|
120
|
+
spokeSupplyIncentives?: IncentiveData[];
|
|
121
|
+
spokeBorrowIncentives?: IncentiveData[];
|
|
122
|
+
hubSupplyIncentives?: IncentiveData[];
|
|
123
|
+
hubBorrowIncentives?: IncentiveData[];
|
|
114
124
|
canBeBorrowed: boolean;
|
|
115
125
|
canBeSupplied: boolean;
|
|
116
126
|
canBeWithdrawn: boolean;
|
package/src/types/merkl.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData } from './common';
|
|
2
2
|
|
|
3
3
|
export enum OpportunityAction {
|
|
4
4
|
LEND = 'LEND',
|
|
@@ -68,4 +68,16 @@ export type MerklOpportunity = {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
export type MerkleRewardInfo = { apy: string; rewardTokenSymbol: string, description: string, identifier: string };
|
|
71
|
-
export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; borrow?: MerkleRewardInfo }>;
|
|
71
|
+
export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; borrow?: MerkleRewardInfo }>;
|
|
72
|
+
|
|
73
|
+
export type AaveV4MerklScopedReward = { supply?: IncentiveData; borrow?: IncentiveData };
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Aave V4 Merkl reward campaigns split by scope:
|
|
77
|
+
* - `hub`: keyed by underlying token address (lowercase) — rewards for supplying to a hub
|
|
78
|
+
* - `spoke`: keyed by `${spokeAddress}_${underlyingAddress}` (both lowercase) — rewards for supplying/borrowing on a spoke
|
|
79
|
+
*/
|
|
80
|
+
export type AaveV4MerklRewardMap = {
|
|
81
|
+
hub: Record<string, AaveV4MerklScopedReward>;
|
|
82
|
+
spoke: Record<string, AaveV4MerklScopedReward>;
|
|
83
|
+
};
|