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.