@defisaver/positions-sdk 2.1.156 → 2.1.158-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.
@@ -8,8 +8,12 @@ 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");
@@ -17,6 +21,10 @@ const types_1 = require("../types");
17
21
  * Merkl tags Aave V4 reward campaigns by scope via the `type` field:
18
22
  * - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub asset
19
23
  * - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke reserve
24
+ * - AAVE_V4_HUB_NET_LENDING / AAVE_V4_SPOKE_NET_BORROWING → reward on the NET position
25
+ * (supply minus same-token borrows, or vice versa); params list scopes as `hubs[]`/`spokes[]`
26
+ * arrays instead of a flat address+id pair, and only the campaign's own side maps to a reward
27
+ * (the opposite-side ids describe what reduces the accrual)
20
28
  * Embedded campaign params provide the exact on-chain identifiers. Token addresses cannot safely
21
29
  * identify Aave V4 rewards because one spoke can expose the same underlying from multiple hubs.
22
30
  * Each stored reward is also tagged with campaign identity (`campaignIds`/`parentCampaignIds`) so
@@ -32,19 +40,40 @@ const buildIncentive = (opportunity) => {
32
40
  apy: (0, moneymarket_1.aprToApy)(opportunity.apr),
33
41
  token,
34
42
  incentiveKind: types_1.IncentiveKind.Reward,
35
- description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
43
+ source: types_1.IncentiveSource.Merkl,
44
+ name: opportunity.name,
45
+ description: opportunity.description || `Eligible for ${token} rewards through Merkl.`,
36
46
  };
37
47
  };
38
48
  const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
39
49
  const result = { hub: {}, spoke: {} };
