Skip to content

Commit 26f22b3

Browse files
noatedenHartick
andauthored
Bend pr (#4573)
* fix: include vaults, remove protocol revenue and fix wrong fee amount from AccrueInterest * fix: get market from events * chore: add dailyProtocolRevenue * refactor --------- Co-authored-by: Bearetta Bera <[email protected]>
1 parent 367e961 commit 26f22b3

File tree

1 file changed

+116
-14
lines changed

1 file changed

+116
-14
lines changed

fees/bend/index.ts

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { request } from "graphql-request";
12
import { FetchOptions, FetchV2, SimpleAdapter } from "../../adapters/types";
23
import { CHAIN } from "../../helpers/chains";
34
import { METRIC } from "../../helpers/metrics";
@@ -11,22 +12,38 @@ const CONFIG = {
1112
const methodology = {
1213
Fees: "Total borrow interest paid by borrowers + liquidation bonuses earned by liquidators.",
1314
SupplySideRevenue: "Total interests are distributed to suppliers/lenders + liquidation bonuses to liquidators.",
14-
Revenue: "Total fees paid by borrowers from all markets and part of performance fee retained from all vaults.",
15-
ProtocolRevenue: "No revenue for Bend protocol.",
15+
Revenue: "Total interest paid by borrowers and part of performance fees share for Bend protocol.",
16+
ProtocolRevenue: "Total interest paid by borrowers and part of performance fees share for Bend protocol.",
1617
}
1718

18-
const MorphoBlueAbis = {
19-
AccrueInterest: "event AccrueInterest(bytes32 indexed id, uint256 prevBorrowRate, uint256 interest, uint256 feeShares)",
20-
Liquidate: "event Liquidate(bytes32 indexed id,address indexed caller,address indexed borrower,uint256 repaidAssets,uint256 repaidShares,uint256 seizedAssets,uint256 badDebtAssets,uint256 badDebtShares)",
21-
CreateMarket: "event CreateMarket(bytes32 indexed id, tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams)",
22-
};
19+
const abi = {
20+
morphoBlueFunctions: {
21+
market: "function market(bytes32 input) returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
22+
feeRecipient: "function feeRecipient() returns(address feeRecipient)"
23+
},
24+
metaMorphoFunctions: {
25+
withdrawQueueLength: "function withdrawQueueLength() view returns (uint256)",
26+
withdrawQueue: "function withdrawQueue(uint256 index) view returns (bytes32)",
27+
asset: "function asset() view returns (address)",
28+
convertToAssets: "function convertToAssets(uint256 shares) view returns (uint256)"
29+
},
30+
morphoBlueEvents: {
31+
AccrueInterest: "event AccrueInterest(bytes32 indexed id, uint256 prevBorrowRate, uint256 interest, uint256 feeShares)",
32+
CreateMarket: "event CreateMarket(bytes32 indexed id, tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams)",
33+
Liquidate: "event Liquidate(bytes32 indexed id,address indexed caller,address indexed borrower,uint256 repaidAssets,uint256 repaidShares,uint256 seizedAssets,uint256 badDebtAssets,uint256 badDebtShares)"
34+
},
35+
metaMorphoEvents: {
36+
Transfer: "event Transfer(address indexed from, address indexed to, uint256 value)"
37+
}
38+
}
2339

2440
type MorphoMarket = {
2541
marketId: string;
2642
loanAsset: string;
2743
collateralAsset?: string;
2844
lltv: bigint;
2945
lif: bigint;
46+
fee: bigint;
3047
};
3148

3249
type MorphoBlueAccrueInterestEvent = {
@@ -41,12 +58,26 @@ type MorphoBlueLiquidateEvent = {
4158
seizedAmount: bigint;
4259
};
4360

61+
type PerformanceFee = {
62+
token: string,
63+
amount: bigint
64+
}
65+
66+
type RewardVault = {
67+
stakingTokenAddress: string
68+
}
69+
70+
const BERACHAIN_API = "https://api.berachain.com";
71+
72+
4473
const toLowerKey = (id: string) => id.toLowerCase();
4574

4675
const ONE = 10n ** 18n;
4776

4877
const wMul = (x: bigint, y: bigint): bigint => (x * y / ONE);
4978

79+
const ZERO_ADDRESS = '0x' + '0'.repeat(40);
80+
5081
// https://docs.morpho.org/learn/concepts/liquidation/#liquidation-incentive-factor-lif
5182
function _getLIFFromLLTV(lltv: bigint): bigint {
5283
const B = BigInt(3e17) // 0.3
@@ -55,24 +86,46 @@ function _getLIFFromLLTV(lltv: bigint): bigint {
5586
return LIF > M ? M : LIF
5687
}
5788

89+
const _getWhitelistedVaults = async () => {
90+
const data = await request(BERACHAIN_API, `
91+
{
92+
polGetRewardVaults(where: {protocolsIn: ["Bend"], includeNonWhitelisted: false}) {
93+
vaults {
94+
stakingTokenAddress
95+
}
96+
}
97+
}
98+
`);
99+
return data.polGetRewardVaults.vaults.map((v: RewardVault) => v.stakingTokenAddress);
100+
}
101+
58102
const fetchMarketsFromLogs = async (options: FetchOptions): Promise<Array<MorphoMarket>> => {
59103
const markets: Array<MorphoMarket> = [];
60104

61105
const events = await options.getLogs({
62106
target: CONFIG.blue,
63-
eventAbi: MorphoBlueAbis.CreateMarket,
107+
eventAbi: abi.morphoBlueEvents.CreateMarket,
64108
fromBlock: CONFIG.fromBlock,
65109
});
66110

67-
for (const event of events) {
111+
const marketIds = events.map(event => event.id)
112+
113+
const marketsInfo = await options.api.multiCall({
114+
target: CONFIG.blue,
115+
calls: marketIds,
116+
abi: abi.morphoBlueFunctions.market,
117+
});
118+
119+
events.forEach((event, idx) => {
68120
markets.push({
69121
marketId: event.id,
70122
loanAsset: event.marketParams.loanToken,
71123
collateralAsset: event.marketParams.collateralToken,
72124
lltv: BigInt(event.marketParams.lltv),
73125
lif: _getLIFFromLLTV(BigInt(event.marketParams.lltv)),
126+
fee: BigInt(marketsInfo[idx]?.fee)
74127
})
75-
}
128+
})
76129

77130
return markets;
78131
}
@@ -82,23 +135,24 @@ const fetchEvents = async (
82135
): Promise<{ interests: Array<MorphoBlueAccrueInterestEvent>, liquidations: Array<MorphoBlueLiquidateEvent> }> => {
83136
let markets: Array<MorphoMarket> = await fetchMarketsFromLogs(options)
84137

85-
86138
const marketMap = {} as { [key: string]: MorphoMarket };
87139
markets.forEach((item) => {
88140
marketMap[item.marketId.toLowerCase()] = item;
89141
});
90142

91143
const interests: Array<MorphoBlueAccrueInterestEvent> = (
92144
await options.getLogs({
93-
eventAbi: MorphoBlueAbis.AccrueInterest,
145+
eventAbi: abi.morphoBlueEvents.AccrueInterest,
94146
target: CONFIG.blue,
95147
})
96148
).map((log: any) => {
97149
const key = toLowerKey(String(log.id));
98150
const market = marketMap[key];
99151

100152
const interest = BigInt(log.interest);
101-
const feeAmount = BigInt(log.feeShares);
153+
const feeParam = market?.fee ?? 0n;
154+
155+
const feeAmount = wMul(interest, feeParam);
102156

103157
return {
104158
token: market?.loanAsset ?? null,
@@ -110,7 +164,7 @@ const fetchEvents = async (
110164

111165
const liquidations: Array<MorphoBlueLiquidateEvent> = (
112166
await options.getLogs({
113-
eventAbi: MorphoBlueAbis.Liquidate,
167+
eventAbi: abi.morphoBlueEvents.Liquidate,
114168
target: CONFIG.blue,
115169
})
116170
).map((log) => {
@@ -129,12 +183,52 @@ const fetchEvents = async (
129183
return { interests, liquidations }
130184
};
131185

186+
187+
const fetchPerformanceFees = async (options: FetchOptions): Promise<Array<PerformanceFee>> => {
188+
const vaults = await _getWhitelistedVaults();
189+
190+
const [feeRecipient, underlyingAssets] = await Promise.all(
191+
[
192+
options.api.call({
193+
abi: abi.morphoBlueFunctions.feeRecipient,
194+
target: CONFIG.blue,
195+
}),
196+
options.api.multiCall({
197+
abi: abi.metaMorphoFunctions.asset,
198+
calls: vaults,
199+
}
200+
)
201+
])
202+
203+
return Promise.all(vaults.map(async (v: string, idx: number) => {
204+
const sharesAmount = (await options.getLogs({
205+
target: v,
206+
eventAbi: abi.metaMorphoEvents.Transfer,
207+
}))
208+
.filter(log => log[0] == ZERO_ADDRESS && log[1] == feeRecipient)
209+
.map(log => log[2])
210+
.reduce((totalAmount: bigint, value: bigint) => totalAmount + value, 0n);
211+
212+
const assetAmount = await options.api.call({
213+
target: v,
214+
abi: abi.metaMorphoFunctions.convertToAssets,
215+
params: [sharesAmount]
216+
})
217+
218+
return {
219+
token: underlyingAssets[idx],
220+
amount: assetAmount
221+
} as PerformanceFee
222+
}))
223+
}
224+
132225
const fetch: FetchV2 = async (options: FetchOptions) => {
133226
const dailyFees = options.createBalances();
134227
const dailyRevenue = options.createBalances();
135228
const dailySupplySideRevenue = options.createBalances();
136229

137230
const { interests, liquidations } = await fetchEvents(options);
231+
const performanceFees = await fetchPerformanceFees(options)
138232

139233
for (const event of interests) {
140234
if (!event.token) continue;
@@ -154,6 +248,11 @@ const fetch: FetchV2 = async (options: FetchOptions) => {
154248
dailySupplySideRevenue.add(event.token, bonus, METRIC.LIQUIDATION_FEES);
155249
}
156250

251+
for (const performanceFee of performanceFees) {
252+
dailyFees.add(performanceFee.token, performanceFee.amount, METRIC.PERFORMANCE_FEES)
253+
dailyRevenue.add(performanceFee.token, performanceFee.amount, METRIC.PERFORMANCE_FEES)
254+
}
255+
157256
return {
158257
dailyFees: dailyFees,
159258
dailySupplySideRevenue,
@@ -168,17 +267,20 @@ const adapter: SimpleAdapter = {
168267
breakdownMethodology: {
169268
Fees: {
170269
[METRIC.BORROW_INTEREST]: 'All interest paid by borrowers from all markets.',
270+
[METRIC.PERFORMANCE_FEES]: 'Share of interest for Bend protocol paid by Vault curator.',
171271
[METRIC.LIQUIDATION_FEES]: 'All bonuses earned by liquidators from liquidations.',
172272
},
173273
Revenue: {
174274
[METRIC.BORROW_INTEREST]: 'Share of interest for Bend protocol.',
275+
[METRIC.PERFORMANCE_FEES]: 'Share of interest for Bend protocol paid by Vault curator.',
175276
},
176277
SupplySideRevenue: {
177278
[METRIC.BORROW_INTEREST]: 'All interests paid are distributedd to vaults suppliers, lenders.',
178279
[METRIC.LIQUIDATION_FEES]: 'All bonuses earned by liquidators from liquidations.',
179280
},
180281
ProtocolRevenue: {
181282
[METRIC.BORROW_INTEREST]: 'Share of interest for Bend protocol.',
283+
[METRIC.PERFORMANCE_FEES]: 'Share of interest for Bend protocol paid by Vault curator.',
182284
},
183285
},
184286
fetch: fetch,

0 commit comments

Comments
 (0)