From 5cf2e3ff14e10abdcdee2294733bbf4f20fc9616 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Fri, 8 Sep 2023 20:23:56 -0300 Subject: [PATCH 1/4] Adding new tokens; Adding 2 new apr handlers, bloom and maker; --- modules/network/apr-config-types.ts | 22 +++++++++++ modules/network/arbitrum.ts | 34 ++++++++++++++++ modules/network/avalanche.ts | 19 +++++++++ modules/network/mainnet.ts | 24 +++++++++++- modules/network/polygon.ts | 12 ++++++ modules/network/zkevm.ts | 6 +++ .../ib-linear-apr-handlers.ts | 10 +++++ .../sources/abis/bloom-bps-feed.ts | 11 ++++++ .../sources/abis/maker-pot.ts | 9 +++++ .../sources/bloom-apr-handler.ts | 34 ++++++++++++++++ .../sources/maker-apr-handler.ts | 39 +++++++++++++++++++ 11 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/bloom-bps-feed.ts create mode 100644 modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/maker-pot.ts create mode 100644 modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts create mode 100644 modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts diff --git a/modules/network/apr-config-types.ts b/modules/network/apr-config-types.ts index 3b344bf80..d4bdb5a37 100644 --- a/modules/network/apr-config-types.ts +++ b/modules/network/apr-config-types.ts @@ -1,10 +1,12 @@ export interface IbAprConfig { aave?: AaveAprConfig; ankr?: AnkrAprConfig; + bloom?: BloomAprConfig; beefy?: BeefyAprConfig; euler?: EulerAprConfig; gearbox?: GearBoxAprConfig; idle?: IdleAprConfig; + maker?: MakerAprConfig; ovix?: OvixAprConfig; reaper?: ReaperAprConfig; tessera?: TesseraAprConfig; @@ -55,6 +57,16 @@ export interface BeefyAprConfig { }; } +export interface BloomAprConfig { + tokens: { + [tokenName: string]: { + address: string; + feedAddress: string; + isIbYield?: boolean; + }; + }; +} + export interface EulerAprConfig { subgraphUrl: string; tokens: { @@ -87,6 +99,16 @@ export interface IdleAprConfig { }; } +export interface MakerAprConfig { + tokens: { + [tokenName: string]: { + address: string; + potAddress: string; + isIbYield?: boolean; + }; + }; +} + export interface OvixAprConfig { tokens: { [tokenName: string]: { diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 23269b661..5795310a4 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -143,6 +143,22 @@ const arbitrumNetworkData: NetworkData = { }, }, }, + reaper: { + onchainSource: { + averageAPRAcrossLastNHarvests: 5, + tokens: { + rfGrainDAI: { + address: '0x12f256109e744081f633a827be80e06d97ff7447', + }, + rfGrainUSDT: { + address: '0x0179bac7493a92ac812730a4c64a0b41b7ea0ecf', + }, + rfGrainUSDC: { + address: '0xaeacf641a0342330ec681b57c0a6af0b71d5cbff', + }, + }, + }, + }, defaultHandlers: { wstETH: { tokenAddress: '0x5979d7b546e38e414f7e9822514be443a4800529', @@ -150,6 +166,24 @@ const arbitrumNetworkData: NetworkData = { path: 'data.smaApr', isIbYield: true, }, + rETH: { + tokenAddress: '0xec70dcb4a1efa46b8f2d97c310c9c4790ba5ffa8', + sourceUrl: 'https://rocketpool.net/api/mainnet/payload', + path: 'rethAPR', + isIbYield: true, + }, + cbETH: { + tokenAddress: '0x1debd73e752beaf79865fd6446b0c970eae7732f', + sourceUrl: 'https://api.exchange.coinbase.com/wrapped-assets/CBETH/', + path: 'apy', + scale: 1, + }, + sfrxETH: { + tokenAddress: '0x95ab45875cffdba1e5f451b950bc2e42c0053f39', + sourceUrl: 'https://api.frax.finance/v2/frxeth/summary/latest', + path: 'sfrxethApr', + isIbYield: true, + }, }, }, beefy: { diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index c26218821..601fd1cbd 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -158,6 +158,25 @@ const avalancheNetworkData: NetworkData = { }, }, }, + defaultHandlers: { + sAVAX: { + tokenAddress: '0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be', + sourceUrl: 'https://api.benqi.fi/liquidstaking/apr', + path: 'apr', + scale: 1, + }, + yyAVAX: { + tokenAddress: '0xf7d9281e8e363584973f946201b82ba72c965d27', + sourceUrl: 'https://staging-api.yieldyak.com/yyavax', + path: 'yyAVAX.apr', + }, + ggAVAX: { + tokenAddress: '0xa25eaf2906fa1a3a13edac9b9657108af7b703e3', + sourceUrl: 'https://ceres.gogopool.com', + path: 'ggAVAXMonthlyInterestMonth.value', + scale: -0.8333, + }, + }, }, beefy: { linearPools: [''], diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index ef4de0d7f..f596fcc6d 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -163,7 +163,7 @@ export const mainnetNetworkData: NetworkData = { aTokenAddress: '0x018008bfb33d285247a21d44e50697654f754e63', wrappedTokens: { waDAI: '0x098256c06ab24f5655c5506a6488781bd711c14b', - stataEthDAI: '0x098256c06ab24f5655c5506a6488781bd711c14b', + stataEthDAI: '0xeb708639e8e518b86a916db3685f90216b1c1c67', }, }, wETH: { @@ -187,6 +187,14 @@ export const mainnetNetworkData: NetworkData = { }, }, }, + bloom: { + tokens: { + tbyFeb1924: { + address: '0xc4cafefbc3dfea629c589728d648cb6111db3136', + feedAddress: '0xde1f5f2d69339171d679fb84e4562febb71f36e6', + }, + }, + }, euler: { subgraphUrl: 'https://api.thegraph.com/subgraphs/name/euler-xyz/euler-mainnet', tokens: { @@ -222,6 +230,14 @@ export const mainnetNetworkData: NetworkData = { }, }, }, + maker: { + tokens: { + sDAI: { + address: '0x83f20f44975d03b1b09e64809b757c47f942beea', + potAddress: '0x197e90f9fad81970ba7976f33cbd77088e5d7cf7', + }, + }, + }, tessera: { tokens: { sAPE: { @@ -299,6 +315,12 @@ export const mainnetNetworkData: NetworkData = { path: 'wjauraApy', isIbYield: true, }, + ETHx: { + tokenAddress: '0x0f7f961648ae6db43c75663ac7e5414eb79b5704', + sourceUrl: 'https://universe.staderlabs.com/eth/apy', + path: 'value', + isIbYield: true, + }, }, }, beefy: { diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index 3646e3e27..e43f16e17 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -204,6 +204,18 @@ const polygonNetworkData: NetworkData = { path: 'value', isIbYield: true, }, + overnightUSDPlus: { + tokenAddress: '0x5d9d8509c522a47d9285b9e4e9ec686e6a580850', + sourceUrl: 'https://api.overnight.fi/optimism/usd+/fin-data/avg-apr/week', + path: 'value', + group: 'OVERNIGHT', + }, + overnightstUSDPlus: { + tokenAddress: '0x5a5c6aa6164750b530b8f7658b827163b3549a4d', + sourceUrl: 'https://api.overnight.fi/optimism/usd+/fin-data/avg-apr/week', + path: 'value', + group: 'OVERNIGHT', + }, wbETH: { tokenAddress: '0xa2e3356610840701bdf5611a53974510ae27e2e1', sourceUrl: diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index 9ab16362a..0e7367c87 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -119,6 +119,12 @@ const zkevmNetworkData: NetworkData = { path: 'data.smaApr', isIbYield: true, }, + rETH: { + tokenAddress: '0xb23c20efce6e24acca0cef9b7b7aa196b84ec942', + sourceUrl: 'https://rocketpool.net/api/mainnet/payload', + path: 'rethAPR', + isIbYield: true, + }, }, }, beefy: { diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts index 0bf9305eb..82821f414 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts +++ b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts @@ -12,6 +12,8 @@ import { YearnAprHandler } from './sources/yearn-apr-handler'; import { ReaperCryptAprHandler } from './sources/reaper-crypt-apr-handler'; import { BeefyAprHandler } from './sources/beefy-apr-handler'; import { IbAprConfig } from '../../../../network/apr-config-types'; +import { MakerAprHandler } from './sources/maker-apr-handler'; +import { BloomAprHandler } from './sources/bloom-apr-handler'; export class IbLinearAprHandlers { private handlers: AprHandler[] = []; @@ -38,6 +40,10 @@ export class IbLinearAprHandlers { const beefyHandler = new BeefyAprHandler(aprConfig.beefy); handlers.push(beefyHandler); } + if (aprConfig.bloom) { + const bloomAprHandler = new BloomAprHandler(aprConfig.bloom); + handlers.push(bloomAprHandler); + } if (aprConfig.euler) { const eulerHandler = new EulerAprHandler(aprConfig.euler); handlers.push(eulerHandler); @@ -50,6 +56,10 @@ export class IbLinearAprHandlers { const idleHandler = new IdleAprHandler(aprConfig.idle); handlers.push(idleHandler); } + if (aprConfig.maker) { + const makerHandler = new MakerAprHandler(aprConfig.maker); + handlers.push(makerHandler); + } if (aprConfig.ovix) { const ovixHandler = new OvixAprHandler({ ...aprConfig.ovix, diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/bloom-bps-feed.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/bloom-bps-feed.ts new file mode 100644 index 000000000..6298bb929 --- /dev/null +++ b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/bloom-bps-feed.ts @@ -0,0 +1,11 @@ +export const abi = [{ + "inputs":[], + "name":"currentRate", + "outputs":[{ + "internalType":"uint256", + "name":"", + "type":"uint256" + }], + "stateMutability":"view", + "type":"function" +}] as const \ No newline at end of file diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/maker-pot.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/maker-pot.ts new file mode 100644 index 000000000..bc2b3a0cf --- /dev/null +++ b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/maker-pot.ts @@ -0,0 +1,9 @@ +export const abi = [{ + constant: true, + inputs: [], + name: "dsr", + outputs: [{ "internalType": "uint256", "name": "", "type": "uint256" }], + payable: false, + stateMutability: "view", + type: "function" +}] as const \ No newline at end of file diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts new file mode 100644 index 000000000..fed2cc55c --- /dev/null +++ b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts @@ -0,0 +1,34 @@ +import { AprHandler } from '../ib-linear-apr-handlers'; +import { BloomAprConfig } from '../../../../../network/apr-config-types'; +import { getContractAt } from '../../../../../web3/contract'; +import { abi as bloomBpsFeed } from './abis/bloom-bps-feed'; +import * as Sentry from '@sentry/node'; + +export class BloomAprHandler implements AprHandler { + group: string | undefined; + + tokens: BloomAprConfig['tokens']; + + constructor(aprConfig: BloomAprConfig) { + this.tokens = aprConfig.tokens; + } + + async getAprs(): Promise<{ [p: string]: { apr: number; isIbYield: boolean } }> { + const aprs: { [p: string]: { apr: number; isIbYield: boolean } } = {}; + for (const { address, feedAddress, isIbYield } of Object.values(this.tokens)) { + try { + const feedContract = getContractAt(feedAddress, bloomBpsFeed); + const currentRate = await feedContract.currentRate(); + if (!currentRate) { + continue; + } + const tokenApr = (Number(currentRate) - 10000) / 10000; + aprs[address] = { apr: tokenApr, isIbYield: isIbYield ?? false }; + } catch (error) { + console.error(`Bloom APR Failed for token ${address}: `, error); + Sentry.captureException(`Bloom APR Failed for token ${address}: ${error}`); + } + } + return aprs; + } +} diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts new file mode 100644 index 000000000..20220f187 --- /dev/null +++ b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts @@ -0,0 +1,39 @@ +import { AprHandler } from '../ib-linear-apr-handlers'; +import { MakerAprConfig } from '../../../../../network/apr-config-types'; +import { getContractAt } from '../../../../../web3/contract'; +import { abi as makerPotAbi } from './abis/maker-pot'; +import * as Sentry from '@sentry/node'; + +export class MakerAprHandler implements AprHandler { + group: string | undefined; + tokens: { + [tokenName: string]: { + address: string; + potAddress: string; + isIbYield?: boolean; + }; + }; + + constructor(aprConfig: MakerAprConfig) { + this.tokens = aprConfig.tokens; + } + + async getAprs(): Promise<{ [p: string]: { apr: number; isIbYield: boolean } }> { + const aprs: { [p: string]: { apr: number; isIbYield: boolean } } = {}; + for (const { address, potAddress, isIbYield } of Object.values(this.tokens)) { + try { + const potContract = getContractAt(potAddress, makerPotAbi); + const dsr = await potContract.dsr(); + if (!dsr) { + continue; + } + const tokenApr = (Number(dsr) * 10 ** -27 - 1) * 365 * 24 * 60 * 60; + aprs[address] = { apr: tokenApr, isIbYield: isIbYield ?? false }; + } catch (error) { + console.error(`Maker APR Failed for token ${address}: `, error); + Sentry.captureException(`Maker APR Failed for token ${address}: ${error}`); + } + } + return aprs; + } +} From ef1f0240c6d2c4931f42eb5e79b41afdf3c11c20 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 11 Sep 2023 21:23:17 -0300 Subject: [PATCH 2/4] fixing wrong scale; --- modules/network/avalanche.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 601fd1cbd..0afc2a398 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -174,7 +174,7 @@ const avalancheNetworkData: NetworkData = { tokenAddress: '0xa25eaf2906fa1a3a13edac9b9657108af7b703e3', sourceUrl: 'https://ceres.gogopool.com', path: 'ggAVAXMonthlyInterestMonth.value', - scale: -0.8333, + scale: -8.3333, }, }, }, From 12dd31d3fd142e48d094eb0432ca63aa2d23f0d6 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 12 Sep 2023 10:20:26 -0300 Subject: [PATCH 3/4] Adding comment explaining the scale of the ggAVAX APR; --- modules/network/avalanche.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 0afc2a398..3757ec59f 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -174,6 +174,9 @@ const avalancheNetworkData: NetworkData = { tokenAddress: '0xa25eaf2906fa1a3a13edac9b9657108af7b703e3', sourceUrl: 'https://ceres.gogopool.com', path: 'ggAVAXMonthlyInterestMonth.value', + // According to solarcurve, the AVAX Monthly Interest must be multiplied by -12 to represent the APR in normal scale, for example, if the monthly interest is -0,15, the APR would be -0,15 * -12 = 1,8%. + // How this -12 became -8,333? It's because the scale parameter is used to divide the number, and the final apr percentage is in decimal format (1,8% = 0,018), so if: + // M * -12 = A (M is monthly rate and A is APR) => (M/x) = (A/100) => (A / -12x) = (A / 100) [replacing M by A/-12] => x = 100/-12 = -8,33333 scale: -8.3333, }, }, From 014f98cc4a96be3f86f7fbc585cda5e8614e8680 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 12 Sep 2023 10:24:15 -0300 Subject: [PATCH 4/4] Adding one more line of comment with solarcurve explanation; --- modules/network/avalanche.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 3757ec59f..8091b1f52 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -175,6 +175,7 @@ const avalancheNetworkData: NetworkData = { sourceUrl: 'https://ceres.gogopool.com', path: 'ggAVAXMonthlyInterestMonth.value', // According to solarcurve, the AVAX Monthly Interest must be multiplied by -12 to represent the APR in normal scale, for example, if the monthly interest is -0,15, the APR would be -0,15 * -12 = 1,8%. + // @solarcurve: We estimate by multiplying that value by -12 since its the exchange rate of AVAX -> ggAVAX, which will always return less ggAVAX than AVAX // How this -12 became -8,333? It's because the scale parameter is used to divide the number, and the final apr percentage is in decimal format (1,8% = 0,018), so if: // M * -12 = A (M is monthly rate and A is APR) => (M/x) = (A/100) => (A / -12x) = (A / 100) [replacing M by A/-12] => x = 100/-12 = -8,33333 scale: -8.3333,