50
+ const endByCampaignId = {};
51
+ opportunities.forEach((o) => {
52
+ var _a;
53
+ return (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.forEach((c) => {
54
+ if (c.id && c.endTimestamp)
55
+ endByCampaignId[c.id] = +c.endTimestamp;
56
+ });
57
+ });
58
+ const effectiveEndTimestamp = (campaign) => Math.max(campaign.endTimestamp ? +campaign.endTimestamp : 0, ...(campaign.childCampaignIds || []).map((id) => endByCampaignId[id] || 0));
40
59
  opportunities
41
60
  .filter((o) => o.chainId === chainId)
42
61
  .filter((o) => o.status === types_1.OpportunityStatus.LIVE)
43
62
  .filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
44
63
  .forEach((o) => {
45
- var _a, _b;
64
+ var _a, _b, _c;
46
65
  const side = o.action === types_1.OpportunityAction.BORROW ? types_1.IncentiveSide.Borrow : types_1.IncentiveSide.Supply;
47
- const incentive = buildIncentive(o);
66
+ const now = Date.now() / 1000;
67
+ const methods = (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.filter((c) => (c.startTimestamp === undefined || c.startTimestamp <= now)
68
+ && (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; });
69
+ // An opportunity-level APR cannot be split between simultaneous net and additive campaigns.
70
+ if (side === types_1.IncentiveSide.Supply && (methods === null || methods === void 0 ? void 0 : methods.includes('AAVE_V4_NET_APR'))
71
+ && methods.some((method) => !!method && method !== 'AAVE_V4_NET_APR'))
72
+ return;
73
+ const incentive = Object.assign(Object.assign({}, buildIncentive(o)), {
74
+ // Missing methods keep the existing target-yield behavior.
75
+ isAdditiveReward: side === types_1.IncentiveSide.Supply && !!(methods === null || methods === void 0 ? void 0 : methods.length)
76
+ && methods.every((method) => !!method && method !== 'AAVE_V4_NET_APR') });
48
77
  // one opportunity can span several campaigns (e.g. renewed periods), so campaign identity is
49
78
  // collected per scope key before the reward entries are written
50
79
  const idsByKey = {};
@@ -55,31 +84,58 @@ const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
55
84
  idsByKey[key].campaignIds.add(campaign.id);
56
85
  if (campaign.parentCampaignId)
57
86
  idsByKey[key].parentCampaignIds.add(campaign.parentCampaignId);
87
+ const endTimestamp = effectiveEndTimestamp(campaign);
88
+ if (endTimestamp)
89
+ idsByKey[key].endTimestamp = Math.max(idsByKey[key].endTimestamp || 0, endTimestamp);
58
90
  };
59
91
  if (o.type.includes('HUB')) {
60
- (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.forEach((c) => {
61
- var _a;
62
- if (!((_a = c.params) === null || _a === void 0 ? void 0 : _a.hubAddress) || c.params.assetId === undefined || c.params.assetId === null)
92
+ (_b = o.campaigns) === null || _b === void 0 ? void 0 : _b.forEach((c) => {
93
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
94
+ if ((_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.hubs) === null || _b === void 0 ? void 0 : _b.length) {
95
+ c.params.hubs.forEach((hub) => {
96
+ if (!hub.hubAddress)
97
+ return;
98
+ const rewardedIds = (side === types_1.IncentiveSide.Borrow ? hub.borrowAssetIds : hub.lendingAssetIds) || [];
99
+ rewardedIds.forEach((assetId) => collect(scopeKey(hub.hubAddress, assetId), c));
100
+ });
63
101
  return;
64
- collect(scopeKey(c.params.hubAddress, c.params.assetId), c);
102
+ }
103
+ const hubAddress = (_d = (_c = c.params) === null || _c === void 0 ? void 0 : _c.hubAddress) !== null && _d !== void 0 ? _d : (_g = (_f = (_e = c.params) === null || _e === void 0 ? void 0 : _e.distributionMethodParameters) === null || _f === void 0 ? void 0 : _f.distributionSettings) === null || _g === void 0 ? void 0 : _g.hubAddress;
104
+ const assetId = (_j = (_h = c.params) === null || _h === void 0 ? void 0 : _h.assetId) !== null && _j !== void 0 ? _j : (_m = (_l = (_k = c.params) === null || _k === void 0 ? void 0 : _k.distributionMethodParameters) === null || _l === void 0 ? void 0 : _l.distributionSettings) === null || _m === void 0 ? void 0 : _m.assetId;
105
+ if (!hubAddress || assetId === undefined || assetId === null)
106
+ return;
107
+ collect(scopeKey(hubAddress, assetId), c);
65
108
  });
66
109
  Object.entries(idsByKey).forEach(([key, ids]) => {
67
110
  if (!result.hub[key])
68
111
  result.hub[key] = {};
69
- result.hub[key][side] = [...(result.hub[key][side] || []), Object.assign(Object.assign({}, incentive), { campaignIds: [...ids.campaignIds] })];
112
+ result.hub[key][side] = [...(result.hub[key][side] || []), Object.assign(Object.assign({}, incentive), { endTimestamp: ids.endTimestamp, campaignIds: [...ids.campaignIds] })];
70
113
  });
71
114
  }
72
115
  else if (o.type.includes('SPOKE')) {
73
- (_b = o.campaigns) === null || _b === void 0 ? void 0 : _b.forEach((c) => {
74
- var _a;
75
- if (!((_a = c.params) === null || _a === void 0 ? void 0 : _a.spokeAddress) || c.params.reserveId === undefined || c.params.reserveId === null)
116
+ (_c = o.campaigns) === null || _c === void 0 ? void 0 : _c.forEach((c) => {
117
+ var _a, _b, _c;
118
+ if ((_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.spokes) === null || _b === void 0 ? void 0 : _b.length) {
119
+ c.params.spokes.forEach((spoke) => {
120
+ if (!spoke.spokeAddress)
121
+ return;
122
+ const rewardedTokens = (side === types_1.IncentiveSide.Borrow ? spoke.borrowTokens : spoke.supplyTokens) || [];
123
+ rewardedTokens.forEach((tokenScope) => {
124
+ if (tokenScope.reserveId === undefined || tokenScope.reserveId === null)
125
+ return;
126
+ collect(scopeKey(spoke.spokeAddress, tokenScope.reserveId), c);
127
+ });
128
+ });
129
+ return;
130
+ }
131
+ if (!((_c = c.params) === null || _c === void 0 ? void 0 : _c.spokeAddress) || c.params.reserveId === undefined || c.params.reserveId === null)
76
132
  return;
77
133
  collect(scopeKey(c.params.spokeAddress, c.params.reserveId), c);
78
134
  });
79
135
  Object.entries(idsByKey).forEach(([key, ids]) => {
80
136
  if (!result.spoke[key])
81
137
  result.spoke[key] = {};
82
- result.spoke[key][side] = [...(result.spoke[key][side] || []), Object.assign(Object.assign({}, incentive), { campaignIds: [...ids.campaignIds], parentCampaignIds: [...ids.parentCampaignIds] })];
138
+ result.spoke[key][side] = [...(result.spoke[key][side] || []), Object.assign(Object.assign({}, incentive), { endTimestamp: ids.endTimestamp, campaignIds: [...ids.campaignIds], parentCampaignIds: [...ids.parentCampaignIds] })];
83
139
  });
84
140
  }
85
141
  });
@@ -90,7 +146,7 @@ const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, void 0,
90
146
  try {
91
147
  const opportunities = yield (0, merkl_1.fetchAllMerklOpportunities)({
92
148
  mainProtocolId: 'aave',
93
- type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
149
+ type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW,AAVE_V4_HUB_NET_LENDING,AAVE_V4_HUB_NET_BORROWING,AAVE_V4_SPOKE_NET_LENDING,AAVE_V4_SPOKE_NET_BORROWING',
94
150
  status: types_1.OpportunityStatus.LIVE,
95
151
  campaigns: 'true',
96
152
  });
@@ -112,6 +168,8 @@ const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
112
168
  const baseBorrow = asset.borrowIncentives || [];
113
169
  const spokeScoped = spokeAddress ? campaigns.spoke[scopeKey(spokeAddress, asset.reserveId)] : undefined;
114
170
  const hubScoped = asset.hub ? campaigns.hub[scopeKey(asset.hub, asset.assetId)] : undefined;
115
- 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, ...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, ...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 });
171
+ // Net-APR campaigns top up native yield; additive campaigns pay their APR on top.
172
+ 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() })));
173
+ 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
174
  };
117
175
  exports.attachAaveV4MerklIncentives = attachAaveV4MerklIncentives;
