From 9ae80b86a8fc5710288d06e46477e9fedd00935c Mon Sep 17 00:00:00 2001 From: RM Date: Wed, 18 Sep 2024 17:13:01 +0800 Subject: [PATCH] Add Mellow --- src/api/stats/base/index.js | 3 ++ src/api/stats/common/getMellowVeloApys.js | 28 +++++++++++ src/api/stats/common/getMellowVeloPrices.js | 53 +++++++++++++++++++++ src/api/stats/getNonAmmPrices.ts | 5 ++ src/api/stats/optimism/index.js | 3 ++ src/data/base/mellowAeroPools.json | 32 +++++++++++++ src/data/optimism/mellowVeloPools.json | 32 +++++++++++++ 7 files changed, 156 insertions(+) create mode 100644 src/api/stats/common/getMellowVeloApys.js create mode 100644 src/api/stats/common/getMellowVeloPrices.js create mode 100644 src/data/base/mellowAeroPools.json create mode 100644 src/data/optimism/mellowVeloPools.json diff --git a/src/api/stats/base/index.js b/src/api/stats/base/index.js index 63ebb8764..925cf904e 100644 --- a/src/api/stats/base/index.js +++ b/src/api/stats/base/index.js @@ -15,6 +15,8 @@ const getAuraBaseApys = require('./getAuraBaseApys'); const { getSonneApys } = require('./getSonneApys'); const { getSeamlessApys } = require('./getSeamlessApys'); const { getBeefyBaseCowApys } = require('./getBeefyBaseCowApys'); +const { getMellowVeloApys } = require('../common/getMellowVeloApys'); +const { BASE_CHAIN_ID } = require('../../../constants'); const getApys = [ getSeamlessApys, @@ -33,6 +35,7 @@ const getApys = [ getBaseMerklGammaApys, getBaseCompoundV3Apys, getAuraBaseApys, + () => getMellowVeloApys(BASE_CHAIN_ID, require('../../../data/base/mellowAeroPools.json')), getBeefyBaseCowApys, ]; diff --git a/src/api/stats/common/getMellowVeloApys.js b/src/api/stats/common/getMellowVeloApys.js new file mode 100644 index 000000000..2ffe37990 --- /dev/null +++ b/src/api/stats/common/getMellowVeloApys.js @@ -0,0 +1,28 @@ +import BigNumber from 'bignumber.js'; +import { fetchContract } from '../../rpc/client'; +import simpleFarmABI from '../../../abis/ISimpleFarm.json'; +import { fetchPrice } from '../../../utils/fetchPrice'; +import { getApyBreakdown } from './getApyBreakdownNew'; + +export const getMellowVeloApys = async (chainId, pools) => { + const price = await fetchPrice({ oracle: 'tokens', id: 'VELO' }); + + const contracts = pools.map(p => fetchContract(p.gauge, simpleFarmABI, chainId)); + const [rewardRates, totalSupplies] = await Promise.all([ + Promise.all(contracts.map(c => c.read.rewardRate().then(v => new BigNumber(v)))), + Promise.all(contracts.map(c => c.read.totalSupply().then(v => new BigNumber(v)))), + ]); + + const apys = []; + for (let i = 0; i < pools.length; i++) { + const pool = pools[i]; + const lpPrice = await fetchPrice({ oracle: 'lps', id: pool.name }); + const totalStakedInUsd = totalSupplies[i].times(lpPrice).div('1e18'); + const rewardsInUsd = rewardRates[i].times(31536000).times(price).div('1e18'); + const apy = rewardsInUsd.div(totalStakedInUsd); + apys.push(apy); + // console.log(pool.name, 'apy', apy.toString(10)) + } + + return getApyBreakdown(pools.map((p, i) => ({ vaultId: p.name, vault: apys[i] }))); +}; diff --git a/src/api/stats/common/getMellowVeloPrices.js b/src/api/stats/common/getMellowVeloPrices.js new file mode 100644 index 000000000..fa285e554 --- /dev/null +++ b/src/api/stats/common/getMellowVeloPrices.js @@ -0,0 +1,53 @@ +import BigNumber from 'bignumber.js'; +import { fetchContract } from '../../rpc/client'; +import { BASE_CHAIN_ID, OPTIMISM_CHAIN_ID } from '../../../constants'; +import { parseAbi } from 'viem'; +import { addressBookByChainId } from '../../../../packages/address-book/src/address-book'; + +const helpers = { + [OPTIMISM_CHAIN_ID]: '0xF32cc62a8D35CAFA6FCFF475A939d046fFDf24a6', + [BASE_CHAIN_ID]: '0x3f812916E5C050305D8b4744f0254DE3e195d5E5', +}; +const abi = parseAbi(['function getData(address lpWrapper) external view returns (uint, uint, uint)']); + +export const getMellowVeloPrices = async (chainId, pools, tokenPrices) => { + let prices = {}; + + const res = await Promise.all( + pools.map(p => fetchContract(helpers[chainId], abi, chainId).read.getData([p.address])) + ); + + pools.forEach((pool, i) => { + const t0 = addressBookByChainId[chainId].tokens[pool.tokens[0]]; + const t1 = addressBookByChainId[chainId].tokens[pool.tokens[1]]; + + const lp0Bal = new BigNumber(res[i][0]).div(`1e${t0.decimals}`); + const lp1Bal = new BigNumber(res[i][1]).div(`1e${t1.decimals}`); + const lp0Price = getTokenPrice(tokenPrices, t0.oracleId); + const lp1Price = getTokenPrice(tokenPrices, t1.oracleId); + const totalUsd = lp0Bal.times(lp0Price).plus(lp1Bal.times(lp1Price)); + + const totalSupply = new BigNumber(res[i][2]).div('1e18'); + const price = totalUsd.div(totalSupply).toNumber(); + // console.log(pool.name, 'tvl', totalUsd.toString(10)); + + prices[pool.name] = { + price, + tokens: [t0.address, t1.address], + balances: [lp0Bal.toString(10), lp1Bal.toString(10)], + totalSupply: totalSupply.toString(10), + }; + }); + + return prices; +}; + +const getTokenPrice = (tokenPrices, token) => { + let tokenPrice = 1; + if (tokenPrices.hasOwnProperty(token)) { + tokenPrice = tokenPrices[token]; + } else { + console.error(`MellowVelo unknown token '${token}'. Consider adding it to .json file`); + } + return tokenPrice; +}; diff --git a/src/api/stats/getNonAmmPrices.ts b/src/api/stats/getNonAmmPrices.ts index f7572efbc..76183b4fd 100644 --- a/src/api/stats/getNonAmmPrices.ts +++ b/src/api/stats/getNonAmmPrices.ts @@ -78,9 +78,11 @@ import getGammaMoonbeamPrices from './moonbeam/getGammaMoonbeamPrices'; import getVelodromeModeStablePrices from './mode/getVelodromeModeStablePrices'; import { ARBITRUM_CHAIN_ID as ARB_CHAIN_ID, + BASE_CHAIN_ID, ETH_CHAIN_ID, FRAXTAL_CHAIN_ID as FRX_CHAIN_ID, GNOSIS_CHAIN_ID as GNO_CHAIN_ID, + OPTIMISM_CHAIN_ID, } from '../../constants'; import getEthSiloPrices from './ethereum/getEthereumSiloPrices'; import getEthRangePrices from './ethereum/getEthRangePrices'; @@ -102,6 +104,7 @@ import { getBeefyCowMantaPrices } from './manta/getBeefyMantaCowPrices'; import { getBeefyCowMantlePrices } from './mantle/getBeefyMantleCowPrices'; import { getBeefyCowSeiPrices } from './sei/getBeefySeiCowPrices'; import { getBeefyCowBscPrices } from './bsc/getBeefyCowBscPrices'; +import { getMellowVeloPrices } from './common/getMellowVeloPrices'; export type NonAmmPrices = { prices: Record; @@ -221,6 +224,8 @@ export async function getNonAmmPrices(tokenPrices: Record): Prom getBeefyCowSeiPrices(tokenPrices), getBeefyCowBscPrices(tokenPrices), getFtmIchiPrices(tokenPrices), + getMellowVeloPrices(OPTIMISM_CHAIN_ID, require('../../data/optimism/mellowVeloPools.json'), tokenPrices), + getMellowVeloPrices(BASE_CHAIN_ID, require('../../data/base/mellowAeroPools.json'), tokenPrices), getPearlTridentPrices(tokenPrices), ]; diff --git a/src/api/stats/optimism/index.js b/src/api/stats/optimism/index.js index 5e9f83545..bb939e1da 100644 --- a/src/api/stats/optimism/index.js +++ b/src/api/stats/optimism/index.js @@ -19,6 +19,8 @@ const getUniswapGammaApys = require('./getUniswapGammaApys'); const { getSonneApys } = require('./getSonneApys'); const { getBeefyOPCowApys } = require('./getBeefyOPCowApys'); const { getOpSiloApys } = require('./getOpSiloApys'); +const { getMellowVeloApys } = require('../common/getMellowVeloApys'); +const { OPTIMISM_CHAIN_ID } = require('../../../constants'); const getApys = [ getSonneApys, @@ -41,6 +43,7 @@ const getApys = [ getBeOpxEarnApy, getBeVeloV2Apr, getBeefyOPCowApys, + () => getMellowVeloApys(OPTIMISM_CHAIN_ID, require('../../../data/optimism/mellowVeloPools.json')), getOpSiloApys, ]; diff --git a/src/data/base/mellowAeroPools.json b/src/data/base/mellowAeroPools.json new file mode 100644 index 000000000..c3045f545 --- /dev/null +++ b/src/data/base/mellowAeroPools.json @@ -0,0 +1,32 @@ +[ + { + "name": "mellow-aero-weth-cbbtc", + "address": "0xbdB39E6A52A4d91a96E79c7C2Dcbd8915cE99c14", + "gauge": "0xB17D90FF52077811304D24Ba8CE969425815B163", + "tokens": ["WETH", "cbBTC"] + }, + { + "name": "mellow-aero-weth-usdc", + "address": "0x731f2CDF517b9f8702cb4C3200bB2Bd8ECD3C7a7", + "gauge": "0xd4305877ab5DeDFC939Fa209812C828343a23F83", + "tokens": ["WETH", "USDC"] + }, + { + "name": "mellow-aero-eurc-usdc", + "address": "0xe9f4Eb4b4c884204a3383fC64481e157BBA882AA", + "gauge": "0xD9EF9874D58719E4f256d96eDD2DcE19F9C3D3d9", + "tokens": ["EURC", "USDC"] + }, + { + "name": "mellow-aero-cbeth-weth", + "address": "0x53017a5B2c56583e184577578d55aB098b40323D", + "gauge": "0x8Bd5E3141A134d3D26b458293Ad1c9d56d66Fb8A", + "tokens": ["cbETH", "WETH"] + }, + { + "name": "mellow-aero-usdz-usdc", + "address": "0x6E4B3093C38b4a422fA6F4617EAB4fA93283A73D", + "gauge": "0xF5d8609c202E84F665679c3625f6dc6B27B70799", + "tokens": ["USDz", "USDC"] + } +] diff --git a/src/data/optimism/mellowVeloPools.json b/src/data/optimism/mellowVeloPools.json new file mode 100644 index 000000000..979c9f38e --- /dev/null +++ b/src/data/optimism/mellowVeloPools.json @@ -0,0 +1,32 @@ +[ + { + "name": "mellow-velo-usdc-weth", + "address": "0x7243936FBA26190299D38eCB7069C7A539F8cA3a", + "gauge": "0x739C53D4434Db94e782D82e310e50f9A655cf525", + "tokens": ["USDC", "WETH"] + }, + { + "name": "mellow-velo-weth-op", + "address": "0x9b0c181987fb01081258E1863D39A2BE55aaFE3A", + "gauge": "0x4E90232c38ed817D5f03a3DCF13a43D5d015a1cE", + "tokens": ["WETH", "OP"] + }, + { + "name": "mellow-velo-usdc-usdt", + "address": "0x65Cd4f08709D1d1C1eBCbe91073C390afABC3144", + "gauge": "0xB55da32e47914be05348547367bde9f95CE2dAB9", + "tokens": ["USDC", "USDT"] + }, + { + "name": "mellow-velo-usdc-susd", + "address": "0x9128eCebafb428f52aF9dd863d1bbB36a677FB59", + "gauge": "0x59AA940f2A7135Ca824310AC107F30C829738fE0", + "tokens": ["USDC", "sUSD"] + }, + { + "name": "mellow-velo-usdc-usdc.e", + "address": "0x67fE54E339949346e99bC65aE4C425abAB7EF502", + "gauge": "0x7DB963A6EDc02473dBc9F4acDBF0D58a455d58bE", + "tokens": ["USDC", "opUSDCe"] + } +]