Sub-Adapters 11
Preview and test each sub adapter.
Aave - Version 1 (aave-v1)
Aave - Version 2 (aave-v2)
Aave - Version 2 - AMM (aave-v2-amm)
Aave - Version 2 - Polygon (aave-v2-polygon-proto)
Aave - Version 3 - Polygon (aave-v3-polygon-proto)
Aave - Version 2 - Avalanche (aave-v2-avalanche-proto)
Aave - Version 3 - Avalanche (aave-v3-avalanche-proto)
Aave - Version 3 - Optimism (aave-v3-optimism-proto)
Aave - Version 3 - Arbitrum (aave-v3-arbitrum-proto)
Aave - Version 3 - Fantom (aave-v3-fantom-proto)
Aave - Version 3 - Harmony (aave-v3-harmony-proto)
Adapter Code
Check the entire code written for the Adapter.
Source code
Showing TS source.
1export const name = 'Aave Fees';
2export const version = '0.3.7';
3export const license = 'MIT';
4
5type V1Reserve = {
6 lifetimeFlashloanDepositorsFee: string
7 lifetimeFlashloanProtocolFee: string
8 lifetimeOriginationFee: string
9 lifetimeDepositorsInterestEarned: string
10 priceInUsd: string
11 reserve: {
12 decimals: number
13 symbol: string
14 }
15}
16
17type V2Reserve = {
18 lifetimeFlashLoanPremium: string
19 lifetimeReserveFactorAccrued: string
20 lifetimeDepositorsInterestEarned: string
21 priceInUsd: string
22 reserve: {
23 decimals: number
24 symbol: string
25 }
26}
27
28type V3Reserve = {
29 lifetimeFlashLoanLPPremium : string
30 lifetimeFlashLoanProtocolPremium: string
31 lifetimePortalLPFee: string
32 lifetimePortalProtocolFee: string
33 // in v3 lifetimeReserveFactorAccrued contains the claimed amount and accruedToTreasury the currently claimable
34 // for gas saving it's not automatically transfered to treasury any more
35 lifetimeReserveFactorAccrued: string
36 accruedToTreasury: string
37 lifetimeDepositorsInterestEarned: string
38 priceInUsd: string
39 reserve: {
40 decimals: number
41 symbol: string
42 }
43}
44
45const SUBGRAPHS = {
46 V1: 'aave/protocol-multy-raw',
47 V2: 'aave/protocol-v2',
48 POLYGON: 'aave/aave-v2-matic',
49 POLYGON_V3: 'aave/protocol-v3-polygon',
50 AVALANCHE: 'aave/protocol-v2-avalanche',
51 AVALANCHE_V3: 'aave/protocol-v3-avalanche',
52 ARBITRUM_V3: 'aave/protocol-v3-arbitrum',
53 OPTIMISM_V3: 'aave/protocol-v3-optimism',
54 FANTOM_V3: 'aave/protocol-v3-fantom',
55 HARMONY_V3: 'aave/protocol-v3-harmony'
56}
57
58const POOL_IDS = {
59 V1: '0x24a42fd28c976a61df5d00d0599c34c4f90748c8',
60 V2: '0xb53c1a33016b2dc2ff3653530bff1848a515c8c5',
61 V2_AMM: '0xacc030ef66f9dfeae9cbb0cd1b25654b82cfa8d5',
62 V2_POLYGON: '0xd05e3e715d945b59290df0ae8ef85c1bdb684744',
63 V2_AVALANCHE: '0xb6a86025f0fe1862b372cb0ca18ce3ede02a318f',
64 // v3 uses the same address on all networks
65 V3: '0xa97684ead0e402dc232d5a977953df7ecbab3cdb'
66}
67
68function expectNextDayExists(reserves: {nextDay: []}[]) {
69 if(!reserves.some(reserve => reserve.nextDay?.length !== 0)){
70 throw new Error('day might not be finished yet')
71 }
72}
73
74const ONE_DAY = 24 * 60 * 60;
75
76export function setup(sdk: Context) {
77 async function getV1ReservesSnapshot(poolId: string, timestamp: number) {
78 const query = `{
79 reserves(where: { pool: "${poolId}" }) {
80 id
81 paramsHistory(
82 where: { timestamp_lte: ${timestamp}, timestamp_gte: ${timestamp - ONE_DAY} },
83 orderBy: "timestamp",
84 orderDirection: "desc",
85 first: 1
86 ) {
87 id
88 priceInUsd
89 reserve {
90 decimals
91 symbol
92 }
93 lifetimeFlashloanDepositorsFee
94 lifetimeFlashloanProtocolFee
95 lifetimeOriginationFee
96 lifetimeDepositorsInterestEarned
97 }
98 nextDay: paramsHistory(
99 where: { timestamp_gte: ${timestamp}, timestamp_lte: ${timestamp + ONE_DAY} },
100 first: 1
101 ) {
102 id
103 }
104 }
105 }`;
106
107 const result = await sdk.graph.query(SUBGRAPHS.V1, query)
108 expectNextDayExists(result.reserves)
109 const reserves = result.reserves
110 .map((r: any) => r.paramsHistory[0])
111 .filter((r: any) => r)
112
113 return reserves
114 }
115
116 function calculateV1Fees(startReserves: V1Reserve[], endReserves: V1Reserve[]) {
117 return endReserves.reduce((acc: number, reserve: V1Reserve) => {
118 const startReserve = startReserves
119 .find((r: any) => r.reserve.symbol === reserve.reserve.symbol)
120
121 if (!startReserve) {
122 return acc;
123 }
124
125 const priceInUsd = parseFloat(reserve.priceInUsd);
126
127 const depositorInterest = parseFloat(reserve.lifetimeDepositorsInterestEarned)
128 - parseFloat(startReserve.lifetimeDepositorsInterestEarned);
129 const depositorInterestUSD = depositorInterest * priceInUsd / (10 ** reserve.reserve.decimals);
130
131 const originationFees = parseFloat(reserve.lifetimeOriginationFee)
132 - parseFloat(startReserve.lifetimeOriginationFee);
133 const originationFeesUSD = originationFees * priceInUsd / (10 ** reserve.reserve.decimals);
134
135 const flashloanDepositorsFees = parseFloat(reserve.lifetimeFlashloanDepositorsFee)
136 - parseFloat(startReserve.lifetimeFlashloanDepositorsFee);
137 const flashloanDepositorsFeesUSD = flashloanDepositorsFees * priceInUsd / (10 ** reserve.reserve.decimals);
138
139 const flashloanProtocolFees = parseFloat(reserve.lifetimeFlashloanProtocolFee)
140 - parseFloat(startReserve.lifetimeFlashloanProtocolFee);
141 const flashloanProtocolFeesUSD = flashloanProtocolFees * priceInUsd / (10 ** reserve.reserve.decimals);
142
143 return acc
144 + depositorInterestUSD
145 + originationFeesUSD
146 + flashloanProtocolFeesUSD
147 + flashloanDepositorsFeesUSD;
148 }, 0);
149 }
150
151 const getV1Fees = async (date: string): Promise<number> => {
152 const poolId = POOL_IDS.V1;
153 const startTimestamp = sdk.date.dateToTimestamp(date);
154 const nextDayTimestamp = sdk.date.dateToTimestamp(sdk.date.offsetDaysFormatted(date, 1));
155
156 const startReserves = await getV1ReservesSnapshot(poolId, startTimestamp);
157 const endReserves = await getV1ReservesSnapshot(poolId, nextDayTimestamp);
158
159 return calculateV1Fees(startReserves, endReserves);
160 }
161
162 ////
163 // V2 deployments
164 ////
165
166 async function getV2ReservesSnapshot(poolId: string, timestamp: number, subgraph: string) {
167 const query = `{
168 reserves(where: { pool: "${poolId}" }) {
169 id
170 paramsHistory(
171 where: { timestamp_lte: ${timestamp}, timestamp_gte: ${timestamp - ONE_DAY} },
172 orderBy: "timestamp",
173 orderDirection: "desc",
174 first: 1
175 ) {
176 id
177 priceInEth
178 priceInUsd
179 reserve {
180 decimals
181 symbol
182 }
183 lifetimeFlashLoanPremium
184 lifetimeReserveFactorAccrued
185 lifetimeDepositorsInterestEarned
186 }
187 nextDay: paramsHistory(
188 where: { timestamp_gte: ${timestamp}, timestamp_lte: ${timestamp + ONE_DAY} },
189 first: 1
190 ) {
191 id
192 }
193 }
194 }`;
195
196 const result = await sdk.graph.query(subgraph, query)
197 expectNextDayExists(result.reserves)
198 const reserves = result.reserves
199 .map((r: any) => r.paramsHistory[0])
200 .filter((r: any) => r)
201
202 return reserves
203 }
204
205 function calculateV2Fees(startReserves: V2Reserve[], endReserves: V2Reserve[], usdPriceFeed?: boolean) {
206 return endReserves.reduce((acc: number, reserve: V2Reserve) => {
207 const startReserve = startReserves
208 .find((r: any) => r.reserve.symbol === reserve.reserve.symbol)
209
210 if (!startReserve) {
211 return acc;
212 }
213
214 const priceInUsd = usdPriceFeed
215 ? parseFloat(reserve.priceInUsd) / (10 ** 8)
216 : parseFloat(reserve.priceInUsd)
217
218 const depositorInterest = parseFloat(reserve.lifetimeDepositorsInterestEarned)
219 - (parseFloat(startReserve?.lifetimeDepositorsInterestEarned) || 0);
220 const depositorInterestUSD = depositorInterest * priceInUsd / (10 ** reserve.reserve.decimals);
221
222 const flashloanPremium = parseFloat(reserve.lifetimeFlashLoanPremium)
223 - (parseFloat(startReserve?.lifetimeFlashLoanPremium) || 0);
224 const flashloanPremiumUSD = flashloanPremium * priceInUsd / (10 ** reserve.reserve.decimals);
225
226 const reserveFactor = parseFloat(reserve.lifetimeReserveFactorAccrued)
227 - (parseFloat(startReserve.lifetimeReserveFactorAccrued) || 0);
228 const reserveFactorUSD = reserveFactor * priceInUsd / (10 ** reserve.reserve.decimals);
229
230 return acc
231 + depositorInterestUSD
232 + flashloanPremiumUSD
233 + reserveFactorUSD;
234 }, 0);
235 }
236
237 const getV2Fees = (
238 poolId: string,
239 subgraph: string,
240 usdPriceFeed?: boolean
241 ) => async (date: string): Promise<number> => {
242 const startTimestamp = sdk.date.dateToTimestamp(date);
243 const nextDayTimestamp = sdk.date.dateToTimestamp(sdk.date.offsetDaysFormatted(date, 1));
244
245 const startReserves = await getV2ReservesSnapshot(poolId, startTimestamp, subgraph);
246 const endReserves = await getV2ReservesSnapshot(poolId, nextDayTimestamp, subgraph);
247
248 return calculateV2Fees(startReserves, endReserves, usdPriceFeed);
249 }
250
251 async function getV3ReservesSnapshot(poolId: string, timestamp: number, subgraph: string) {
252 const query = `{
253 reserves(where: { pool: "${poolId}" }) {
254 id
255 paramsHistory(
256 where: { timestamp_lte: ${timestamp}, timestamp_gte: ${timestamp - ONE_DAY} },
257 orderBy: "timestamp",
258 orderDirection: "desc",
259 first: 1
260 ) {
261 id
262 priceInEth
263 priceInUsd
264 reserve {
265 decimals
266 symbol
267 }
268 lifetimeFlashLoanLPPremium
269 lifetimeFlashLoanProtocolPremium
270 lifetimePortalLPFee
271 lifetimePortalProtocolFee
272 lifetimeReserveFactorAccrued
273 lifetimeDepositorsInterestEarned: lifetimeSuppliersInterestEarned
274 accruedToTreasury
275 }
276 nextDay: paramsHistory(
277 where: { timestamp_gte: ${timestamp}, timestamp_lte: ${timestamp + ONE_DAY} },
278 first: 1
279 ) {
280 id
281 }
282 }
283 }`;
284
285 const result = await sdk.graph.query(subgraph, query)
286 expectNextDayExists(result.reserves)
287 const reserves = result.reserves
288 .map((r: any) => r.paramsHistory[0])
289 .filter((r: any) => r)
290
291 return reserves
292 }
293
294 function calculateV3FeeBreakdown(startReserves: V3Reserve[], endReserves: V3Reserve[], usdPriceFeed?: boolean) {
295 return endReserves.reduce((acc, reserve: V3Reserve) => {
296 const startReserve = startReserves
297 .find((r: any) => r.reserve.symbol === reserve.reserve.symbol)
298
299 if (!startReserve) {
300 return acc;
301 }
302
303 const priceInUsd = usdPriceFeed
304 ? parseFloat(reserve.priceInUsd) / (10 ** 8)
305 : parseFloat(reserve.priceInUsd)
306
307 const depositorInterest = parseFloat(reserve.lifetimeDepositorsInterestEarned)
308 - parseFloat(startReserve?.lifetimeDepositorsInterestEarned);
309 const depositorInterestUSD = depositorInterest * priceInUsd / (10 ** reserve.reserve.decimals);
310
311 const flashloanLPPremium = parseFloat(reserve.lifetimeFlashLoanLPPremium)
312 - parseFloat(startReserve.lifetimeFlashLoanLPPremium);
313 const flashloanLPPremiumUSD = flashloanLPPremium * priceInUsd / (10 ** reserve.reserve.decimals);
314
315 const flashloanProtocolPremium = parseFloat(reserve.lifetimeFlashLoanProtocolPremium)
316 - parseFloat(startReserve.lifetimeFlashLoanProtocolPremium);
317 const flashloanProtocolPremiumUSD = flashloanProtocolPremium * priceInUsd / (10 ** reserve.reserve.decimals);
318
319 const portalLPFee = parseFloat(reserve.lifetimePortalLPFee)
320 - parseFloat(startReserve?.lifetimePortalLPFee);
321 const portalLPFeeUSD = portalLPFee * priceInUsd / (10 ** reserve.reserve.decimals);
322
323 const portalProtocolFee = parseFloat(reserve.lifetimePortalProtocolFee)
324 - parseFloat(startReserve?.lifetimePortalProtocolFee);
325 const portalProtocolFeeUSD = portalProtocolFee * priceInUsd / (10 ** reserve.reserve.decimals);
326
327 const treasuryIncome = parseFloat(reserve.lifetimeReserveFactorAccrued) - parseFloat(startReserve?.lifetimeReserveFactorAccrued);
328
329 const outstandingTreasuryIncome = parseFloat(reserve.accruedToTreasury) - parseFloat(startReserve?.accruedToTreasury);
330
331 const treasuryIncomeUSD = treasuryIncome * priceInUsd / (10 ** reserve.reserve.decimals);
332
333 const outstandingTreasuryIncomeUSD = outstandingTreasuryIncome * priceInUsd / (10 ** reserve.reserve.decimals);
334
335 acc.outstandingTreasuryIncomeUSD += outstandingTreasuryIncomeUSD;
336 acc.treasuryIncomeUSD += treasuryIncomeUSD;
337 acc.depositorInterestUSD += depositorInterestUSD;
338 acc.flashloanLPPremiumUSD += flashloanLPPremiumUSD;
339 acc.flashloanProtocolPremiumUSD += flashloanProtocolPremiumUSD;
340 acc.portalLPFeeUSD += portalLPFeeUSD;
341 acc.portalProtocolFeeUSD += portalProtocolFeeUSD;
342 return acc;
343 }, {
344 depositorInterestUSD: 0,
345 flashloanLPPremiumUSD: 0,
346 flashloanProtocolPremiumUSD: 0,
347 portalLPFeeUSD: 0,
348 portalProtocolFeeUSD: 0,
349 treasuryIncomeUSD: 0,
350 outstandingTreasuryIncomeUSD: 0
351 });
352 }
353
354 const getV3Fees = (
355 poolId: string,
356 subgraph: string,
357 ) => async (date: string): Promise<number> => {
358 const startTimestamp = sdk.date.dateToTimestamp(date);
359 const nextDayTimestamp = sdk.date.dateToTimestamp(sdk.date.offsetDaysFormatted(date, 1));
360
361 const startReserves = await getV3ReservesSnapshot(poolId, startTimestamp, subgraph);
362 const endReserves = await getV3ReservesSnapshot(poolId, nextDayTimestamp, subgraph);
363
364 const breakdown = calculateV3FeeBreakdown(startReserves, endReserves, true);
365
366 return breakdown.depositorInterestUSD + breakdown.outstandingTreasuryIncomeUSD + breakdown.treasuryIncomeUSD;
367 }
368
369 const getV3FeeBreakdown = (
370 poolId: string,
371 subgraph: string,
372 ) => async (date: string): Promise<any> => {
373 const startTimestamp = sdk.date.dateToTimestamp(date);
374 const nextDayTimestamp = sdk.date.dateToTimestamp(sdk.date.offsetDaysFormatted(date, 1));
375
376 const startReserves = await getV3ReservesSnapshot(poolId, startTimestamp, subgraph);
377 const endReserves = await getV3ReservesSnapshot(poolId, nextDayTimestamp, subgraph);
378
379 return calculateV3FeeBreakdown(startReserves, endReserves, true);
380 }
381
382 const metadata = {
383 name: 'Aave',
384 icon: sdk.ipfs.getDataURILoader('QmW4X8Q36jjPm8fzU21NzFKRxNzReQy4JnehKbRrgybFh6', 'image/svg+xml'),
385 category: 'lending',
386 description:
387 'Aave is an open source and non-custodial liquidity protocol for earning interest on deposits and borrowing assets.',
388 feeDescription:
389 'Fees are accrued from the dynamics of providing liquidity and borrowing, going to liquidity suppliers and the Aave DAO treasury.',
390 blockchain: 'Ethereum',
391 source: 'The Graph Protocol',
392 website: 'https://aave.com',
393 tokenTicker: 'AAVE',
394 tokenCoingecko: 'aave',
395 tokenLaunch: '2020-09-14', // TODO: add real token launch data
396 protocolLaunch: '2020-01-08',
397 };
398
399 sdk.registerBundle('aave', metadata);
400
401 sdk.register({
402 id: 'aave-v1',
403 bundle: 'aave',
404 queries: {
405 oneDayTotalFees: getV1Fees,
406 },
407 metadata: {
408 ...metadata,
409 subtitle: 'Version 1',
410 protocolLaunch: '2020-01-08',
411 },
412 });
413
414 sdk.register({
415 id: 'aave-v2',
416 bundle: 'aave',
417 queries: {
418 oneDayTotalFees: getV2Fees(POOL_IDS.V2, SUBGRAPHS.V2),
419 },
420 metadata: {
421 ...metadata,
422 subtitle: 'Version 2',
423 protocolLaunch: '2020-12-03',
424 },
425 });
426
427 sdk.register({
428 id: 'aave-v2-amm',
429 bundle: 'aave',
430 queries: {
431 oneDayTotalFees: getV2Fees(POOL_IDS.V2_AMM, SUBGRAPHS.V2),
432 },
433 metadata: {
434 ...metadata,
435 subtitle: 'Version 2 - AMM',
436 protocolLaunch: '2021-03-08',
437 },
438 });
439
440 sdk.register({
441 id: 'aave-v2-polygon-proto',
442 bundle: 'aave',
443 queries: {
444 oneDayTotalFees: getV2Fees(POOL_IDS.V2_POLYGON, SUBGRAPHS.POLYGON),
445 },
446 metadata: {
447 ...metadata,
448 subtitle: 'Version 2 - Polygon',
449 protocolLaunch: '2021-03-31',
450 blockchain: 'Polygon',
451 },
452 });
453
454 sdk.register({
455 id: 'aave-v3-polygon-proto',
456 bundle: 'aave',
457 queries: {
458 oneDayTotalFees: getV3Fees(POOL_IDS.V3, SUBGRAPHS.POLYGON_V3),
459 oneDayFeeBreakdown: getV3FeeBreakdown(POOL_IDS.V3, SUBGRAPHS.POLYGON_V3)
460 },
461 metadata: {
462 ...metadata,
463 subtitle: 'Version 3 - Polygon',
464 protocolLaunch: '2022-03-14',
465 blockchain: 'Polygon',
466 },
467 });
468
469 sdk.register({
470 id: 'aave-v2-avalanche-proto',
471 bundle: 'aave',
472 queries: {
473 oneDayTotalFees: getV2Fees(POOL_IDS.V2_AVALANCHE, SUBGRAPHS.AVALANCHE, true),
474 },
475 metadata: {
476 ...metadata,
477 subtitle: 'Version 2 - Avalanche',
478 protocolLaunch: '2021-10-04',
479 blockchain: 'Avalanche',
480 },
481 });
482
483 sdk.register({
484 id: 'aave-v3-avalanche-proto',
485 bundle: 'aave',
486 queries: {
487 oneDayTotalFees: getV3Fees(POOL_IDS.V3, SUBGRAPHS.AVALANCHE_V3),
488 oneDayFeeBreakdown: getV3FeeBreakdown(POOL_IDS.V3, SUBGRAPHS.AVALANCHE_V3)
489 },
490 metadata: {
491 ...metadata,
492 subtitle: 'Version 3 - Avalanche',
493 protocolLaunch: '2022-03-14',
494 blockchain: 'Avalanche',
495 },
496 });
497
498 sdk.register({
499 id: 'aave-v3-optimism-proto',
500 bundle: 'aave',
501 queries: {
502 oneDayTotalFees: getV3Fees(POOL_IDS.V3, SUBGRAPHS.OPTIMISM_V3),
503 oneDayFeeBreakdown: getV3FeeBreakdown(POOL_IDS.V3, SUBGRAPHS.OPTIMISM_V3)
504 },
505 metadata: {
506 ...metadata,
507 subtitle: 'Version 3 - Optimism',
508 protocolLaunch: '2022-03-14',
509 blockchain: 'Optimism',
510 },
511 });
512
513 sdk.register({
514 id: 'aave-v3-arbitrum-proto',
515 bundle: 'aave',
516 queries: {
517 oneDayTotalFees: getV3Fees(POOL_IDS.V3, SUBGRAPHS.ARBITRUM_V3),
518 oneDayFeeBreakdown: getV3FeeBreakdown(POOL_IDS.V3, SUBGRAPHS.ARBITRUM_V3)
519 },
520 metadata: {
521 ...metadata,
522 subtitle: 'Version 3 - Arbitrum',
523 protocolLaunch: '2022-03-14',
524 blockchain: 'Arbitrum One',
525 },
526 });
527
528 sdk.register({
529 id: 'aave-v3-fantom-proto',
530 bundle: 'aave',
531 queries: {
532 oneDayTotalFees: getV3Fees(POOL_IDS.V3, SUBGRAPHS.FANTOM_V3),
533 oneDayFeeBreakdown: getV3FeeBreakdown(POOL_IDS.V3, SUBGRAPHS.FANTOM_V3)
534 },
535 metadata: {
536 ...metadata,
537 subtitle: 'Version 3 - Fantom',
538 protocolLaunch: '2022-03-14',
539 blockchain: 'Fantom',
540 },
541 });
542
543 sdk.register({
544 id: 'aave-v3-harmony-proto',
545 bundle: 'aave',
546 queries: {
547 oneDayTotalFees: getV3Fees(POOL_IDS.V3, SUBGRAPHS.HARMONY_V3),
548 oneDayFeeBreakdown: getV3FeeBreakdown(POOL_IDS.V3, SUBGRAPHS.HARMONY_V3)
549 },
550 metadata: {
551 ...metadata,
552 subtitle: 'Version 3 - Harmony',
553 protocolLaunch: '2022-03-14',
554 blockchain: 'Harmony',
555 },
556 });
557}
558
It's something off?
Report it to the discussion board on Discord, we will take care of it.