@@ -29,7 +29,9 @@ export interface IncentiveData {
29
29
  apy: string;
30
30
  incentiveKind?: IncentiveKind;
31
31
  source?: IncentiveSource;
32
+ name?: string;
32
33
  description?: string;
34
+ endTimestamp?: number;
33
35
  eligibilityId?: IncentiveEligibilityId;
34
36
  }
35
37
  export type EthAddress = HexString;
@@ -18,12 +18,56 @@ export type MerklCampaign = {
18
18
  */
19
19
  parentCampaignId?: string;
20
20
  childCampaignIds?: string[];
21
+ startTimestamp?: number;
22
+ endTimestamp?: number;
21
23
  params?: {
22
24
  reserveId?: string | number;
23
25
  spokeAddress?: EthAddress;
24
26
  hubAddress?: EthAddress;
25
27
  hubAssetId?: string | number;
26
28
  assetId?: string | number;
29
+ /**
30
+ * AAVE_V4_HUB_NET_* campaigns scope per hub: the reward applies to the ids on the campaign's
31
+ * own side (`lendingAssetIds` for LEND, `borrowAssetIds` for BORROW) — the opposite-side ids
32
+ * only describe positions that reduce the net accrual, so they never map to a reward.
33
+ */
34
+ hubs?: {
35
+ hubAddress: EthAddress;
36
+ assets?: {
37
+ symbol: string;
38
+ assetId: string | number;
39
+ decimals?: number;
40
+ underlyingToken?: EthAddress;
41
+ }[];
42
+ lendingAssetIds?: (string | number)[];
43
+ borrowAssetIds?: (string | number)[];
44
+ }[];
45
+ /** AAVE_V4_SPOKE_NET_* campaigns scope per spoke, with the same own-side rule as `hubs`. */
46
+ spokes?: {
47
+ spokeAddress: EthAddress;
48
+ spokeName?: string;
49
+ supplyTokens?: {
50
+ symbol: string;
51
+ reserveId: string | number;
52
+ hubAddress?: EthAddress;
53
+ hubAssetId?: string | number;
54
+ underlyingToken?: EthAddress;
55
+ }[];
56
+ borrowTokens?: {
57
+ symbol: string;
58
+ reserveId: string | number;
59
+ hubAddress?: EthAddress;
60
+ hubAssetId?: string | number;
61
+ underlyingToken?: EthAddress;
62
+ }[];
63
+ }[];
64
+ distributionMethodParameters?: {
65
+ distributionMethod?: string;
66
+ distributionSettings?: {
67
+ hubAddress?: EthAddress;
68
+ assetId?: string | number;
69
+ };
70
+ };
27
71
  };
28
72
  };
29
73
  export type MerklOpportunity = {
@@ -102,6 +146,7 @@ export type MerkleRewardMap = Record<EthAddress, {
102
146
  export type AaveV4MerklIncentive = IncentiveData & {
103
147
  campaignIds?: string[];
104
148
  parentCampaignIds?: string[];
149
+ isAdditiveReward?: boolean;
105
150
  };
106
151
  export type AaveV4MerklScopedReward = {
107
152
  [side in IncentiveSide]?: AaveV4MerklIncentive[];
@@ -7,13 +7,18 @@ 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
- import { IncentiveKind, IncentiveSide, OpportunityAction, OpportunityStatus, } from '../types';
13
+ import { IncentiveKind, IncentiveSide, IncentiveSource, OpportunityAction, OpportunityStatus, } from '../types';
13
14
  /**
14
15
  * Merkl tags Aave V4 reward campaigns by scope via the `type` field:
15
16
  * - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub asset
16
17
  * - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke reserve
18
+ * - AAVE_V4_HUB_NET_LENDING / AAVE_V4_SPOKE_NET_BORROWING → reward on the NET position
19
+ * (supply minus same-token borrows, or vice versa); params list scopes as `hubs[]`/`spokes[]`
20
+ * arrays instead of a flat address+id pair, and only the campaign's own side maps to a reward
21
+ * (the opposite-side ids describe what reduces the accrual)
17
22
  * Embedded campaign params provide the exact on-chain identifiers. Token addresses cannot safely
18
23
  * identify Aave V4 rewards because one spoke can expose the same underlying from multiple hubs.
19
24
  * Each stored reward is also tagged with campaign identity (`campaignIds`/`parentCampaignIds`) so
@@ -29,19 +34,40 @@ const buildIncentive = (opportunity) => {
29
34
  apy: aprToApy(opportunity.apr),
30
35
  token,
31
36
  incentiveKind: IncentiveKind.Reward,
32
- description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
37
+ source: IncentiveSource.Merkl,
38
+ name: opportunity.name,
39
+ description: opportunity.description || `Eligible for ${token} rewards through Merkl.`,
33
40
  };
34
41
  };
35
42
  export const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
36
43
  const result = { hub: {}, spoke: {} };
44
+ const endByCampaignId = {};
45
+ opportunities.forEach((o) => {
46
+ var _a;
47
+ return (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.forEach((c) => {
48
+ if (c.id && c.endTimestamp)
49
+ endByCampaignId[c.id] = +c.endTimestamp;
50
+ });
51
+ });
52
+ const effectiveEndTimestamp = (campaign) => Math.max(campaign.endTimestamp ? +campaign.endTimestamp : 0, ...(campaign.childCampaignIds || []).map((id) => endByCampaignId[id] || 0));
37
53
  opportunities
38
54
  .filter((o) => o.chainId === chainId)
39
55
  .filter((o) => o.status === OpportunityStatus.LIVE)
40
56
  .filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
41
57
  .forEach((o) => {
42
- var _a, _b;
58
+ var _a, _b, _c;
43
59
  const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
44
- const incentive = buildIncentive(o);
60
+ const now = Date.now() / 1000;
61
+ const methods = (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.filter((c) => (c.startTimestamp === undefined || c.startTimestamp <= now)
62
+ && (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; });
63
+ // An opportunity-level APR cannot be split between simultaneous net and additive campaigns.
64
+ if (side === IncentiveSide.Supply && (methods === null || methods === void 0 ? void 0 : methods.includes('AAVE_V4_NET_APR'))
65
+ && methods.some((method) => !!method && method !== 'AAVE_V4_NET_APR'))
66
+ return;
67
+ const incentive = Object.assign(Object.assign({}, buildIncentive(o)), {
68
+ // Missing methods keep the existing target-yield behavior.
69
+ isAdditiveReward: side === IncentiveSide.Supply && !!(methods === null || methods === void 0 ? void 0 : methods.length)
70
+ && methods.every((method) => !!method && method !== 'AAVE_V4_NET_APR') });
45
71
  // one opportunity can span several campaigns (e.g. renewed periods), so campaign identity is
46
72
  // collected per scope key before the reward entries are written
47
73
  const idsByKey = {};
@@ -52,31 +78,58 @@ export const buildAaveV4MerklRewardMap = (opportunities, chainId) => {
52
78
  idsByKey[key].campaignIds.add(campaign.id);
53
79
  if (campaign.parentCampaignId)
54
80
  idsByKey[key].parentCampaignIds.add(campaign.parentCampaignId);
81
+ const endTimestamp = effectiveEndTimestamp(campaign);
82
+ if (endTimestamp)
83
+ idsByKey[key].endTimestamp = Math.max(idsByKey[key].endTimestamp || 0, endTimestamp);
55
84
  };
56
85
  if (o.type.includes('HUB')) {
57
- (_a = o.campaigns) === null || _a === void 0 ? void 0 : _a.forEach((c) => {
58
- var _a;
59
- if (!((_a = c.params) === null || _a === void 0 ? void 0 : _a.hubAddress) || c.params.assetId === undefined || c.params.assetId === null)
86
+ (_b = o.campaigns) === null || _b === void 0 ? void 0 : _b.forEach((c) => {
87
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
88
+ if ((_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.hubs) === null || _b === void 0 ? void 0 : _b.length) {
89
+ c.params.hubs.forEach((hub) => {
90
+ if (!hub.hubAddress)
91
+ return;
92
+ const rewardedIds = (side === IncentiveSide.Borrow ? hub.borrowAssetIds : hub.lendingAssetIds) || [];
93
+ rewardedIds.forEach((assetId) => collect(scopeKey(hub.hubAddress, assetId), c));
94
+ });
60
95
  return;
61
- collect(scopeKey(c.params.hubAddress, c.params.assetId), c);
96
+ }
97
+ const hubAddress = (_d = (_c = c.params) === null || _c === void 0 ? void 0 : _c.hubAddress) !== null && _d !== void 0 ? _d : (_g = (_f = (_e = c.params) === null || _e === void 0 ? void 0 : _e.distributionMethodParameters) === null || _f === void 0 ? void 0 : _f.distributionSettings) === null || _g === void 0 ? void 0 : _g.hubAddress;
98
+ const assetId = (_j = (_h = c.params) === null || _h === void 0 ? void 0 : _h.assetId) !== null && _j !== void 0 ? _j : (_m = (_l = (_k = c.params) === null || _k === void 0 ? void 0 : _k.distributionMethodParameters) === null || _l === void 0 ? void 0 : _l.distributionSettings) === null || _m === void 0 ? void 0 : _m.assetId;
99
+ if (!hubAddress || assetId === undefined || assetId === null)
100
+ return;
101
+ collect(scopeKey(hubAddress, assetId), c);
62
102
  });
63
103
  Object.entries(idsByKey).forEach(([key, ids]) => {
64
104
  if (!result.hub[key])
65
105
  result.hub[key] = {};
66
- result.hub[key][side] = [...(result.hub[key][side] || []), Object.assign(Object.assign({}, incentive), { campaignIds: [...ids.campaignIds] })];
106
+ result.hub[key][side] = [...(result.hub[key][side] || []), Object.assign(Object.assign({}, incentive), { endTimestamp: ids.endTimestamp, campaignIds: [...ids.campaignIds] })];
67
107
  });
68
108
  }
69
109
  else if (o.type.includes('SPOKE')) {
70
- (_b = o.campaigns) === null || _b === void 0 ? void 0 : _b.forEach((c) => {
71
- var _a;
72
- if (!((_a = c.params) === null || _a === void 0 ? void 0 : _a.spokeAddress) || c.params.reserveId === undefined || c.params.reserveId === null)
110
+ (_c = o.campaigns) === null || _c === void 0 ? void 0 : _c.forEach((c) => {
111
+ var _a, _b, _c;
112
+ if ((_b = (_a = c.params) === null || _a === void 0 ? void 0 : _a.spokes) === null || _b === void 0 ? void 0 : _b.length) {
113
+ c.params.spokes.forEach((spoke) => {
114
+ if (!spoke.spokeAddress)
115
+ return;
116
+ const rewardedTokens = (side === IncentiveSide.Borrow ? spoke.borrowTokens : spoke.supplyTokens) || [];
117
+ rewardedTokens.forEach((tokenScope) => {
118
+ if (tokenScope.reserveId === undefined || tokenScope.reserveId === null)
119
+ return;
120
+ collect(scopeKey(spoke.spokeAddress, tokenScope.reserveId), c);
121
+ });
122
+ });
123
+ return;
124
+ }
125
+ if (!((_c = c.params) === null || _c === void 0 ? void 0 : _c.spokeAddress) || c.params.reserveId === undefined || c.params.reserveId === null)
73
126
  return;
74
127
  collect(scopeKey(c.params.spokeAddress, c.params.reserveId), c);
75
128
  });
76
129
  Object.entries(idsByKey).forEach(([key, ids]) => {
77
130
  if (!result.spoke[key])
78
131
  result.spoke[key] = {};
79
- result.spoke[key][side] = [...(result.spoke[key][side] || []), Object.assign(Object.assign({}, incentive), { campaignIds: [...ids.campaignIds], parentCampaignIds: [...ids.parentCampaignIds] })];
132
+ result.spoke[key][side] = [...(result.spoke[key][side] || []), Object.assign(Object.assign({}, incentive), { endTimestamp: ids.endTimestamp, campaignIds: [...ids.campaignIds], parentCampaignIds: [...ids.parentCampaignIds] })];
80
133
  });
81
134
  }
82
135
  });
@@ -86,7 +139,7 @@ export const getAaveV4MerkleCampaigns = (chainId) => __awaiter(void 0, void 0, v
86
139
  try {
87
140
  const opportunities = yield fetchAllMerklOpportunities({
88
141
  mainProtocolId: 'aave',
89
- type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
142
+ type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW,AAVE_V4_HUB_NET_LENDING,AAVE_V4_HUB_NET_BORROWING,AAVE_V4_SPOKE_NET_LENDING,AAVE_V4_SPOKE_NET_BORROWING',
90
143
  status: OpportunityStatus.LIVE,
91
144
  campaigns: 'true',
92
145
  });
@@ -107,5 +160,7 @@ export const attachAaveV4MerklIncentives = (asset, spokeAddress, campaigns) => {
107
160
  const baseBorrow = asset.borrowIncentives || [];
108
161
  const spokeScoped = spokeAddress ? campaigns.spoke[scopeKey(spokeAddress, asset.reserveId)] : undefined;
109
162
  const hubScoped = asset.hub ? campaigns.hub[scopeKey(asset.hub, asset.assetId)] : undefined;
110
- 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, ...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, ...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 });
163
+ // Net-APR campaigns top up native yield; additive campaigns pay their APR on top.
164
+ 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() })));
165
+ 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
166
  };
@@ -29,7 +29,9 @@ export interface IncentiveData {
29
29
  apy: string;
30
30
  incentiveKind?: IncentiveKind;
31
31
  source?: IncentiveSource;
32
+ name?: string;
32
33
  description?: string;
34
+ endTimestamp?: number;
33
35
  eligibilityId?: IncentiveEligibilityId;
34
36
  }
35
37
  export type EthAddress = HexString;
@@ -18,12 +18,56 @@ export type MerklCampaign = {
18
18
  */
19
19
  parentCampaignId?: string;
20
20
  childCampaignIds?: string[];
21
+ startTimestamp?: number;
22
+ endTimestamp?: number;
21
23
  params?: {
22
24
  reserveId?: string | number;
23
25
  spokeAddress?: EthAddress;
24
26
  hubAddress?: EthAddress;
25
27
  hubAssetId?: string | number;
26
28
  assetId?: string | number;
29
+ /**
30
+ * AAVE_V4_HUB_NET_* campaigns scope per hub: the reward applies to the ids on the campaign's
31
+ * own side (`lendingAssetIds` for LEND, `borrowAssetIds` for BORROW) — the opposite-side ids
32
+ * only describe positions that reduce the net accrual, so they never map to a reward.
33
+ */
34
+ hubs?: {
35
+ hubAddress: EthAddress;
36
+ assets?: {
37
+ symbol: string;
38
+ assetId: string | number;
39
+ decimals?: number;
40
+ underlyingToken?: EthAddress;
41
+ }[];
42
+ lendingAssetIds?: (string | number)[];
43
+ borrowAssetIds?: (string | number)[];
44
+ }[];
45
+ /** AAVE_V4_SPOKE_NET_* campaigns scope per spoke, with the same own-side rule as `hubs`. */
46
+ spokes?: {
47
+ spokeAddress: EthAddress;
48
+ spokeName?: string;
49
+ supplyTokens?: {
50
+ symbol: string;
51
+ reserveId: string | number;
52
+ hubAddress?: EthAddress;
53
+ hubAssetId?: string | number;
54
+ underlyingToken?: EthAddress;
55
+ }[];
56
+ borrowTokens?: {
57
+ symbol: string;
58
+ reserveId: string | number;
59
+ hubAddress?: EthAddress;
60
+ hubAssetId?: string | number;
61
+ underlyingToken?: EthAddress;
62
+ }[];
63
+ }[];
64
+ distributionMethodParameters?: {
65
+ distributionMethod?: string;
66
+ distributionSettings?: {
67
+ hubAddress?: EthAddress;
68
+ assetId?: string | number;
69
+ };
70
+ };
27
71
  };
28
72
  };
29
73
  export type MerklOpportunity = {
@@ -102,6 +146,7 @@ export type MerkleRewardMap = Record<EthAddress, {
102
146
  export type AaveV4MerklIncentive = IncentiveData & {
103
147
  campaignIds?: string[];
104
148
  parentCampaignIds?: string[];
149
+ isAdditiveReward?: boolean;
105
150
  };
106
151
  export type AaveV4MerklScopedReward = {
107
152
  [side in IncentiveSide]?: AaveV4MerklIncentive[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.156",
3
+ "version": "2.1.158-dev",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -1,11 +1,14 @@
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,
8
10
  IncentiveSide,
11
+ IncentiveSource,
9
12
  MerklCampaign,
10
13
  MerklOpportunity,
11
14
  OpportunityAction,
@@ -17,6 +20,10 @@ import {
17
20
  * Merkl tags Aave V4 reward campaigns by scope via the `type` field:
18
21
  * - AAVE_V4_HUB_SUPPLY / AAVE_V4_HUB_BORROW → reward tied to a hub asset
19
22
  * - AAVE_V4_SPOKE_SUPPLY / AAVE_V4_SPOKE_BORROW → reward tied to a spoke reserve
23
+ * - AAVE_V4_HUB_NET_LENDING / AAVE_V4_SPOKE_NET_BORROWING → reward on the NET position
24
+ * (supply minus same-token borrows, or vice versa); params list scopes as `hubs[]`/`spokes[]`
25
+ * arrays instead of a flat address+id pair, and only the campaign's own side maps to a reward
26
+ * (the opposite-side ids describe what reduces the accrual)
20
27
  * Embedded campaign params provide the exact on-chain identifiers. Token addresses cannot safely
21
28
  * identify Aave V4 rewards because one spoke can expose the same underlying from multiple hubs.
22
29
  * Each stored reward is also tagged with campaign identity (`campaignIds`/`parentCampaignIds`) so
@@ -33,46 +40,95 @@ const buildIncentive = (opportunity: MerklOpportunity): IncentiveData => {
33
40
  apy: aprToApy(opportunity.apr),
34
41
  token,
35
42
  incentiveKind: IncentiveKind.Reward,
36
- description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
43
+ source: IncentiveSource.Merkl,
44
+ name: opportunity.name,
45
+ description: opportunity.description || `Eligible for ${token} rewards through Merkl.`,
37
46
  };
38
47
  };
39
48
 
40
49
  export const buildAaveV4MerklRewardMap = (opportunities: MerklOpportunity[], chainId: NetworkNumber): AaveV4MerklRewardMap => {
41
50
  const result: AaveV4MerklRewardMap = { hub: {}, spoke: {} };
42
51
 
52
+ const endByCampaignId: Record<string, number> = {};
53
+ opportunities.forEach((o) => o.campaigns?.forEach((c) => {
54
+ if (c.id && c.endTimestamp) endByCampaignId[c.id] = +c.endTimestamp;
55
+ }));
56
+ const effectiveEndTimestamp = (campaign: MerklCampaign): number => Math.max(
57
+ campaign.endTimestamp ? +campaign.endTimestamp : 0,
58
+ ...(campaign.childCampaignIds || []).map((id) => endByCampaignId[id] || 0),
59
+ );
60
+
43
61
  opportunities
44
62
  .filter((o) => o.chainId === chainId)
45
63
  .filter((o) => o.status === OpportunityStatus.LIVE)
46
64
  .filter((o) => typeof o.type === 'string' && o.type.startsWith('AAVE_V4_'))
47
65
  .forEach((o) => {
48
66
  const side = o.action === OpportunityAction.BORROW ? IncentiveSide.Borrow : IncentiveSide.Supply;
49
- const incentive = buildIncentive(o);
67
+ const now = Date.now() / 1000;
68
+ const methods = o.campaigns
69
+ ?.filter((c) => (c.startTimestamp === undefined || c.startTimestamp <= now)
70
+ && (c.endTimestamp === undefined || c.endTimestamp > now))
71
+ .map((c) => c.params?.distributionMethodParameters?.distributionMethod);
72
+ // An opportunity-level APR cannot be split between simultaneous net and additive campaigns.
73
+ if (side === IncentiveSide.Supply && methods?.includes('AAVE_V4_NET_APR')
74
+ && methods.some((method) => !!method && method !== 'AAVE_V4_NET_APR')) return;
75
+ const incentive = {
76
+ ...buildIncentive(o),
77
+ // Missing methods keep the existing target-yield behavior.
78
+ isAdditiveReward: side === IncentiveSide.Supply && !!methods?.length
79
+ && methods.every((method) => !!method && method !== 'AAVE_V4_NET_APR'),
80
+ };
50
81
  // one opportunity can span several campaigns (e.g. renewed periods), so campaign identity is
51
82
  // collected per scope key before the reward entries are written
52
- const idsByKey: Record<string, { campaignIds: Set<string>, parentCampaignIds: Set<string> }> = {};
83
+ const idsByKey: Record<string, { campaignIds: Set<string>, parentCampaignIds: Set<string>, endTimestamp?: number }> = {};
53
84
  const collect = (key: string, campaign: MerklCampaign) => {
54
85
  if (!idsByKey[key]) idsByKey[key] = { campaignIds: new Set(), parentCampaignIds: new Set() };
55
86
  if (campaign.id) idsByKey[key].campaignIds.add(campaign.id);
56
87
  if (campaign.parentCampaignId) idsByKey[key].parentCampaignIds.add(campaign.parentCampaignId);
88
+ const endTimestamp = effectiveEndTimestamp(campaign);
89
+ if (endTimestamp) idsByKey[key].endTimestamp = Math.max(idsByKey[key].endTimestamp || 0, endTimestamp);
57
90
  };
58
91
 
59
92
  if (o.type.includes('HUB')) {
60
93
  o.campaigns?.forEach((c) => {
61
- if (!c.params?.hubAddress || c.params.assetId === undefined || c.params.assetId === null) return;
62
- collect(scopeKey(c.params.hubAddress, c.params.assetId), c);
94
+ if (c.params?.hubs?.length) {
95
+ c.params.hubs.forEach((hub) => {
96
+ if (!hub.hubAddress) return;
97
+ const rewardedIds = (side === IncentiveSide.Borrow ? hub.borrowAssetIds : hub.lendingAssetIds) || [];
98
+ rewardedIds.forEach((assetId) => collect(scopeKey(hub.hubAddress, assetId), c));
99
+ });
100
+ return;
101
+ }
102
+ const hubAddress = c.params?.hubAddress ?? c.params?.distributionMethodParameters?.distributionSettings?.hubAddress;
103
+ const assetId = c.params?.assetId ?? c.params?.distributionMethodParameters?.distributionSettings?.assetId;
104
+ if (!hubAddress || assetId === undefined || assetId === null) return;
105
+ collect(scopeKey(hubAddress, assetId), c);
63
106
  });
64
107
  Object.entries(idsByKey).forEach(([key, ids]) => {
65
108
  if (!result.hub[key]) result.hub[key] = {};
66
- result.hub[key][side] = [...(result.hub[key][side] || []), { ...incentive, campaignIds: [...ids.campaignIds] }];
109
+ result.hub[key][side] = [...(result.hub[key][side] || []), { ...incentive, endTimestamp: ids.endTimestamp, campaignIds: [...ids.campaignIds] }];
67
110
  });
68
111
  } else if (o.type.includes('SPOKE')) {
69
112
  o.campaigns?.forEach((c) => {
113
+ if (c.params?.spokes?.length) {
114
+ c.params.spokes.forEach((spoke) => {
115
+ if (!spoke.spokeAddress) return;
116
+ const rewardedTokens = (side === IncentiveSide.Borrow ? spoke.borrowTokens : spoke.supplyTokens) || [];
117
+ rewardedTokens.forEach((tokenScope) => {
118
+ if (tokenScope.reserveId === undefined || tokenScope.reserveId === null) return;
119
+ collect(scopeKey(spoke.spokeAddress, tokenScope.reserveId), c);
120
+ });
121
+ });
122
+ return;
123
+ }
70
124
  if (!c.params?.spokeAddress || c.params.reserveId === undefined || c.params.reserveId === null) return;
71
125
  collect(scopeKey(c.params.spokeAddress, c.params.reserveId), c);
72
126
  });
73
127
  Object.entries(idsByKey).forEach(([key, ids]) => {
74
128
  if (!result.spoke[key]) result.spoke[key] = {};
75
- result.spoke[key][side] = [...(result.spoke[key][side] || []), { ...incentive, campaignIds: [...ids.campaignIds], parentCampaignIds: [...ids.parentCampaignIds] }];
129
+ result.spoke[key][side] = [...(result.spoke[key][side] || []), {
130
+ ...incentive, endTimestamp: ids.endTimestamp, campaignIds: [...ids.campaignIds], parentCampaignIds: [...ids.parentCampaignIds],
131
+ }];
76
132
  });
77
133
  }
78
134
  });
@@ -84,7 +140,7 @@ export const getAaveV4MerkleCampaigns = async (chainId: NetworkNumber): Promise<
84
140
  try {
85
141
  const opportunities = await fetchAllMerklOpportunities({
86
142
  mainProtocolId: 'aave',
87
- type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW',
143
+ type: 'AAVE_V4_HUB_SUPPLY,AAVE_V4_HUB_BORROW,AAVE_V4_SPOKE_SUPPLY,AAVE_V4_SPOKE_BORROW,AAVE_V4_HUB_NET_LENDING,AAVE_V4_HUB_NET_BORROWING,AAVE_V4_SPOKE_NET_LENDING,AAVE_V4_SPOKE_NET_BORROWING',
88
144
  status: OpportunityStatus.LIVE,
89
145
  campaigns: 'true',
90
146
  });
@@ -105,12 +161,17 @@ export const attachAaveV4MerklIncentives = (asset: AaveV4ReserveAssetData, spoke
105
161
 
106
162
  const spokeScoped = spokeAddress ? campaigns.spoke[scopeKey(spokeAddress, asset.reserveId)] : undefined;
107
163
  const hubScoped = asset.hub ? campaigns.hub[scopeKey(asset.hub, asset.assetId)] : undefined;
164
+ // Net-APR campaigns top up native yield; additive campaigns pay their APR on top.
165
+ const supplyRewards = (rewards: AaveV4MerklIncentive[] = []) => rewards.map((reward) => ({
166
+ ...reward,
167
+ apy: reward.isAdditiveReward ? reward.apy : Dec.max(0, new Dec(reward.apy).minus(asset.supplyRate || 0)).toString(),
168
+ }));
108
169
 
109
170
  return {
110
171
  ...asset,
111
- spokeSupplyIncentives: spokeScoped?.supply?.length ? [...baseSupply, ...spokeScoped.supply] : baseSupply,
172
+ spokeSupplyIncentives: spokeScoped?.supply?.length ? [...baseSupply, ...supplyRewards(spokeScoped.supply)] : baseSupply,
112
173
  spokeBorrowIncentives: spokeScoped?.borrow?.length ? [...baseBorrow, ...spokeScoped.borrow] : baseBorrow,
113
- hubSupplyIncentives: hubScoped?.supply?.length ? [...baseSupply, ...hubScoped.supply] : baseSupply,
174
+ hubSupplyIncentives: hubScoped?.supply?.length ? [...baseSupply, ...supplyRewards(hubScoped.supply)] : baseSupply,
114
175
  hubBorrowIncentives: hubScoped?.borrow?.length ? [...baseBorrow, ...hubScoped.borrow] : baseBorrow,
115
176
  };
116
177
  };
@@ -34,7 +34,9 @@ export interface IncentiveData {
34
34
  apy: string,
35
35
  incentiveKind?: IncentiveKind;
36
36
  source?: IncentiveSource;
37
+ name?: string;
37
38
  description?: string;
39
+ endTimestamp?: number;
38
40
  eligibilityId?: IncentiveEligibilityId;
39
41
  }
40
42
 
@@ -21,12 +21,39 @@ export type MerklCampaign = {
21
21
  */
22
22
  parentCampaignId?: string;
23
23
  childCampaignIds?: string[];
24
+ startTimestamp?: number;
25
+ endTimestamp?: number;
24
26
  params?: {
25
27
  reserveId?: string | number;
26
28
  spokeAddress?: EthAddress;
27
29
  hubAddress?: EthAddress;
28
30
  hubAssetId?: string | number;
29
31
  assetId?: string | number;
32
+ /**
33
+ * AAVE_V4_HUB_NET_* campaigns scope per hub: the reward applies to the ids on the campaign's
34
+ * own side (`lendingAssetIds` for LEND, `borrowAssetIds` for BORROW) — the opposite-side ids
35
+ * only describe positions that reduce the net accrual, so they never map to a reward.
36
+ */
37
+ hubs?: {
38
+ hubAddress: EthAddress;
39
+ assets?: { symbol: string; assetId: string | number; decimals?: number; underlyingToken?: EthAddress }[];
40
+ lendingAssetIds?: (string | number)[];
41
+ borrowAssetIds?: (string | number)[];
42
+ }[];
43
+ /** AAVE_V4_SPOKE_NET_* campaigns scope per spoke, with the same own-side rule as `hubs`. */
44
+ spokes?: {
45
+ spokeAddress: EthAddress;
46
+ spokeName?: string;
47
+ supplyTokens?: { symbol: string; reserveId: string | number; hubAddress?: EthAddress; hubAssetId?: string | number; underlyingToken?: EthAddress }[];
48
+ borrowTokens?: { symbol: string; reserveId: string | number; hubAddress?: EthAddress; hubAssetId?: string | number; underlyingToken?: EthAddress }[];
49
+ }[];
50
+ distributionMethodParameters?: {
51
+ distributionMethod?: string;
52
+ distributionSettings?: {
53
+ hubAddress?: EthAddress;
54
+ assetId?: string | number;
55
+ };
56
+ };
30
57
  };
31
58
  };
32
59
 
@@ -100,6 +127,7 @@ export type MerkleRewardMap = Record<EthAddress, { supply?: MerkleRewardInfo; bo
100
127
  export type AaveV4MerklIncentive = IncentiveData & {
101
128
  campaignIds?: string[];
102
129
  parentCampaignIds?: string[];
130
+ isAdditiveReward?: boolean;
103
131
  };
104
132
 
105
133
  export type AaveV4MerklScopedReward = { [side in IncentiveSide]?: AaveV4MerklIncentive[] };