diff --git a/.gitignore b/.gitignore index 6595e5c85e..1b2ffff9e7 100755 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ src/adaptors/list.js Untitled.ipynb .ipynb_checkpoints/ yarn-error.log -test.sql \ No newline at end of file +test.sql +yarn.lock \ No newline at end of file diff --git a/src/adaptors/abracadabra/abis/MultiRewardsStaking.json b/src/adaptors/abracadabra/abis/MultiRewardsStaking.json new file mode 100644 index 0000000000..ac9dea855f --- /dev/null +++ b/src/adaptors/abracadabra/abis/MultiRewardsStaking.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrInvalidTokenAddress","type":"error"},{"inputs":[],"name":"ErrRewardAlreadyAdded","type":"error"},{"inputs":[],"name":"ErrRewardPeriodStillActive","type":"error"},{"inputs":[],"name":"ErrZeroAmount","type":"error"},{"inputs":[],"name":"ErrZeroDuration","type":"error"},{"inputs":[],"name":"NotAllowedOperator","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"LogRewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"LogRewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LogRewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"bool","name":"","type":"bool"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rewardData","outputs":[{"components":[{"internalType":"uint256","name":"rewardsDuration","type":"uint256"},{"internalType":"uint256","name":"periodFinish","type":"uint256"},{"internalType":"uint256","name":"rewardRate","type":"uint256"},{"internalType":"uint256","name":"lastUpdateTime","type":"uint256"},{"internalType":"uint256","name":"rewardPerTokenStored","type":"uint256"}],"internalType":"struct MultiRewardsStaking.Reward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/src/adaptors/abracadabra/cauldrons.js b/src/adaptors/abracadabra/cauldrons.js index 200a1d77fd..395eef36fb 100644 --- a/src/adaptors/abracadabra/cauldrons.js +++ b/src/adaptors/abracadabra/cauldrons.js @@ -25,6 +25,30 @@ const POOLS = { address: '0x726413d7402ff180609d0ebc79506df8633701b1', collateralPoolId: 'a4bcffaa-3b75-436c-b6c2-7b1c3840d041' }, // magicGLP + { + version: 4, + address: '0x7962acfcfc2ccebc810045391d60040f635404fb', + collateralPoolId: '906b233c-8478-4b94-94e5-2d77e6c7c9e5', + symbol: "SOL-USDC", + }, // gmSOL + { + version: 4, + address: '0x2b02bBeAb8eCAb792d3F4DDA7a76f63Aa21934FA', + collateralPoolId: '61b4c35c-97f6-4c05-a5ff-aeb4426adf5b', + symbol: "ETH-USDC", + }, // gmETH + { + version: 4, + address: '0xD7659D913430945600dfe875434B6d80646d552A', + collateralPoolId: '5b8c0691-b9ff-4d82-97e4-19a1247e6dbf', + symbol: "WBTC.B-USDC", + }, // gmBTC + { + version: 4, + address: '0x4F9737E994da9811B8830775Fd73E2F1C8e40741', + collateralPoolId: 'f3fa942f-1867-4028-95ff-4eb76816cd07', + symbol: "ARB-USDC", + }, // gmARB ], }, avax: { @@ -221,11 +245,7 @@ const getMarketLensDetailsForCauldrons = ( const enrichMarketInfos = (cauldrons, marketInfos) => marketInfos .map((marketInfo, i) => ({ - cauldron: cauldrons[i].address, - maximumCollateralRatio: cauldrons[i].maximumCollateralRatio, - interestPerYear: cauldrons[i].interestPerYear, - cauldronMeta: cauldrons[i].cauldronMeta, - collateralPoolId: cauldrons[i].collateralPoolId, + ...cauldrons[i], ...marketInfo, })) .map((enrichedMarketInfo) => _.omitBy(enrichedMarketInfo, _.isUndefined)); @@ -551,9 +571,9 @@ const marketInfoToPool = (chain, marketInfo, collateral, pricesObj) => { const ltv = marketInfo.maximumCollateralRatio / 10000; const pool = { - pool: `${marketInfo.cauldron}-${chain}`, + pool: `${marketInfo.address}-${chain}`, chain: utils.formatChain(chain), - symbol: utils.formatSymbol(collateral.symbol), + symbol: marketInfo.symbol ?? utils.formatSymbol(collateral.symbol), tvlUsd: totalSupplyUsd, apyBaseBorrow, totalSupplyUsd, @@ -627,13 +647,13 @@ const getApy = async () => { return Object.entries(marketInfos).flatMap(([chain, chainMarketInfos]) => chainMarketInfos.map((marketInfo) => { const collateralAddress = - collaterals[chain][marketInfo.cauldron.toLowerCase()].toLowerCase(); + collaterals[chain][marketInfo.address.toLowerCase()].toLowerCase(); const bentobox = - bentoboxes[chain][marketInfo.cauldron.toLowerCase()].toLowerCase(); + bentoboxes[chain][marketInfo.address.toLowerCase()].toLowerCase(); const collateral = { address: collateralAddress, - symbol: symbols[chain][marketInfo.cauldron.toLowerCase()], - decimals: decimals[chain][marketInfo.cauldron.toLowerCase()], + symbol: symbols[chain][marketInfo.address.toLowerCase()], + decimals: decimals[chain][marketInfo.address.toLowerCase()], }; // Add negative strategy APY to collateral if there's one for the cauldron @@ -646,6 +666,11 @@ const getApy = async () => { marketInfo.collateralPoolId !== undefined ? _.find(apyObj, { pool: marketInfo.collateralPoolId }) : undefined; + if (collateralApy !== undefined) { + collateral.apyBase = collateralApy.apyBase; + } else { + collateral.apyBase = 0; + } if (strategyDetails !== undefined) { const strategy = strategyDetails.address.toLowerCase(); const targetPercentage = strategyDetails.strategyData.targetPercentage; @@ -658,21 +683,19 @@ const getApy = async () => { strategy, ]); if (negativeInterestStrategyApy !== undefined) { - collateral.apyBase = + collateral.apyBase += (targetPercentage / 100) * -negativeInterestStrategyApy; } else if ( strategyFee !== undefined && collateralApy !== undefined ) { - collateral.apyBase = - ((collateralApy.apy * targetPercentage) / 100) * + collateral.apyBase += + ((collateralApy.apyReward * targetPercentage) / 100) * (1 - strategyFee); } - } else { - // No strategy to consider, so just use the apy from the pool if one exists. - if (collateralApy) { - collateral.apyBase = collateralApy.apy; - } + } + if (collateral.apyBase === 0) { + collateral.apyBase = undefined; } return { diff --git a/src/adaptors/abracadabra/index.js b/src/adaptors/abracadabra/index.js index 9b6d2173f5..248490d589 100644 --- a/src/adaptors/abracadabra/index.js +++ b/src/adaptors/abracadabra/index.js @@ -1,12 +1,15 @@ const cauldrons = require('./cauldrons'); +const multiRewardFarms = require('./multi-reward-farms'); const farms = require('./farms'); const magicGlp = require('./magic-glp'); -const getApy = async () => [ - ...(await cauldrons()), - ...(await farms()), - ...(await magicGlp()), -]; +const getApy = async () => + [ + ...(await cauldrons()), + ...(await farms()), + ...(await magicGlp()), + ...(await multiRewardFarms()), + ].map((i) => ({ ...i, pool: i.pool.toLowerCase() })); module.exports = { timetravel: false, diff --git a/src/adaptors/abracadabra/multi-reward-farms.js b/src/adaptors/abracadabra/multi-reward-farms.js new file mode 100644 index 0000000000..c4a09bb5b6 --- /dev/null +++ b/src/adaptors/abracadabra/multi-reward-farms.js @@ -0,0 +1,91 @@ +const axios = require('axios'); +const { utils: { formatUnits }, BigNumber } = require('ethers'); +const sdk = require('@defillama/sdk'); +const utils = require('../utils'); +const MULTI_REWARDS_STAKING = require('./abis/MultiRewardsStaking.json'); +const SECONDS_PER_YEAR = 31536000; + +const FARMS = { + arbitrum: [{ + address: "0x6d2070b13929Df15B13D96cFC509C574168988Cd", + stakingToken: "0x30dF229cefa463e991e29D42DB0bae2e122B2AC7", + stakingTokenPool: "0bf3cb38-1908-4d85-87c3-af62651d5a03", + rewardTokens: [ + "0x912CE59144191C1204E64559FE8253a0e49E6548", // ARB + "0x3E6648C5a70A150A88bCE65F4aD4d506Fe15d2AF" // SPELL + ], + symbol: "MIM/USDC/USDT", + underlyingTokens: [ + "0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A", // MIM + "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // USDC + "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", // USDT + ], + url: "https://app.abracadabra.money/#/farm/4", + }], +} + +const getApy = async () => { + const pools = await Promise.all(Object.entries(FARMS).map(async ([chain, chainFarms]) => { + const rewardDataCalls = chainFarms.flatMap(({ address, rewardTokens }) => + rewardTokens.map((rewardToken) => ({ + target: address, + params: [rewardToken] + }))); + const [rewardDataResults, totalSupplyResults, prices, symbols, yieldPools] = await Promise.all([ + sdk.api.abi.multiCall({ + abi: MULTI_REWARDS_STAKING.find(({ name }) => name === "rewardData"), + calls: rewardDataCalls, + chain, + }).then((call => call.output)), + sdk.api.abi.multiCall({ + abi: MULTI_REWARDS_STAKING.find(({ name }) => name === "totalSupply"), + calls: chainFarms.map(({ address }) => ({ target: address })), + chain + }).then((call => call.output.map((x) => [x.input.target.toLowerCase(), x]))) + .then(Object.fromEntries), + utils.getPrices(chainFarms.flatMap(({ rewardTokens }) => rewardTokens), chain), + sdk.api.abi.multiCall({ + abi: "erc20:symbol", + calls: chainFarms.map(({ stakingToken }) => ({ + target: stakingToken + })), + chain + }).then((call => call.output.map((x) => [x.input.target.toLowerCase(), x]))) + .then(Object.fromEntries), + axios.get('https://yields.llama.fi/pools').then((result) => result.data.data), + ]); + + const tvlUsdChainFarms = Object.fromEntries(chainFarms.map(({ address }) => { + const totalSupply = formatUnits(totalSupplyResults[address.toLowerCase()].output, 18); + const totalSupplyUsd = totalSupply; // Assume $1 + return [address.toLowerCase(), totalSupplyUsd]; + })) + const aprs = Object.fromEntries(chainFarms.map(({ address }) => [address.toLowerCase(), 0])); + rewardDataResults.forEach(({ input: { target: farm, params: [rewardToken] }, output: { rewardRate } }) => { + const rewardsPerYearRaw = BigNumber.from(rewardRate).mul(SECONDS_PER_YEAR); + const rewardsPerYear = formatUnits(rewardsPerYearRaw, 18); + const rewardsPerYearUsd = rewardsPerYear * prices.pricesByAddress[rewardToken.toLowerCase()]; + const rewardApr = rewardsPerYearUsd / tvlUsdChainFarms[farm.toLowerCase()] * 100; + + aprs[farm.toLowerCase()] = aprs[farm.toLowerCase()] + rewardApr; + }); + return chainFarms.map(({ address, rewardTokens, stakingToken, stakingTokenPool, symbol, underlyingTokens, url }) => { + const stakingTokenYieldPool = yieldPools.find(({ pool }) => pool === stakingTokenPool); + return { + pool: `${address}-${chain}`, + chain: utils.formatChain(chain), + project: 'abracadabra', + tvlUsd: Number(tvlUsdChainFarms[address.toLowerCase()]), + symbol: symbol ?? utils.formatSymbol(symbols[stakingToken.toLowerCase()].output), + apyBase: stakingTokenYieldPool.apyBase, + apyReward: aprs[address.toLowerCase()], + rewardTokens, + underlyingTokens: underlyingTokens ?? stakingTokenYieldPool.underlyingTokens, + url, + } + }); + })); + return pools.flat(); +} + +module.exports = getApy; diff --git a/src/adaptors/amphor/index.js b/src/adaptors/amphor/index.js new file mode 100644 index 0000000000..3d70ab0476 --- /dev/null +++ b/src/adaptors/amphor/index.js @@ -0,0 +1,74 @@ +const utils = require('../utils'); +const sdk = require('@defillama/sdk'); + +const poolsFunction = async () => { + + // const wbtc = "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"; + const wsteth = "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0"; + const prices = (await utils.getPrices([wsteth/*, wbtc*/], 'ethereum')) + .pricesByAddress; + const usdcVaultAddress = '0x3b022EdECD65b63288704a6fa33A8B9185b5096b'; + const wstethVaultAddress = '0x2791EB5807D69Fe10C02eED6B4DC12baC0701744'; + + const ERC4626TotalAssets = + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }; + + const wstethAprData = await utils.getData( + 'https://app.amphor.io/api/apr?vaultSelected=ETH&networkId=1' + ); + const wstethApy = ((1 + Number(wstethAprData.apr)/2600) ** (26) - 1) * 100; + + const usdcAprData = await utils.getData( + 'https://app.amphor.io/api/apr?vaultSelected=USDC&networkId=1' + ); + const usdcApy = ((1 + Number(usdcAprData.apr)/2600) ** (26) - 1) * 100; + + const usdcTotalAsset = await sdk.api.abi.call({ + abi: ERC4626TotalAssets, + chain: 'ethereum', + target: usdcVaultAddress, + }); + const wstethTotalAsset = await sdk.api.abi.call({ + abi: ERC4626TotalAssets, + chain: 'ethereum', + target: wstethVaultAddress, + }); + + const usdcPool = { + pool: usdcVaultAddress, + chain: 'ethereum', + project: 'amphor', + symbol: utils.formatSymbol('USDC'), + tvlUsd: Number(usdcTotalAsset.output)/1e6, + apy: usdcApy, + }; + + const wstethPool = { + pool: wstethVaultAddress, + chain: 'ethereum', + project: 'amphor', + symbol: utils.formatSymbol('WSTETH'), + tvlUsd: (Number(wstethTotalAsset.output)/1e18) * prices[wsteth.toLowerCase()], + apy: wstethApy, + }; + + return [usdcPool, wstethPool]; +}; + +module.exports = { + timetravel: false, + apy: poolsFunction, + url: 'https://app.amphor.io/earn', +}; diff --git a/src/adaptors/astroport/index.js b/src/adaptors/astroport/index.js index 3a0705cb4a..9ccd1a1fbd 100644 --- a/src/adaptors/astroport/index.js +++ b/src/adaptors/astroport/index.js @@ -1,7 +1,7 @@ const { request } = require('graphql-request'); const num = require('bignumber.js'); -const API_ENDPOINT = "https://multichain-api.astroport.fi/graphql"; +const API_ENDPOINT = 'https://multichain-api.astroport.fi/graphql'; const yieldQuery = ` query Pools($chains: [String]!, $limit: Int, $sortField: PoolSortFields, $sortDirection: SortDirections) { @@ -33,16 +33,22 @@ const yieldQuery = ` `; const chainIdToNames = { - "phoenix-1": "Terra2", - "injective-1": "Injective", - "neutron-1": "Neutron" -} + 'phoenix-1': 'Terra2', + 'injective-1': 'Injective', + 'neutron-1': 'Neutron', + 'pacific-1': 'Sei', +}; const astroDenoms = { - "phoenix-1": "terra1nsuqsk6kh58ulczatwev87ttq2z6r3pusulg9r24mfj2fvtzd4uq3exn26", - "injective-1": "ibc/EBD5A24C554198EBAF44979C5B4D2C2D312E6EBAB71962C92F735499C7575839", - "neutron-1": "ibc/5751B8BCDA688FD0A8EC0B292EEF1CDEAB4B766B63EC632778B196D317C40C3A" -} + 'phoenix-1': + 'terra1nsuqsk6kh58ulczatwev87ttq2z6r3pusulg9r24mfj2fvtzd4uq3exn26', + 'injective-1': + 'ibc/EBD5A24C554198EBAF44979C5B4D2C2D312E6EBAB71962C92F735499C7575839', + 'neutron-1': + 'ibc/5751B8BCDA688FD0A8EC0B292EEF1CDEAB4B766B63EC632778B196D317C40C3A', + 'pacific-1': + 'ibc/0EC78B75D318EA0AAB6160A12AEE8F3C7FEA3CFEAD001A3B103E11914709F4CE', +}; const getRewardTokens = (pool) => { const rewardTokens = []; @@ -53,37 +59,44 @@ const getRewardTokens = (pool) => { rewardTokens.push(astroDenoms[pool.chainId]); } return rewardTokens.length > 0 ? rewardTokens : undefined; -} +}; const apy = async () => { - let results = await request(API_ENDPOINT, yieldQuery, { - "chains": ["phoenix-1", "injective-1", "neutron-1"], - "limit": 100, - "sortField": "TVL", - "sortDirection": "DESC" + chains: ['phoenix-1', 'injective-1', 'neutron-1', 'pacific-1'], + limit: 100, + sortField: 'TVL', + sortDirection: 'DESC', }); - const apy = results?.pools?.filter(pool => pool?.poolLiquidityUsd && pool?.poolLiquidityUsd > 10000).map((pool) => { - const apyBase = pool?.tradingFees?.apy ? new num(pool?.tradingFees?.apy).times(100).dp(6).toNumber() : 0; - const chain = chainIdToNames[pool.chainId]; - const astroRewards = pool?.astroRewards?.apr || 0; - const protocolRewards = pool?.protocolRewards?.apr || 0; - const apyReward = new num(astroRewards).plus(protocolRewards).times(100).dp(6).toNumber(); + const apy = results?.pools + ?.filter((pool) => pool?.poolLiquidityUsd && pool?.poolLiquidityUsd > 10000) + .map((pool) => { + const apyBase = pool?.tradingFees?.apy + ? new num(pool?.tradingFees?.apy).times(100).dp(6).toNumber() + : 0; + const chain = chainIdToNames[pool.chainId]; + const astroRewards = pool?.astroRewards?.apr || 0; + const protocolRewards = pool?.protocolRewards?.apr || 0; + const apyReward = new num(astroRewards) + .plus(protocolRewards) + .times(100) + .dp(6) + .toNumber(); - return { - pool: `${pool.poolAddress}-${chain}`.toLowerCase(), - project: 'astroport', - chain, - symbol: `${pool.assets[0].symbol}-${pool.assets[1].symbol}`, - tvlUsd: pool.poolLiquidityUsd || 0, - apyBase, - apyReward, - rewardTokens: getRewardTokens(pool), - underlyingTokens: [pool.token0Address, pool.token1Address], - url: `https://app.astroport.fi/pools/${pool.poolAddress}/provide` - } - }); + return { + pool: `${pool.poolAddress}-${chain}`.toLowerCase(), + project: 'astroport', + chain, + symbol: `${pool.assets[0].symbol}-${pool.assets[1].symbol}`, + tvlUsd: pool.poolLiquidityUsd || 0, + apyBase, + apyReward, + rewardTokens: getRewardTokens(pool), + underlyingTokens: [pool.token0Address, pool.token1Address], + url: `https://app.astroport.fi/pools/${pool.poolAddress}/provide`, + }; + }); return apy; }; diff --git a/src/adaptors/betswirl/index.js b/src/adaptors/betswirl/index.js index 907ffc85e2..a3706b43ad 100644 --- a/src/adaptors/betswirl/index.js +++ b/src/adaptors/betswirl/index.js @@ -29,13 +29,6 @@ const stakingContracts = [ chainId: 42161, chainName: 'Arbitrum', }, - { - target: '0xaeaF7948C38973908fFA97c92F3384595d057135', - chain: 'ethereum', - chainId: 1, - chainName: 'Ethereum', - is8020Staking: true - }, ]; const getInfoABI = ABI.find(({ name }) => name === 'getInfo'); @@ -87,21 +80,31 @@ module.exports = { ); // Fetch the 8020 Balancer LPs receipt token price - const balancer8020Stakings = poolsData.filter((poolData) => poolData.is8020Staking) + const balancer8020Stakings = poolsData.filter( + (poolData) => poolData.is8020Staking + ); const prices = await Promise.all( - balancer8020Stakings - .map((balancer8020Staking) => fetch(`https://api.betswirl.com/api/price?pool=${balancer8020Staking.stakingToken.tokenAddress.toLowerCase()}&chainId=${balancer8020Staking.chainId}`) - .then((res) => res.json())) - ) + balancer8020Stakings.map((balancer8020Staking) => + fetch( + `https://api.betswirl.com/api/price?pool=${balancer8020Staking.stakingToken.tokenAddress.toLowerCase()}&chainId=${ + balancer8020Staking.chainId + }` + ).then((res) => res.json()) + ) + ); balancer8020Stakings.forEach((balancer8020Staking, i) => { - pricesByAddress[balancer8020Staking.stakingToken.tokenAddress.toLowerCase()] = prices[i] - }) + pricesByAddress[ + balancer8020Staking.stakingToken.tokenAddress.toLowerCase() + ] = prices[i]; + }); for (const pool of poolsData) { const tvlUsd = fromWei( pool.totalSupply, pool.stakingToken.decimals - ).multipliedBy(pricesByAddress[pool.stakingToken.tokenAddress.toLowerCase()]); + ).multipliedBy( + pricesByAddress[pool.stakingToken.tokenAddress.toLowerCase()] + ); pools.push({ pool: `${pool.target.toLowerCase()}-${pool.chain}`, chain: pool.chainName, @@ -131,7 +134,9 @@ module.exports = { rewardToken.token.tokenAddress.toLowerCase() ), underlyingTokens: [pool.stakingToken.tokenAddress.toLowerCase()], - url: `https://app.betswirl.com/staking?pool=${pool.target.toLowerCase()}&c=${pool.chainId}`, + url: `https://app.betswirl.com/staking?pool=${pool.target.toLowerCase()}&c=${ + pool.chainId + }`, }); } return pools; diff --git a/src/adaptors/dfx-finance/abis/abiStakingRewardsMulti.json b/src/adaptors/dfx-v2/abis/abiStakingRewardsMulti.json similarity index 100% rename from src/adaptors/dfx-finance/abis/abiStakingRewardsMulti.json rename to src/adaptors/dfx-v2/abis/abiStakingRewardsMulti.json diff --git a/src/adaptors/dfx-finance/config.js b/src/adaptors/dfx-v2/config.js similarity index 100% rename from src/adaptors/dfx-finance/config.js rename to src/adaptors/dfx-v2/config.js diff --git a/src/adaptors/dfx-finance/index.js b/src/adaptors/dfx-v2/index.js similarity index 99% rename from src/adaptors/dfx-finance/index.js rename to src/adaptors/dfx-v2/index.js index 7168dcde7c..2e1d516ad6 100644 --- a/src/adaptors/dfx-finance/index.js +++ b/src/adaptors/dfx-v2/index.js @@ -86,7 +86,7 @@ const buildPool = (entry, chainString) => { const newPool = { pool: entry.id, chain: utils.formatChain(chainString), - project: 'dfx-finance', + project: 'dfx-v2', symbol, tvlUsd: entry.totalValueLockedUSD, rewardTokens: entry.rewardsTokens, diff --git a/src/adaptors/equalizer-exchange/index.js b/src/adaptors/equalizer-exchange/index.js index 55c9bdabb1..b2aa78c9d5 100644 --- a/src/adaptors/equalizer-exchange/index.js +++ b/src/adaptors/equalizer-exchange/index.js @@ -81,11 +81,27 @@ const getApy = async () => { .concat(EQUAL) ), ]; - const priceKeys = tokens.map((i) => `fantom:${i}`).join(','); - const prices = ( - await axios.get(`https://coins.llama.fi/prices/current/${priceKeys}`) - ).data.coins; + const maxSize = 50; + const pages = Math.ceil(tokens.length / maxSize); + let pricesA = []; + let keys = ''; + for (const p of [...Array(pages).keys()]) { + keys = tokens + .slice(p * maxSize, maxSize * (p + 1)) + .map((i) => `fantom:${i}`) + .join(',') + .replaceAll('/', ''); + pricesA = [ + ...pricesA, + (await axios.get(`https://coins.llama.fi/prices/current/${keys}`)).data + .coins, + ]; + } + let prices = {}; + for (const p of pricesA) { + prices = { ...prices, ...p }; + } const pools = allPairs.map((p, i) => { const poolMeta = metaData[i]; diff --git a/src/adaptors/gamma/index.js b/src/adaptors/gamma/index.js index fb6bbdcf7f..3f22987811 100644 --- a/src/adaptors/gamma/index.js +++ b/src/adaptors/gamma/index.js @@ -354,7 +354,7 @@ const getApy = async () => { ...i, apyReward: x.includes(i.pool) ? null : i.apyReward, rewardTokens: x.includes(i.pool) ? null : i.rewardTokens, - })); + })).filter(p => p.chain != 'Binance'); }; module.exports = { diff --git a/src/adaptors/impermax-finance/index.js b/src/adaptors/impermax-finance/index.js index 9e29a7bac3..10f9df6b21 100644 --- a/src/adaptors/impermax-finance/index.js +++ b/src/adaptors/impermax-finance/index.js @@ -593,9 +593,9 @@ async function getTokenPrices(tokens, chain, allCoins) { const totalLiquidity = pairs .map((p) => p.liquidity.usd) - .reduce((a, b) => a + b); + .reduce((a, b) => a + b, 0); return ( - pairs.map((p) => p.priceUsd * p.liquidity.usd).reduce((a, b) => a + b) / + pairs.map((p) => p.priceUsd * p.liquidity.usd).reduce((a, b) => a + b, 0) / totalLiquidity ); } diff --git a/src/adaptors/integral/index.js b/src/adaptors/integral/index.js index eb83c331cf..9b5a3b148d 100644 --- a/src/adaptors/integral/index.js +++ b/src/adaptors/integral/index.js @@ -1,15 +1,13 @@ const { default: BigNumber } = require('bignumber.js'); const utils = require('../utils'); -const baseUrlSize = - 'https://size-api.integral.link/api/v5/pools?apiKey=00Gfs4iNa%2FXJDBkF%2B%2FX83SRqx3MXXAngJMkpx3lM%2FTU='; -const baseUrlFive = - 'https://five-api.integral.link/api/v1/pools?apiKey=00Gfs4iNa%2FXJDBkF%2B%2FX83SRqx3MXXAngJMkpx3lM%2FTU='; -const mainnetUrlSize = `${baseUrlSize}&network=Mainnet`; -const mainnetUrlFive = `${baseUrlFive}&network=Mainnet`; +const mainnetUrlSize = `https://size-api.integral.link/api/v6/pools?apiKey=00Gfs4iNa%2FXJDBkF%2B%2FX83SRqx3MXXAngJMkpx3lM%2FTU=&network=Mainnet`; +const mainnetUrlFive = `https://five-api.integral.link/api/v1/pools?apiKey=00Gfs4iNa%2FXJDBkF%2B%2FX83SRqx3MXXAngJMkpx3lM%2FTU=&network=Mainnet`; +const arbitrumUrlSize = `https://arbitrum-size-api.integral.link/api/v6/pools?apiKey=00Gfs4iNa%2FXJDBkF%2B%2FX83SRqx3MXXAngJMkpx3lM%2FTU=&network=Arbitrum`; const chains = { eth: 'ethereum', + arb: 'arbitrum', }; const buildPool = (entry, chainString, version) => { @@ -49,6 +47,7 @@ const main = async () => { const data = await Promise.all([ topLvl(chains.eth, mainnetUrlSize, 'SIZE'), topLvl(chains.eth, mainnetUrlFive, 'FIVE'), + topLvl(chains.arb, arbitrumUrlSize, 'SIZE'), ]); return data.flat(); diff --git a/src/adaptors/joe-v2.1/index.js b/src/adaptors/joe-v2.1/index.js index 7bbd4e740f..82e71cae72 100644 --- a/src/adaptors/joe-v2.1/index.js +++ b/src/adaptors/joe-v2.1/index.js @@ -4,7 +4,7 @@ const utils = require('../utils'); const apy = async () => { const pools = await Promise.all( ['avalanche', 'arbitrum', 'binance'].map(async (chain) => { - const apiUrl = `https://barn.traderjoexyz.com/v1/pools/${chain}?filterBy=1d&orderBy=volume&pageNum=1&pageSize=25&status=main`; + const apiUrl = `https://barn.traderjoexyz.com/v1/pools/${chain}?filterBy=1d&orderBy=volume&pageNum=1&pageSize=100&status=main`; const pools = (await axios.get(apiUrl)).data; @@ -23,7 +23,7 @@ const apy = async () => { }) ); - return pools.flat(); + return pools.flat().filter((p) => utils.keepFinite(p)); }; module.exports = { diff --git a/src/adaptors/lodestar-v1/index.js b/src/adaptors/lodestar-v1/index.js index 12bd64b52d..a5a9d726ca 100755 --- a/src/adaptors/lodestar-v1/index.js +++ b/src/adaptors/lodestar-v1/index.js @@ -29,6 +29,12 @@ const PROTOCOL_TOKEN = { address: '0xF19547f9ED24aA66b03c3a552D181Ae334FBb8DB'.toLowerCase(), }; +const ARB_TOKEN = { + decimals: 18, + symbol: 'ARB', + address: '0x912CE59144191C1204E64559FE8253a0e49E6548'.toLowerCase(), +} + const getPrices = async (addresses) => { const prices = ( await superagent.get( @@ -153,13 +159,14 @@ const main = async () => { ); const lodePrices = await getPrices([`${CHAIN}:${PROTOCOL_TOKEN.address}`]); + const arbPrices = await getPrices([`${CHAIN}:${ARB_TOKEN.address}`]); const pools = allMarkets.map((market, i) => { const token = underlyingTokens[i] || NATIVE_TOKEN.address; const symbol = // for maker underlyingTokens[i]?.toLowerCase() === - '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2' + '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2' ? 'MKR' : underlyingSymbols[i] || NATIVE_TOKEN.symbol; @@ -188,10 +195,46 @@ const main = async () => { 100 ); }; - const apyReward = calcRewardApy(extraRewards, totalSupplyUsd); + + const baseApyReward = calcRewardApy(extraRewards, totalSupplyUsd); const _apyRewardBorrow = calcRewardApy(extraRewardsBorrow, totalBorrowUsd); const apyRewardBorrow = isNaN(_apyRewardBorrow) ? 0 : _apyRewardBorrow; + // Need to math off the total TVL for ARB and supply and borrow rewards are distributed evenly + let totalTvlUsd = 0; + + // Calculate total TVL (sum of supply and borrow for all markets) + allMarkets.forEach((market, i) => { + const decimals = Number(underlyingDecimals[i]) || NATIVE_TOKEN.decimals; + let price = prices[underlyingTokens[i]?.toLowerCase() || NATIVE_TOKEN.address.toLowerCase()]; + if (price === undefined) + price = underlyingSymbols[i]?.toLowerCase().includes('usd') ? 1 : 0; + + const totalSupplyUsd = + ((Number(marketsCash[i]) + Number(totalBorrows[i])) / 10 ** decimals) * + price; + const totalBorrowUsd = (Number(totalBorrows[i]) / 10 ** decimals) * price; + + totalTvlUsd += totalSupplyUsd + totalBorrowUsd; + }); + + // Calculate ARB APY reward based on total TVL + const calcRewardApyForArb = (denom) => { + return ((arbPrices[ARB_TOKEN.address] * 50892 * 52) / denom) * 100; + }; + + // Define the cut-off date (February 15th, 2024) for the ARB stip rewards + const cutOffDate = new Date('2024-02-15'); + const currentDate = new Date(); + + let arbApyReward = 0; + if (currentDate <= cutOffDate) { + arbApyReward = calcRewardApyForArb(totalTvlUsd); + } + + // Computed total (LODE + ARB rewards) + const apyReward = baseApyReward + arbApyReward; + let poolReturned = { pool: market.toLowerCase(), chain: utils.formatChain(CHAIN), @@ -201,7 +244,7 @@ const main = async () => { apyBase, apyReward, underlyingTokens: [token], - rewardTokens: [apyReward ? PROTOCOL_TOKEN.address : null].filter(Boolean), + rewardTokens: arbApyReward > 0 ? [ARB_TOKEN.address, PROTOCOL_TOKEN.address] : [PROTOCOL_TOKEN.address], }; if (isPaused[i] === false) { poolReturned = { diff --git a/src/adaptors/notional-v3/index.js b/src/adaptors/notional-v3/index.js new file mode 100644 index 0000000000..0f2a815da7 --- /dev/null +++ b/src/adaptors/notional-v3/index.js @@ -0,0 +1,197 @@ +const { request, gql } = require('graphql-request'); +const utils = require('../utils'); +const superagent = require('superagent'); +const { default: BigNumber } = require('bignumber.js'); + +const API = (chain) => `https://data-dev.notional.finance/${chain}/yields` +const NOTE_Mainnet = '0xCFEAead4947f0705A14ec42aC3D44129E1Ef3eD5' + +const SUBGRAPHS = { + arbitrum: 'https://api.studio.thegraph.com/query/36749/notional-v3-arbitrum/version/latest' +}; + + +const query = gql` +query GetYieldsData { + oracles(where: { + matured: false, + oracleType_in: [ + nTokenToUnderlyingExchangeRate, + nTokenBlendedInterestRate, + nTokenFeeRate, + nTokenIncentiveRate + ], + }, first: 1000) { + base { id } + quote { id symbol } + decimals + oracleType + latestRate + } + NOTE: tokens(where: { tokenType: NOTE}) { id } + # Use this to calculate the nToken TVL + nTokens: tokens(where: { tokenType: nToken}) { + id + symbol + totalSupply + underlying {id symbol decimals} + } + activeMarkets { + pCashMarket { + underlying { id symbol decimals } + primeCash { id symbol decimals } + primeDebt { id symbol decimals } + current { + totalPrimeCashInUnderlying + totalPrimeDebtInUnderlying + supplyInterestRate + debtInterestRate + } + } + fCashMarkets { + maturity + underlying { id symbol decimals } + fCash { id symbol decimals } + current { + lastImpliedRate + totalfCashPresentValue + totalPrimeCashInUnderlying + totalfCashDebtOutstandingPresentValue + } + } + } + } +` + +async function getUSDPrice(chain, address) { + // price of base token in USD terms + const key = `${chain}:${address}`; + const priceRes = await superagent.get( + `https://coins.llama.fi/prices/current/${key}` + ); + const price = priceRes.body.coins[key]; + return price ? price.price : 0; +} + +const getPools = async (chain) => { + let results = await request(SUBGRAPHS[chain], query); + const project = 'notional-v3'; + const NOTE = results['NOTE'][0].id + const NOTEPriceUSD = await getUSDPrice('ethereum', NOTE_Mainnet) + + const nTokens = await Promise.all(results['nTokens'].map(async (n) => { + const oracles = results['oracles'].filter(({ quote: { symbol } }) => symbol === n.symbol) + const nTokenExRate = oracles.find(({ oracleType }) => oracleType === 'nTokenToUnderlyingExchangeRate').latestRate + const nTokenBlendedRate = oracles.find(({ oracleType }) => oracleType === 'nTokenBlendedInterestRate').latestRate + const nTokenFeeRate = oracles.find(({ oracleType }) => oracleType === 'nTokenFeeRate').latestRate + const nTokenIncentiveRate = oracles.find(({ oracleType }) => oracleType === 'nTokenIncentiveRate').latestRate + const underlyingDecimals = BigInt(10) ** BigInt(n.underlying.decimals) + const tvlUnderlying = (BigInt(n.totalSupply) * BigInt(nTokenExRate)) / BigInt(1e9) + const underlyingPrice = await getUSDPrice('arbitrum', n.underlying.id) + const tvlUsd = Number(tvlUnderlying) / 1e8 * underlyingPrice + const NOTEPriceInUnderlying = NOTEPriceUSD / underlyingPrice + + return { + pool: `${n.id}-${chain}`, + chain, + project, + symbol: n.symbol, + rewardTokens: [ NOTE ], + underlyingTokens: [ n.underlying.id ], + poolMeta: 'Liquidity Token', + url: `https://arbitrum.notional.finance/liquidity-variable/${n.underlying.symbol}`, + tvlUsd, + apyBase: (Number(nTokenBlendedRate) + Number(nTokenFeeRate)) * 100 / 1e9, + apyReward: (Number(nTokenIncentiveRate) * NOTEPriceInUnderlying) * 100 / 1e9 + } + })) + + const primeCash = await Promise.all(results['activeMarkets'].map(async ({ pCashMarket: p }) => { + const underlyingDecimals = BigNumber(10).pow(p.underlying.decimals) + const totalSupplyUnderlying = BigNumber(p.current.totalPrimeCashInUnderlying).div(underlyingDecimals) + const totalDebtUnderlying = BigNumber(p.current.totalPrimeDebtInUnderlying).div(underlyingDecimals) + const tvlUnderlying = totalSupplyUnderlying.minus(totalDebtUnderlying); + const underlyingPrice = await getUSDPrice('arbitrum', p.underlying.id) + const tvlUsd = tvlUnderlying.times(underlyingPrice).toNumber() + const totalSupplyUsd = totalSupplyUnderlying.times(underlyingPrice).toNumber(); + const totalBorrowUsd = totalDebtUnderlying.times(underlyingPrice).toNumber(); + + return { + pool: `${p.primeCash.id}-${chain}`, + chain, + project, + symbol: p.primeCash.symbol, + underlyingTokens: [ p.underlying.id ], + poolMeta: 'Variable Lend', + url: `https://arbitrum.notional.finance/lend-variable/${p.underlying.symbol}`, + tvlUsd, + apyBase: Number(p.current.supplyInterestRate) * 100 / 1e9, + apyBaseBorrow: Number(p.current.debtInterestRate) * 100 / 1e9, + totalSupplyUsd, + totalBorrowUsd, + } + })) + + const fCash = await Promise.all(results['activeMarkets'].flatMap(({ fCashMarkets }) => { + return fCashMarkets.map(async (f) => { + const underlyingDecimals = BigNumber(10).pow(f.underlying.decimals) + const totalfCashUnderlying = BigNumber(f.current.totalfCashPresentValue).div(underlyingDecimals) + const totalDebtUnderlying = BigNumber(f.current.totalfCashDebtOutstandingPresentValue).div(-underlyingDecimals) + const tvlUnderlying = totalfCashUnderlying.plus(BigNumber(f.current.totalPrimeCashInUnderlying)).div(underlyingDecimals) + + const underlyingPrice = await getUSDPrice('arbitrum', f.underlying.id) + const tvlUsd = tvlUnderlying.times(underlyingPrice).toNumber() + const totalSupplyUsd = totalfCashUnderlying.times(underlyingPrice).toNumber(); + const totalBorrowUsd = totalDebtUnderlying.times(underlyingPrice).toNumber(); + const date = (new Date(Number(f.maturity) * 1000)).toISOString().split('T')[0]; + + return { + pool: `${f.fCash.id}-${chain}`, + chain, + project, + symbol: `f${f.underlying.symbol}`, + underlyingTokens: [ f.underlying.id ], + poolMeta: `Fixed Lend Maturing On ${date}`, + url: `https://arbitrum.notional.finance/lend-fixed/${f.underlying.symbol}`, + tvlUsd, + apyBase: Number(f.current.lastImpliedRate) * 100 / 1e9, + apyBaseBorrow: Number(f.current.lastImpliedRate) * 100 / 1e9, + totalSupplyUsd, + totalBorrowUsd + } + }) + })) + + // TODO: add vaults in the future + // TODO: add leveraged liquidity in the future + // const apiResults = await utils.getData(API(chain)) + // const vaults = apiResults.filter((r) => r.token.tokenType === 'VaultShare' && !!r['leveraged']) + // .map((v) => { + // // TODO: sum up all vaults with this TVL and then select the max APY + // return { + // pool: `${v.token.id}-${chain}`, + // chain, + // project, + // // NOTE: this is pretty ugly, need better metadata + // symbol: v.token.symbol, + // underlyingTokens: [ v.token.underlying ], + // poolMeta: 'Leveraged Vault', + // url: `https://arbitrum.notional.finance/vaults/${v.token.vaultAddress}`, + // tvlUsd: Number(tvlUnderlying), + // apyBase: Number(f.current.lastImpliedRate) / 1e9, + // } + // }) + + return nTokens.concat(primeCash).concat(fCash); +}; + +const main = async () => { + return Object.keys(SUBGRAPHS).reduce(async (acc, chain) => { + return [...(await acc), ...(await getPools(chain))]; + }, Promise.resolve([])); +} + +module.exports = { + timetravel: false, + apy: main, +}; diff --git a/src/adaptors/primex-finance/index.js b/src/adaptors/primex-finance/index.js index 51473d178b..2f143a55a5 100644 --- a/src/adaptors/primex-finance/index.js +++ b/src/adaptors/primex-finance/index.js @@ -1,13 +1,11 @@ const sdk = require('@defillama/sdk'); const superagent = require('superagent'); const { abi } = require('./abi'); -const { APYREWARD_BY_SYMBOL, CHAIN_IDS, DEAD_ADDRESS, ROLES, SECONDS_PER_YEAR, APY_REWARD_BONUS, config } = require('./utils') - -const getPoolUrl = (address, chain) => `https://app.primex.finance/#/bucket-details/${address}?network=${CHAIN_IDS[chain]}` +const { CHAIN_IDS, DEAD_ADDRESS, ROLES, SECONDS_PER_YEAR, APY_REWARD_BONUS, config, addressEq, getPoolUrl } = require('./utils') const formatPool = async (bucket, config, EPMXPrice) => { const { bucketAddress, asset, supportedAssets, supply, demand, bar, lar, estimatedBar, estimatedLar, miningParams, name } = bucket; - const { chain, activityRewardDistributor, EPMX } = config + const { chain, activityRewardDistributor, EPMX, USDCE, apyRewardBySymbol } = config const [rewardPerTokenLender, rewardPerTokenTrader] = (await Promise.all( Object.values(ROLES).map((r) => { @@ -20,9 +18,11 @@ const formatPool = async (bucket, config, EPMXPrice) => { }) ) }) - )).map(v => v.output.isFinished ? 0 : v.output.rewardPerToken) + )).map(v => { + return v.output.isFinished ? 0 : v.output.rewardPerToken + }) - const symbol = asset.symbol + const symbol = addressEq(asset.tokenAddress, USDCE) ? 'USDC.E' : asset.symbol const underlyingTokens = [asset.tokenAddress] const priceKeys = underlyingTokens.map((t) => `${chain.toLowerCase()}:${t}`).join(',') @@ -43,11 +43,13 @@ const formatPool = async (bucket, config, EPMXPrice) => { const apyBaseBorrowCalculated = (Math.pow(1 + (bar / 10 ** 27) / SECONDS_PER_YEAR, SECONDS_PER_YEAR) - 1) * 100 const apyBaseBorrow = isMiningPhase ? 0 : apyBaseBorrowCalculated - const apyRewardCalculated = rewardPerTokenLender * 10 ** asset.decimals / 10 ** 18 * SECONDS_PER_YEAR * EPMXPrice / assetPrice / 10 ** 18 * 100; - const apyReward = isMiningPhase ? APYREWARD_BY_SYMBOL[symbol] + APY_REWARD_BONUS : apyRewardCalculated + // const apyRewardCalculated = (rewardPerTokenLender * 10 ** asset.decimals / 10 ** 18 * SECONDS_PER_YEAR * EPMXPrice / assetPrice.price / 10 ** 18) * 100; + // const apyReward = isMiningPhase ? apyRewardBySymbol[symbol] + APY_REWARD_BONUS : apyRewardCalculated + const apyReward = isMiningPhase ? APY_REWARD_BONUS : 0 - const apyRewardBorrowCalculated = rewardPerTokenTrader * 10 ** asset.decimals / 10 ** 18 * SECONDS_PER_YEAR * EPMXPrice / assetPrice / 10 ** 18 * 100; - const apyRewardBorrow = isMiningPhase ? 0 : apyRewardBorrowCalculated + // const apyRewardBorrowCalculated = (rewardPerTokenTrader * 10 ** asset.decimals / 10 ** 18 * SECONDS_PER_YEAR * EPMXPrice / assetPrice.price / 10 ** 18) * 100; + // const apyRewardBorrow = isMiningPhase ? 0 : apyRewardBorrowCalculated + const apyRewardBorrow = 0 return { pool: `${bucketAddress}-${chain}`.toLowerCase(), diff --git a/src/adaptors/primex-finance/utils.js b/src/adaptors/primex-finance/utils.js index b88c4a0d36..8867d4bc6f 100644 --- a/src/adaptors/primex-finance/utils.js +++ b/src/adaptors/primex-finance/utils.js @@ -7,32 +7,57 @@ const ROLES = { } const CHAIN_IDS = { - Polygon: 137, + Polygon: 'polygon_mainnet', + Arbitrum: 'arbitrum_one' }; -const APYREWARD_BY_SYMBOL = { - ['WETH']: 18, - ['WBTC']: 1, - ['USDC']: 31, - ['USDT']: 20, - ['WMATIC']: 21, -} - const APY_REWARD_BONUS = 7; const config = [ { chain: 'Polygon', - lensAddress: '0xA37a23C5Eb527985caae2a710a0F0De7C49ACb9d', + lensAddress: '0xCbaEc4b0683Ed6F2C2C318500962857768Fc1366', bucketsFactory: '0x7E6915D307F434E4171cCee90e180f5021c60089', positionManager: '0x02bcaA4633E466d151b34112608f60A82a4F6035', activityRewardDistributor: '0x156e2fC8e1906507412BEeEB6640Bf999a1Ea76b', + USDCE: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", EPMX: "0xDc6D1bd104E1efa4A1bf0BBCf6E0BD093614E31A", EPMXPriceFeed: "0x103A9FF33c709405DF58f8f209C53f6B5c5eA2BE", EPMXPriceFeedDecimals: 8, + apyRewardBySymbol: { + ['WETH']: 18, + ['WBTC']: 1, + ['USDC.E']: 31, + ['USDT']: 20, + ['WMATIC']: 21, + } + }, + { + chain: 'Arbitrum', + lensAddress: '0x3a5CAdB5eDF17876fD2518AEC3a4d804964aA89e', + bucketsFactory: '0x4e6f7372bCE4083c779c17B240A94dc2EA57AE67', + positionManager: '0x86890E30cE9E1e13Db5560BbEb435c55567Af1cd', + activityRewardDistributor: '0x38D94212AAe3f4aB3E4fE801d9021ab0aA371DaB', + USDCE: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", + EPMX: "0xA533f744B179F2431f5395978e391107DC76e103", + EPMXPriceFeed: "0x053FB5b7c555FC0d9Bc49118023d6B6A4019168f", + EPMXPriceFeedDecimals: 8, + apyRewardBySymbol: { + ['WETH']: 23.5, + ['WBTC']: 1.25, + ['USDC']: 39.5, + ['USDT']: 41, + ['ARB']: 1, + ['USDC.E']: 39.5, + } }, ] -const getPoolUrl = (address, chain) => `https://app.primex.finance/#/bucket-details/${address}?network=${CHAIN_IDS[chain]}` +const getPoolUrl = (address, chain) => `https://app.primex.finance/#/bucket-details/${address}?network=${CHAIN_IDS[chain]}`; + +const addressEq = (addressA, addressB) => { + if (!addressA || !addressB) return false; + return addressA.toLowerCase() === addressB.toLowerCase(); +} -module.exports = { DEAD_ADDRESS, SECONDS_PER_YEAR, ROLES, CHAIN_IDS, APYREWARD_BY_SYMBOL, APY_REWARD_BONUS, config } \ No newline at end of file +module.exports = { DEAD_ADDRESS, SECONDS_PER_YEAR, ROLES, CHAIN_IDS, APY_REWARD_BONUS, config, getPoolUrl, addressEq } \ No newline at end of file diff --git a/src/adaptors/reserve/index.js b/src/adaptors/reserve/index.js index 902e109d0f..750a875759 100644 --- a/src/adaptors/reserve/index.js +++ b/src/adaptors/reserve/index.js @@ -4,14 +4,22 @@ const superagent = require('superagent'); const { request, gql } = require('graphql-request'); const ethers = require('ethers'); const { default: BigNumber } = require('bignumber.js'); -const sdk = require('@defillama/sdk'); +const sdk = require('@defillama/sdk4'); const { facadeAbi, rtokenAbi } = require('./abi'); -const CHAIN = 'ethereum'; -const FACADE_ADDRESS = '0xad0BFAEE863B1102e9fD4e6330A02B08d885C715'; -const graphEndpoint = - 'https://api.thegraph.com/subgraphs/name/lcamargof/reserve-test'; +const chains = [ + { + chainName: 'base', + facade: '0xe1aa15DA8b993c6312BAeD91E0b470AE405F91BF', + graph: 'https://graph-base.register.app/subgraphs/name/lcamargof/reserve', + }, + { + chainName: 'ethereum', + facade: '0xad0BFAEE863B1102e9fD4e6330A02B08d885C715', + graph: 'https://api.thegraph.com/subgraphs/name/lcamargof/reserve-test', + }, +]; const rtokenQuery = gql` { @@ -57,12 +65,17 @@ const poolsMap = { 'c04005c9-7e34-41a6-91c4-295834ed8ac0': 'stkcvxeusd3crv-f', 'fa4d7ee4-0001-4133-9e8d-cf7d5d194a91': 'fusdc-vault', '325ad2d6-70b1-48d7-a557-c2c99a036f87': 'mrp-ausdc', + // Base + 'df65c4f4-e33a-481c-bac8-0c2252867c93': 'wcusdcv3', + '9d09b0be-f6c2-463a-ad2c-4552b3e12bd9': 'wsgusdbc', + '0f45d730-b279-4629-8e11-ccb5cc3038b4': 'cbeth', }; const rtokenTvl = (rtoken) => (rtoken.token?.totalSupply / 1e18) * rtoken.token?.lastPriceUSD || 0; -const main = async () => { +const apyChain = async (chainProps) => { + const { chainName, facade, graph } = chainProps; const poolsData = (await utils.getData('https://yields.llama.fi/pools')) ?.data; @@ -77,7 +90,8 @@ const main = async () => { } } } - const { rtokens } = await request(graphEndpoint, rtokenQuery); + + const { rtokens } = await request(graph, rtokenQuery); const filteredRtokens = rtokens.filter( (rtoken) => rtoken && rtokenTvl(rtoken) > 10_000 @@ -87,6 +101,7 @@ const main = async () => { const { output: mainAddresses } = await sdk.api.abi.multiCall({ abi: rtokenAbi.find(({ name }) => name === 'main'), + chain: chainName, calls: rtokenAddresses.map((rtokenAddress) => ({ target: rtokenAddress, params: [], @@ -94,6 +109,7 @@ const main = async () => { }); const { output: distributorAddresses } = await sdk.api.abi.multiCall({ + chain: chainName, abi: rtokenAbi.find(({ name }) => name === 'distributor'), calls: mainAddresses.map(({ output: mainAddress }) => ({ target: mainAddress, @@ -102,6 +118,7 @@ const main = async () => { }); const { output: distributions } = await sdk.api.abi.multiCall({ + chain: chainName, abi: rtokenAbi.find(({ name }) => name === 'distribution'), calls: distributorAddresses.map(({ output: distributorAddress }) => ({ target: distributorAddress, @@ -110,9 +127,10 @@ const main = async () => { }); const { output: basketBreakdowns } = await sdk.api.abi.multiCall({ + chain: chainName, abi: facadeAbi.find(({ name }) => name === 'basketBreakdown'), calls: rtokenAddresses.map((rtokenAddress) => ({ - target: FACADE_ADDRESS, + target: facade, params: [rtokenAddress], })), }); @@ -122,6 +140,7 @@ const main = async () => { if (!rtoken) return null; const { output: symbols } = await sdk.api.abi.multiCall({ + chain: chainName, abi: 'erc20:symbol', calls: basketBreakdowns[i].output.erc20s.map((erc20) => ({ target: erc20, @@ -153,7 +172,7 @@ const main = async () => { return { pool: rtoken.id, - chain: CHAIN, + chain: chainName, project: 'reserve', symbol: rtoken.token?.symbol, tvlUsd: rtokenTvl(rtoken), @@ -171,7 +190,15 @@ const main = async () => { return reservePools; }; +const apy = async () => { + const pools = await Promise.all( + chains.map(async (chainProps) => await apyChain(chainProps)) + ); + + return pools.flat(); +}; + module.exports = { timetravel: false, - apy: main, + apy, }; diff --git a/src/adaptors/sandclock/abi.js b/src/adaptors/sandclock/abi.js index 3e721f2177..779d6972b3 100644 --- a/src/adaptors/sandclock/abi.js +++ b/src/adaptors/sandclock/abi.js @@ -1,81 +1,80 @@ -const lusdVaultABI = [ - { - "inputs": [], - "name": "totalUnderlying", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalUnderlyingMinusSponsored", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalShares", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } +const erc4626ABI = [ + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, ]; -const erc4626ABI = [ +const erc20ABI = [ { - "inputs": [], - "name": "totalAssets", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" }, +]; + +const stabilityPoolABI = [ { - "inputs": [ - { - "internalType": "uint256", - "name": "shares", - "type": "uint256" - } - ], - "name": "convertToAssets", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } + "inputs": [ + { + "internalType": "address", + "name": "_depositor", + "type": "address" + } + ], + "name": "getDepositorLQTYGain", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, ]; module.exports = { - lusdVaultABI, erc4626ABI, + erc20ABI, + stabilityPoolABI, }; \ No newline at end of file diff --git a/src/adaptors/sandclock/index.js b/src/adaptors/sandclock/index.js index 7694b322f1..1916c68407 100644 --- a/src/adaptors/sandclock/index.js +++ b/src/adaptors/sandclock/index.js @@ -1,108 +1,101 @@ const { ethers } = require('ethers'); const { getProvider } = require('@defillama/sdk/build/general'); -const { lusdVaultABI, erc4626ABI } = require('./abi'); +const { erc4626ABI, erc20ABI, stabilityPoolABI } = require('./abi'); const BigNumber = require('bignumber.js'); // support decimal points const superagent = require('superagent'); -const LIQUITY_VAULT = '0x91a6194f1278f6cf25ae51b604029075695a74e5'; -const YEARN_VAULT = '0x4FE4BF4166744BcBc13C19d959722Ed4540d3f6a'; -const WETH_VAULT = '0x1Fc623b96c8024067142Ec9c15D669E5c99c5e9D'; -const USDC_VAULT = '0x1038Ff057b7092f17807358c6f68b42661d15caB'; +// const LIQUITY_VAULT = '0x91a6194f1278f6cf25ae51b604029075695a74e5'; // deprecated +// const YEARN_VAULT = '0x4FE4BF4166744BcBc13C19d959722Ed4540d3f6a'; // deprecated +// const WETH_VAULT = '0x1Fc623b96c8024067142Ec9c15D669E5c99c5e9D'; // never in frontend or facing user +// const USDC_VAULT = '0x1038Ff057b7092f17807358c6f68b42661d15caB'; // never in frontend or facing user +// const JADE = '0x00C567D2b1E23782d388c8f58E64937CA11CeCf1'; // not enough tvl for yield server +// const AMETHYST = '0x8c0792Bfee67c80f0E7D4A2c5808edBC9af85e6F'; // not enough tvl for yield server +const AMBER = '0xdb369eEB33fcfDCd1557E354dDeE7d6cF3146A11'; +const EMERALD = '0x4c406C068106375724275Cbff028770C544a1333'; +const OPAL = '0x096697720056886b905D0DEB0f06AfFB8e4665E5'; + const LUSD = '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0'; const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; -const chain = 'ethereum'; -const provider = getProvider(chain); -const BLOCKS_PER_DAY = 7160; +const LQTY = '0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D'; -const apy = async () => { - - const prices = await getPrices([LUSD, USDC, WETH]); +const QUARTZ = '0xbA8A621b4a54e61C442F5Ec623687e2a942225ef'; - const lusdPool = await calcLusdPoolApy(prices[LUSD.toLowerCase()]); +const LIQUITY_STABILITY_POOL = '0x66017D22b0f8556afDd19FC67041899Eb65a21bb'; - const usdcDecimals = new BigNumber(10**6); - const usdcPool = await calcErc4626PoolApy(USDC, 'USDC', usdcDecimals, USDC_VAULT, prices[USDC.toLowerCase()]); - - const wethDecimals = new BigNumber(10).pow(18); - const wethPool = await calcErc4626PoolApy(WETH, 'WETH', wethDecimals, WETH_VAULT, prices[WETH.toLowerCase()]); +const chain = 'ethereum'; +const provider = getProvider(chain); +const BLOCKS_PER_DAY = 7160; - return [lusdPool, usdcPool, wethPool]; -} +const lqtyContract = new ethers.Contract(LQTY, erc20ABI, provider); +const stabilityPoolContract = new ethers.Contract(LIQUITY_STABILITY_POOL, stabilityPoolABI, provider); +const LQTY_DECIMALS = new BigNumber(1e18.toString()); -async function calcLusdPoolApy(lusdPrice) { - const liquityVault = new ethers.Contract(LIQUITY_VAULT, lusdVaultABI, provider); - const yearnVault = new ethers.Contract(YEARN_VAULT, lusdVaultABI, provider); +const apy = async () => { - // combine LIQUTIY_VAULT and YEARN_VAULT to be consistent with the DefiLlama TVL adaptor - const [lvl, yvl] = await Promise.all([ - await liquityVault.totalUnderlying(), - await yearnVault.totalUnderlying() - ]); + const prices = await getPrices([LUSD, USDC, WETH, LQTY]); - const lusdDecimals = new BigNumber(10).pow(18); - // convert ethers BigNumber to bignumber's BigNumber to have decimal points - const tvlLUSD = new BigNumber(lvl.add(yvl).toString()).div(lusdDecimals); - const tvlUsd = tvlLUSD.multipliedBy(lusdPrice).toNumber(); + const amber = await calcErc4626PoolApy(LUSD, 'LUSD', 'Amber', AMBER, prices, true); - // s1: totalShares at the moment - // u1: LUSD in the vault at the moment - const [s1, u1] = await Promise.all([ - await liquityVault.totalShares(), - await liquityVault.totalUnderlyingMinusSponsored() - ]); + const opal = await calcErc4626PoolApy(USDC, 'USDC', 'Opal', OPAL, prices, false); - - // s0: totalShares 28 days before - // u0: LUSD in the vault 28 days before - const [s0, u0] = await Promise.all([ - await liquityVault.totalShares({ blockTag: -BLOCKS_PER_DAY * 28 }), - await liquityVault.totalUnderlyingMinusSponsored({ blockTag: -BLOCKS_PER_DAY * 28 }) - ]); - - // Let sp1 be the current share price in LUSD, i.e., sp1 = u1 / s1 - // Let sp0 be the share price in LSUD 28 days before, i.e., sp0 = u0 / s0 - // we compound 28 days return 13 times to get the apy, as - // that is, {[u1 * s0 / (u0 * s1)]^13 - 1} * 100 - const n = new BigNumber(u1.mul(s0).toString()); - const d = new BigNumber(u0.mul(s1).toString()); - const apy = n.div(d).pow(13).minus(1).times(100).toNumber(); - - const lusdPool = { - pool: `${LIQUITY_VAULT}-${chain}`, - chain, - project: 'sandclock', - symbol: 'LUSD', - tvlUsd, - underlyingTokens: [LUSD], - apy, - poolMeta: 'LUSD Vault', - url: 'https://app.sandclock.org/' - }; + const emerald = await calcErc4626PoolApy(WETH, 'WETH', 'Emerald', EMERALD, prices, false); - return lusdPool; + return [amber, opal, emerald]; } -async function calcErc4626PoolApy(asset, symbol, decimals, vault, price) { +async function calcErc4626PoolApy(asset, symbol, poolMeta, vault, prices, liquity) { const contract = new ethers.Contract(vault, erc4626ABI, provider); - const tvl = await contract.totalAssets(); - const tvlUsd = new BigNumber(tvl.toString()).multipliedBy(price).div(decimals).toNumber(); - - const sharePriceNow = await contract.convertToAssets(1); - const sharePriceDayBefore = await contract. convertToAssets(1, { blockTag: -BLOCKS_PER_DAY }); - const n = new BigNumber(sharePriceNow.toString()); - const d = new BigNumber(sharePriceDayBefore.toString()); - const apy = n.div(d).pow(365).minus(1).times(100).toNumber(); + const decimals = asset == USDC ? new BigNumber(1e6.toString()) : new BigNumber(1e18.toString()); + const price = prices[asset.toLowerCase()]; + const tvl = await contract.totalAssets(); + let tvlUsd = new BigNumber(tvl.toString()).multipliedBy(price).div(decimals); + if (liquity) { + let lqtyUsd = await calcLqtyUsd(vault, prices); + tvlUsd = tvlUsd.plus(lqtyUsd); + } + + const days = 7; + let totalAssetsNow = new BigNumber((await contract.totalAssets()).toString()); + if (liquity) { + totalAssetsNow = totalAssetsNow.multipliedBy(price); + await calcLqtyAssetNow(vault, prices); + totalAssetsNow = totalAssetsNow.plus(lqtyTotalUsd); + } + const totalSharesNow =new BigNumber((await contract.totalSupply()).toString()); + const sharePriceNow = totalSharesNow.isZero()? + new BigNumber(0) : + totalAssetsNow.div(totalSharesNow); + + let totalAssetsBefore = new BigNumber((await contract.totalAssets({ blockTag: -BLOCKS_PER_DAY * days })).toString()); + if (liquity) { + totalAssetsBefore = totalAssetsBefore.multipliedBy(price); + await calcLqtyAssetBefore(vault, days, prices); + totalAssetsBefore = totalAssetsBefore.plus(lqtyTotalUsd); + } + const totalSharesBefore =new BigNumber((await contract.totalSupply({ blockTag: -BLOCKS_PER_DAY * days })).toString()); + const sharePriceBefore = totalSharesBefore.isZero()? + new BigNumber(0) : + totalAssetsBefore.div(totalSharesBefore); + // const compound = Math.floor(365 / days); + // const apy = n.div(d).pow(compound).minus(1).times(100).toNumber(); + const apyBase = sharePriceBefore.isZero() ? + 0 : + sharePriceNow.minus(sharePriceBefore).multipliedBy(365).div(days).div(sharePriceBefore).multipliedBy(100).toNumber(); + + const apyReward = 15; // QUARTZ will be airdropped to depositors to Amber, Opal and Emerald vaults const erc4626Pool = { pool: `${vault}-${chain}`, chain, project: 'sandclock', symbol, - tvlUsd, + tvlUsd: tvlUsd.toNumber(), underlyingTokens: [asset], - apy, - poolMeta: `${symbol} Vault`, + rewardTokens: [QUARTZ], + apyBase, + apyReward, + poolMeta, url: 'https://app.sandclock.org/' }; @@ -126,6 +119,37 @@ const getPrices = async (addresses) => { return pricesByAddresses; }; +async function calcLqtyUsd(vault, prices) { + let lqtyTotal = ethers.BigNumber.from(0); + + const lqtyBalance = await lqtyContract.balanceOf(vault); + lqtyTotal = lqtyTotal.add(lqtyBalance); + + const lqtyGain = await stabilityPoolContract.getDepositorLQTYGain(vault); + lqtyTotal = lqtyTotal.add(lqtyGain); + + let lqtyUsd = new BigNumber(lqtyTotal.toString()).multipliedBy(prices[LQTY.toLowerCase()]).div(LQTY_DECIMALS); + return lqtyUsd; +} + +async function calcLqtyAssetNow(vault, prices) { + let lqtyTotal = ethers.BigNumber.from(0); + const lqtyBalance = await lqtyContract.balanceOf(vault); + lqtyTotal = lqtyTotal.add(lqtyBalance); + const lqtyGain = await stabilityPoolContract.getDepositorLQTYGain(vault); + lqtyTotal = lqtyTotal.add(lqtyGain); + lqtyTotalUsd = new BigNumber(lqtyTotal.toString()).multipliedBy(prices[LQTY.toLowerCase()]); +} + +async function calcLqtyAssetBefore(vault, days, prices) { + let lqtyTotal = ethers.BigNumber.from(0); + const lqtyBalance = await lqtyContract.balanceOf(vault, { blockTag: -BLOCKS_PER_DAY * days }); + lqtyTotal = lqtyTotal.add(lqtyBalance); + const lqtyGain = await stabilityPoolContract.getDepositorLQTYGain(vault, { blockTag: -BLOCKS_PER_DAY * days }); + lqtyTotal = lqtyTotal.add(lqtyGain); + lqtyTotalUsd = new BigNumber(lqtyTotal.toString()).multipliedBy(prices[LQTY.toLowerCase()]); +} + module.exports = { timetravel: true, apy, diff --git a/src/adaptors/solv-funds/index.js b/src/adaptors/solv-funds/index.js index f169908ed7..5c5711f802 100644 --- a/src/adaptors/solv-funds/index.js +++ b/src/adaptors/solv-funds/index.js @@ -1,17 +1,6 @@ const { default: request, gql } = require('graphql-request'); const axios = require('axios'); -const poolIds = [ - "0xe037ef7b5f74bf3c988d8ae8ab06ad34643749ba9d217092297241420d600fce", - "0xc573ea3cb55168e5ebe04b118c4b8a33ba62e86bbe1d1e735a447467795c3bd4", - "0xf514bf1cefa177ea26156b1d0d0da28da8f88b5eb8ed673ddd2cea5334bb6d6c", - "0x9119ceb6bcf974578e868ab65ae20c0d546716a6657eb27dc3a6bf113f0b519c", - "0xf514bf1cefa177ea26156b1d0d0da28da8f88b5eb8ed673ddd2cea5334bb6d6c", - "0x3f9c68bbb64152799c006c8c505d60edf930f7a45db3b63534ed4c2dae07f7a8", - "0x504d291d2f4dedf8fa3ac3a342ff3531b8947fa835077c8312fa18da2be4084c", - "0x370c1ec55ca4a79b54909892f55573603611e0cf34f89a40405e81b8d6a78195" -] - const chain = { 1: 'ethereum', 56: 'bsc', @@ -33,7 +22,9 @@ const poolsQuery = gql` query Pools { pools(filter:{ poolStatus: "Active", - auditStatus: "Approved" + auditStatus: "Approved", + isUnlisted: false, + saleStatus: ["Upcoming","Fundraising","Active"] }){ poolsInfo { id @@ -74,9 +65,13 @@ const poolsFunction = async () => { await axios.get(`https://coins.llama.fi/prices/current/${pricesArray}`) ).data.coins; + const filterOutPoolId = ( + await axios.get(`https://raw.githubusercontent.com/solv-finance-dev/slov-protocol-defillama/main/pools.json`) + ).data.filterOut; + let ustPool = []; for (const pool of pools.poolsInfo) { - if (poolIds.indexOf(pool.poolOrderInfo.poolId) == -1) { + if (filterOutPoolId.indexOf(pool.poolOrderInfo.poolId) !== -1) { continue } diff --git a/src/adaptors/sommelier/config.js b/src/adaptors/sommelier/config.js index 8290c60fab..ea76b90f21 100644 --- a/src/adaptors/sommelier/config.js +++ b/src/adaptors/sommelier/config.js @@ -17,6 +17,8 @@ const turbogho = '0x0c190ded9be5f512bd72827bdad4003e9cc7975c'; const ethgrowth = '0x6c51041a91c91c86f3f08a72cb4d3f67f1208897'; const turbosteth = '0xfd6db5011b171b05e1ea3b92f9eacaeeb055e971'; const turbosomm = '0x5195222f69c5821f8095ec565e71e18ab6a2298f'; +const turboeeth = '0x9a7b4980C6F0FCaa50CD5f288Ad7038f434c692e'; +const turbostethstethDeposit = '0xc7372Ab5dd315606dB799246E8aA112405abAeFf'; // Rewards are paid out in EVM SOMM const rewardTokens = ['0xa670d7237398238de01267472c6f13e5b8010fd1']; @@ -47,6 +49,8 @@ const stakingPools = { [ethgrowth]: '0xb1D3948F4DCd7Aa5e89449080F3D88870aD0137A', // TODO: If we add staking pool for turbo steth, add it here // TODO: If we add staking pool for turbo somm, add it here + [turboeeth]: '0x596c3f05ba9c6c356527e47989b3ed26e2b3449d', + // TODO: If we add staking pool for turbo steth (steth deposit), add it here }; // List of v0815 Cellars @@ -337,6 +341,32 @@ const v2p5Pools = [ underlyingTokens: [], url: 'https://app.sommelier.finance/strategies/Turbo-SOMM', }, + { + pool: `${turboeeth}-ethereum`, + chain, + project, + symbol: 'WETH-EETH', + poolMeta: 'TurboEETH', + tvlUsd: 0, + apyBase: 0, + apyReward: 0, + rewardTokens, + underlyingTokens: [], + url: 'https://app.sommelier.finance/strategies/Turbo-eETH', + }, + { + pool: `${turbostethstethDeposit}-ethereum`, + chain, + project, + symbol: 'STETH-WSTETH-WETH', + poolMeta: 'TurboSTETH(stETHDeposit)', + tvlUsd: 0, + apyBase: 0, + apyReward: 0, + rewardTokens, + underlyingTokens: [], + url: 'https://app.sommelier.finance/strategies/Turbo-STETH-(steth-deposit)', + }, ]; module.exports = { diff --git a/src/adaptors/stablebase/abis.js b/src/adaptors/stablebase/abis.js new file mode 100644 index 0000000000..e73383cff9 --- /dev/null +++ b/src/adaptors/stablebase/abis.js @@ -0,0 +1,1660 @@ +module.exports = { + lpABI: [ + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "reserve0", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "reserve1", + "type": "uint112" + } + ], + "name": "Sync", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MINIMUM_LIQUIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReserves", + "outputs": [ + { + "internalType": "uint112", + "name": "_reserve0", + "type": "uint112" + }, + { + "internalType": "uint112", + "name": "_reserve1", + "type": "uint112" + }, + { + "internalType": "uint32", + "name": "_blockTimestampLast", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_token0", + "type": "address" + }, + { + "internalType": "address", + "name": "_token1", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price0CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price1CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "skim", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "sync", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ], + masterChefABI: [ + { + "inputs": [ + { + "internalType": "contract IBoringERC20", + "name": "_sBaseToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_sBasePerSec", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_treasuryAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "allocPoint", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IBoringERC20", + "name": "lpToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "depositFeeBP", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "harvestInterval", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IComplexRewarder[]", + "name": "rewarders", + "type": "address[]" + } + ], + "name": "Add", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newAmount", + "type": "uint256" + } + ], + "name": "AllocPointsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "EmergencyWithdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "EmissionRateUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountLockedUp", + "type": "uint256" + } + ], + "name": "RewardLockedUp", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "allocPoint", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "depositFeeBP", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "harvestInterval", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IComplexRewarder[]", + "name": "rewarders", + "type": "address[]" + } + ], + "name": "Set", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "SetTreasuryAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastRewardTimestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lpSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accSBasePerShare", + "type": "uint256" + } + ], + "name": "UpdatePool", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "MAXIMUM_DEPOSIT_FEE_RATE", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAXIMUM_HARVEST_INTERVAL", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SBASE", + "outputs": [ + { + "internalType": "contract IBoringERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_allocPoint", + "type": "uint256" + }, + { + "internalType": "contract IBoringERC20", + "name": "_lpToken", + "type": "address" + }, + { + "internalType": "uint16", + "name": "_depositFeeBP", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "_harvestInterval", + "type": "uint256" + }, + { + "internalType": "contract IComplexRewarder[]", + "name": "_rewarders", + "type": "address[]" + } + ], + "name": "add", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "canHarvest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "pid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "depositWithPermit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + } + ], + "name": "emergencyWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_pids", + "type": "uint256[]" + } + ], + "name": "harvestMany", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "massUpdatePools", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "pendingTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "addresses", + "type": "address[]" + }, + { + "internalType": "string[]", + "name": "symbols", + "type": "string[]" + }, + { + "internalType": "uint256[]", + "name": "decimals", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "poolInfo", + "outputs": [ + { + "internalType": "contract IBoringERC20", + "name": "lpToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allocPoint", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastRewardTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "accSBasePerShare", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "depositFeeBP", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "harvestInterval", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalLp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + } + ], + "name": "poolRewarders", + "outputs": [ + { + "internalType": "address[]", + "name": "rewarders", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + } + ], + "name": "poolRewardsPerSec", + "outputs": [ + { + "internalType": "address[]", + "name": "addresses", + "type": "address[]" + }, + { + "internalType": "string[]", + "name": "symbols", + "type": "string[]" + }, + { + "internalType": "uint256[]", + "name": "decimals", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "rewardsPerSec", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "pid", + "type": "uint256" + } + ], + "name": "poolTotalLp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "sBasePerSec", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_allocPoint", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "_depositFeeBP", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "_harvestInterval", + "type": "uint256" + }, + { + "internalType": "contract IComplexRewarder[]", + "name": "_rewarders", + "type": "address[]" + } + ], + "name": "set", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_treasuryAddress", + "type": "address" + } + ], + "name": "setTreasuryAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "startFarming", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "startTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAllocPoint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalLockedUpRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSBaseInPools", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_allocPoint", + "type": "uint256" + } + ], + "name": "updateAllocPoint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_sBasePerSec", + "type": "uint256" + } + ], + "name": "updateEmissionRate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + } + ], + "name": "updatePool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userInfo", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardLockedUp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nextHarvestUntil", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +}; diff --git a/src/adaptors/stablebase/index.js b/src/adaptors/stablebase/index.js new file mode 100644 index 0000000000..4a7ee3fff0 --- /dev/null +++ b/src/adaptors/stablebase/index.js @@ -0,0 +1,107 @@ +const sdk = require('@defillama/sdk4'); +const Web3 = require('web3'); +const superagent = require('superagent'); +const { masterChefABI, lpABI } = require('./abis'); +const utils = require('../utils'); + +const MASTERCHEF_ADDRESS = '0x8eE78D4836B44944D393941A736b6DC23e33bc69'; +const sBASE = '0x616F5b97C22Fa42C3cA7376F7a25F0d0F598b7Bb'; +const WETH = '0x4200000000000000000000000000000000000006'; +const sBASE_WETH_LP = "0xd072a63c2d54b49229a4557b3aeb1cbe04eb6b2e" + +const RPC_URL = 'https://mainnet.base.org'; +const web3 = new Web3(RPC_URL); + +const poolStaticData = { + "0x5626f9217e774eabebb86821f5fbe6e8ed0770dc": { + tokens: 'USDC-DAI-axlUSD', + }, + [sBASE_WETH_LP]: { + tokens: 'sBASE-WETH', + }, + "0x890cff90ee1c24b0a2264e5d3618bfdec05b077c": { + tokens: 'USDC-USDbC', + } +} + +const getPrice = async () => { + const lpContract = new web3.eth.Contract(lpABI, sBASE_WETH_LP); + const sBASEreserves = await lpContract.methods.getReserves().call(); + const totalSupply = await lpContract.methods.totalSupply().call(); + + // WETH Price + const ethPriceRes = ( + await superagent.get( + `https://coins.llama.fi/prices/current/base:${WETH.toLowerCase()}` + ) + ).body.coins; + const ethPrice = ethPriceRes[`base:${WETH}`]?.price + + // sBASE Price + const reserve1 = sBASEreserves[0] + const reserve0 = sBASEreserves[1] + const sBASE_IN_ETH = Number(reserve1) / Number(reserve0) + const sBasePrice = sBASE_IN_ETH * ethPrice + + // sBASE-WETH LP Price + const token0total = Number(Number(sBasePrice * (Number(reserve0) / 10 ** 18)).toString()) + const token1total = Number(Number(ethPrice * (Number(reserve1) / 10 ** 18)).toString()) + const lpTotalPrice = Number(token0total + token1total) + const lpPrice = lpTotalPrice / (totalSupply / 10 ** 18) + + return { sBasePrice, ethPrice, lpPrice } +}; + +const main = async () => { + const { sBasePrice, ethPrice, lpPrice } = await getPrice(); + const masterChef = new web3.eth.Contract(masterChefABI, MASTERCHEF_ADDRESS); + + const poolsCount = await masterChef.methods.poolLength().call(); + const totalAllocPoint = await masterChef.methods.totalAllocPoint().call(); + const perSec = (await masterChef.methods.sBasePerSec().call()) / 1e18; + + const poolsRes = await sdk.api.abi.multiCall({ + abi: masterChefABI.filter(({ name }) => name === 'poolInfo')[0], + calls: [...Array(Number(poolsCount)).keys()].map((i) => ({ + target: MASTERCHEF_ADDRESS, + params: i, + })), + chain: 'base', + }); + + const pools = poolsRes.output.map(({ output }, i) => ({ ...output, i })); + + const response = pools.map((pool, index) => { + const allocPoint = pool[1] + const _tvl = pool[6] + const numerator = allocPoint * perSec * 86400; + const denominator = totalAllocPoint + const rewardPerDay = numerator / denominator; + const rewardPerYear = (rewardPerDay * 365) * 100 + let apy = 0 + let tvl = _tvl / 1e18 + if (index === 1) { + tvl = ((_tvl / 1e18) * lpPrice) + apy = (rewardPerYear * sBasePrice) / tvl + } else { + apy = (rewardPerYear * sBasePrice) / tvl + } + return { + pool: pool[0], + chain: utils.formatChain('base'), + project: 'stablebase', + symbol: poolStaticData[pool[0].toLowerCase()].tokens, + tvlUsd: tvl, + apyReward: apy, + rewardTokens: [sBASE] + } + }) + + return response +}; + +module.exports = { + timetravel: false, + apy: main, + url: 'https://stablebase.fi/farm', +}; diff --git a/src/adaptors/stakedao/index.js b/src/adaptors/stakedao/index.js index defea1e90a..82b6284f37 100644 --- a/src/adaptors/stakedao/index.js +++ b/src/adaptors/stakedao/index.js @@ -1,7 +1,7 @@ const utils = require('../utils'); -const LOCKERS_ENDPOINT = 'https://lockers.stakedao.org/api/lockers/cache'; -const STRATEGIES_ENDPOINT = 'https://lockers.stakedao.org/api/strategies/cache'; +const LOCKERS_ENDPOINT = 'https://classic.stakedao.org/api/lockers/cache'; +const STRATEGIES_ENDPOINT = 'https://classic.stakedao.org/api/strategies/cache'; const SDT_ADDRESS = '0x73968b9a57c6e53d41345fd57a6e6ae27d6cdb2f'; const symbolMapping = { @@ -30,7 +30,7 @@ const poolsFunction = async () => { utils.getData(`${STRATEGIES_ENDPOINT}/curve`), utils.getData(`${STRATEGIES_ENDPOINT}/balancer`), utils.getData(`${STRATEGIES_ENDPOINT}/fraxv2`), - utils.getData(`${LOCKERS_ENDPOINT}`) + utils.getData(`${LOCKERS_ENDPOINT}`), ]); const angleStrategies = resp[0]; const curveStrategies = resp[1]; @@ -74,7 +74,11 @@ const poolsFunction = async () => { apyReward = apyAngle + apySDT; } else { // calcul for lockers APR - if (strat?.aprBreakdown[2]?.isBribe || strat.key === 'apw' || strat.key === 'bpt') { + if ( + strat?.aprBreakdown[2]?.isBribe || + strat.key === 'apw' || + strat.key === 'bpt' + ) { apyReward = strat?.aprBreakdown?.reduce((acc, t) => { if (t.token.address === SDT_ADDRESS) { @@ -112,34 +116,36 @@ const poolsFunction = async () => { symbol = Object.keys(symbolMapping).includes(symbol) ? symbolMapping[symbol] : symbol.includes('san') - ? symbol.replace('san', '').split('-')[0] - : symbol.replace('FRAXBP', '-crvFRAX'); - + ? symbol.replace('san', '').split('-')[0] + : symbol.replace('FRAXBP', '-crvFRAX'); let underlyingTokens = []; if (strat?.underlyingTokens?.length > 0) { - underlyingTokens = - strat?.underlyingTokens?.map((t) => - t?.symbol === 'ETH' - ? '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' - : t?.address - ); + underlyingTokens = strat?.underlyingTokens?.map((t) => + t?.symbol === 'ETH' + ? '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' + : t?.address + ); } else if (strat?.underlyingToken?.address) { underlyingTokens = [strat?.underlyingToken?.address]; } else { underlyingTokens = []; } if (underlyingTokens.length === 0 || strat.key === 'bal') { - underlyingTokens = [strat?.tokenReceipt?.address]; } - const sdTknTknPool = ['factory-v2-109', 'factory-v2-101', 'factory-v2-239', 'b_80ldo_20weth_sdbal'] + const sdTknTknPool = [ + 'factory-v2-109', + 'factory-v2-101', + 'factory-v2-239', + 'b_80ldo_20weth_sdbal', + ]; if (sdTknTknPool.includes(strat?.key) && symbol.includes('-')) { - symbol = symbol.replaceAll('-', '') + symbol = symbol.replaceAll('-', ''); } return acc.concat([ { - pool: "sd-" + strat.key, + pool: 'sd-' + strat.key, chain: utils.formatChain('ethereum'), project: 'stakedao', symbol: utils.formatSymbol(symbol), @@ -153,11 +159,11 @@ const poolsFunction = async () => { ]); }, []); - return strats; + return strats.filter((i) => utils.keepFinite(i)); }; module.exports = { timetravel: false, apy: poolsFunction, url: 'https://lockers.stakedao.org', -}; \ No newline at end of file +}; diff --git a/src/adaptors/thena-fusion/index.js b/src/adaptors/thena-fusion/index.js new file mode 100644 index 0000000000..475d8886b0 --- /dev/null +++ b/src/adaptors/thena-fusion/index.js @@ -0,0 +1,299 @@ +const superagent = require('superagent'); +const { request, gql } = require('graphql-request'); +const sdk = require('@defillama/sdk'); +const utils = require('../utils'); +const { print } = require('graphql'); + +const EXCHANGES_API = { + thena: 'thena/', +}; +const EXCHANGES_CHAINS = { + thena: ['bsc'], +}; +const CHAINS_API = { + bsc: 'bsc/', +}; +const CHAIN_IDS = { + bsc: 56, +}; +const UNISWAP_FEE = { + 100: '0.01%', + 500: '0.05%', + 1000: '0.1%', + 3000: '0.3%', + 10000: '1%', + 0: '', +}; + +var pools_processed = []; // unique pools name +// v1 pools (read only) and private hypervisors ( non retail) +const blacklist = { + ethereum: [ + '0xd930ab15c8078ebae4ac8da1098a81583603f7ce', + '0xdbaa93e030bf2983add67c851892a9e2ee51e66a', + '0x586880065937a0b1b9541723619b75739df8ef13', + '0x33412fef1af035d6dba8b2f9b33b022e4c31dbb4', + '0xf6eeca73646ea6a5c878814e6508e87facc7927c', + '0x336d7e0a0f87e2729c0080f86801e6f4becf146f', + '0xc86b1e7fa86834cac1468937cdd53ba3ccbc1153', + '0x85cbed523459b7f6f81c11e710df969703a8a70c', + '0x7f92463e24b2ea1f7267aceed3ad68f7a956d2d8', + '0x23c85dca3d19b31f14aeea19beac32c2cb2ffc72', + '0x5230371a6d5311b1d7dd30c0f5474c2ef0a24661', + '0xc14e7ec60699a39cfd59bae06168afc2c76f32ac', + '0xbff4a47a0f77637f735e3a0ce10ff2bf9be12e89', + '0x93acb12ae1effb3426220c20c6d408eeaae59d72', + '0x65bc5c6a2630a87c2b494f36148e338dd76c054f', + '0xed354a827d99992d9cdada809449985cb73b8bb1', + '0xb666bfdb553a1aff4042c1e4f39e43852ba9731d', + '0xbb9b86a75ca3115caab045e2af17b0bba483acbc', + '0x0407c810546f1dc007f01a80e65983072d5c6dfa', + '0x4564a37c88e3b13d3a0c08832dcf88278997e6fe', + '0xd8dbdb77305898365d7ba6dd438f2663f7d4e409', + '0x33682bfc1d94480a0e3de0a565180b182b71d485', + '0x53a4512bbe5083695d8e890789fe1cf6f5686d52', + '0x09b8d86c6275e707155cdb5963cf611a432ccb21', + '0xc92ff322c8a18e38b46393dbcc8a7c5691586497', + '0x6e67bb258b6485b688cbb526c868d4428b634cf1', + '0x18d3284d9eff64fc97b64ab2b871738e684aa151', + '0x407e99b20d61f245426031df872966953909e9d3', + '0x97491b65c9c8e8754b5c55ed208ff490b2ee6190', + '0x6c8116abe5c5f2c39553c6f4217840e71462539c', + '0x716bd8a7f8a44b010969a1825ae5658e7a18630d', + '0x9a98bffabc0abf291d6811c034e239e916bbcec0', + '0xe065ff6a26f286ddb0e823920caaecd1fcd57ba1', + '0x5d40e4687e36628267854d0b985a9b6e26493b74', + '0xf0a9f5c64f80fa390a46b298791dab9e2bb29bca', + '0xe14dbb7d054ff1ff5c0cd6adac9f8f26bc7b8945', + '0xa625ea468a4c70f13f9a756ffac3d0d250a5c276', + ], + optimism: [], + polygon: [], + polygon_zkevm: [], + arbitrum: [], + celo: [], + bsc: [], + moonbeam: [], + avalanche: [], + fantom: [], + mantle: [], + base: [], + linea: [], + rollux: [], +}; +const masterchef_blacklist = { + bsc: [], +}; +const getUrl_allData = (chain, exchange) => + `https://wire2.gamma.xyz/${exchange}${chain}hypervisors/allData`; + +const getUrl_allRewards2 = (chain, exchange) => + `https://wire2.gamma.xyz/${exchange}${chain}allRewards2`; + +const pairsToObj = (pairs) => + pairs.reduce((acc, [el1, el2]) => ({ ...acc, [el1]: el2 }), {}); + +const getApy = async () => { + var hype_allData = {}; + for (const [exchange, chains] of Object.entries(EXCHANGES_CHAINS)) { + try { + let tmp_dict = pairsToObj( + await Promise.all( + Object.values(chains).map(async (chain) => [ + chain, + await utils.getData( + getUrl_allData(CHAINS_API[chain], EXCHANGES_API[exchange]) + ), + ]) + ) + ); + + Object.entries(tmp_dict).forEach(([chain, hypervisors]) => { + // include exchange to hypervisor dta + Object.entries(hypervisors).forEach(([hyp_id, hyp_dta]) => { + hyp_dta['dex'] = exchange; + }); + + if (chain in hype_allData) { + hype_allData[chain] = Object.assign(hype_allData[chain], hypervisors); + } else { + hype_allData[chain] = hypervisors; + } + }); + } catch (error) { + console.log(error); + } + + // try add rewards + try { + let tmp_rwrds_dict = await Promise.allSettled( + Object.values(chains).map(async (chain) => [ + chain, + await utils.getData( + getUrl_allRewards2(CHAINS_API[chain], EXCHANGES_API[exchange]) + ), + ]) + ).then((results) => { + results.forEach((result) => { + if (result.status == 'fulfilled') { + // result.value[0] = chain + // result.value[1] = items + Object.entries(result.value[1]).forEach(([chef_id, chef_dta]) => { + if ( + masterchef_blacklist[result.value[0]].indexOf(chef_id) == -1 + ) { + Object.entries(chef_dta['pools']).forEach(([k, v]) => { + if (k in hype_allData[result.value[0]]) { + if (!('apr_rewards2' in hype_allData[result.value[0]][k])) { + hype_allData[result.value[0]][k]['apr_rewards2'] = []; + } + hype_allData[result.value[0]][k]['apr_rewards2'].push(v); + } + }); + } + }); + } + }); + }); + } catch (error) { + console.log(error); + } + } + + const tokens = Object.entries(hype_allData).reduce( + (acc, [chain, hypervisors]) => ({ + ...acc, + [chain]: [ + ...new Set( + Object.values(hypervisors) + .map((hypervisor) => [hypervisor.token0, hypervisor.token1]) + .flat() + ), + ], + }), + {} + ); + + let keys = []; + for (const key of Object.keys(tokens)) { + keys.push(tokens[key].map((t) => `${key}:${t}`)); + } + keys = [...new Set(keys.flat())]; + + const maxSize = 50; + const pages = Math.ceil(keys.length / maxSize); + let pricesA = []; + let url = ''; + for (const p of [...Array(pages).keys()]) { + url = keys + .slice(p * maxSize, maxSize * (p + 1)) + .join(',') + .toLowerCase(); + pricesA = [ + ...pricesA, + (await superagent.get(`https://coins.llama.fi/prices/current/${url}`)) + .body.coins, + ]; + } + let prices = {}; + for (const p of pricesA) { + prices = { ...prices, ...p }; + } + + const pools = Object.keys(hype_allData).map((chain) => { + const chainAprs = Object.keys(hype_allData[chain]) + .filter(function (hypervisor_id) { + if (blacklist[chain].indexOf(hypervisor_id) >= 0) { + return false; + } else { + return true; + } + }) + .map((hypervisor_id) => { + // MAIN CALC + const hypervisor = hype_allData[chain][hypervisor_id]; + const TVL = + hypervisor.tvl0 * prices[`${chain}:${hypervisor.token0}`]?.price + + hypervisor.tvl1 * prices[`${chain}:${hypervisor.token1}`]?.price; + const apy = hypervisor['returns']['daily']['feeApy']; + const apr = hypervisor['returns']['daily']['feeApr']; + const TVL_alternative = Number(hypervisor.tvlUSD); + + // rewards + let apr_rewards2 = 0; + const rewards2_tokens = new Set(); + try { + hype_allData[chain][hypervisor_id]['apr_rewards2'].forEach((item) => { + // sum of all + apr_rewards2 += item['apr']; + // add token addresses + Object.entries(item['rewarders']).forEach(([k, v]) => { + rewards2_tokens.add(v['rewardToken']); + }); + }); + } catch (error) {} + + // create a unique pool name + var pool_name = hypervisor_id; + if (pools_processed.indexOf(pool_name) >= 0) { + pool_name = `${hypervisor_id}-${ + chain === 'polygon_zkevm' + ? 'Polygon_zkevm' + : utils.formatChain(chain) + }`; + } + pools_processed.push(pool_name); + + // create a symbol + var symbol_name = hypervisor.name; + let fee_name = ''; + // uniswap (fee%) quickswap no fee + try { + var symbol_spl = hypervisor.name.split('-'); + fee_name = `${UNISWAP_FEE[symbol_spl[symbol_spl.length - 1]]}`; + if (fee_name == ' undefined' || fee_name == 'undefined') { + fee_name = ''; + } + symbol_spl.pop(); + symbol_name = symbol_spl.join('-'); + //symbol_name = `${prices[`${chain}:${hypervisor.token0}`]?.symbol}-${prices[`${chain}:${hypervisor.token1}`]?.symbol}${fee_name}`; + if (symbol_name.includes('undefined')) { + symbol_name = hypervisor.name; + } + } catch { + symbol_name = hypervisor.name; + } + + return { + pool: `${pool_name}-Binance`, + chain: utils.formatChain(chain), + project: 'thena-fusion', + symbol: `${symbol_name}`, + tvlUsd: TVL || TVL_alternative, + apyBase: apr * 100 || apy * 100, + apyReward: apr_rewards2 * 100, + rewardTokens: [...rewards2_tokens], + underlyingTokens: [hypervisor.token0, hypervisor.token1], + poolMeta: fee_name, + }; + }); + return chainAprs; + }); + + // pools on optimism which have wrong reward apy + const x = [ + '0x431f6e577a431d9ee87a535fde2db830e352e33c', + '0xed17209ab7f9224e29cc9894fa14a011f37b6115', + ]; + return pools.flat().map((i) => ({ + ...i, + apyReward: x.includes(i.pool) ? null : i.apyReward, + rewardTokens: x.includes(i.pool) ? null : i.rewardTokens, + })).filter(i => i.chain === 'Binance'); +}; + +module.exports = { + timetravel: false, + apy: getApy, + url: 'https://app.gamma.xyz/dashboard', +}; diff --git a/src/adaptors/vela-exchange/index.js b/src/adaptors/vela-exchange/index.js index 7186e4de46..45c70bd09e 100644 --- a/src/adaptors/vela-exchange/index.js +++ b/src/adaptors/vela-exchange/index.js @@ -23,29 +23,25 @@ const TOKEN_FARM_ADDRESS = { ['arbitrum']: '0x60b8C145235A31f1949a831803768bF37d7Ab7AA', ['base']: '0x00B01710c2098b883C4F93dD093bE8Cf605a7BDe', }; + const RPC = { ['arbitrum']: 'https://arbitrum.llamarpc.com', ['base']: 'https://mainnet.base.org', }; + const chains = ['arbitrum', 'base']; +const VAULT_START_TIME = { + ['arbitrum']: new Date(Date.UTC(2023, 5, 26, 4, 38)).getTime(), + ['base']: new Date(Date.UTC(2023, 8, 5, 15, 0)).getTime(), +}; + const poolsFunction = async () => { const pools = []; const secondsPerYear = 31536000; for (let i = 0; i < chains.length; i++) { const chain = chains[i]; - // 14 days - let distance = await axios( - `https://coins.llama.fi/block/${chain}/${Math.floor( - new Date(new Date(Date.now() - 12096e5)).getTime() / 1000 - )}` - ); - - sdk.api.config.setProvider( - `${chain}`, - new ethers.providers.JsonRpcProvider(`${RPC[chain]}`) - ); const velaPrice = ( await utils.getPrices( @@ -54,13 +50,21 @@ const poolsFunction = async () => { ) ).pricesBySymbol.vela; - const current = ( - await sdk.api.abi.call({ - target: VAULT_ADDRESS, - abi: VaultABI.filter(({ name }) => name === 'getVLPPrice')[0], - chain: `${chain}`, - }) - ).output; + sdk.api.config.setProvider( + `${chain}`, + new ethers.providers.JsonRpcProvider(`${RPC[chain]}`) + ); + + const current = ethers.utils.formatUnits( + ( + await sdk.api.abi.call({ + target: VAULT_ADDRESS, + abi: VaultABI.filter(({ name }) => name === 'getVLPPrice')[0], + chain: `${chain}`, + }) + ).output, + 5 + ); const totalSupply = ( await sdk.api.abi.call({ @@ -70,18 +74,25 @@ const poolsFunction = async () => { }) ).output; - const historical = ( - await sdk.api.abi.call({ - target: VAULT_ADDRESS, - abi: VaultABI.filter(({ name }) => name === 'getVLPPrice')[0], - chain: `${chain}`, - block: distance.data.height, - }) - ).output; + const diff = current - 1; + const now = new Date(); + const dayDiff = Math.floor( + (new Date( + Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate(), + now.getUTCHours(), + now.getUTCMinutes() + ) + ).getTime() - + VAULT_START_TIME[chain]) / + (24 * 60 * 60 * 1000) + ); + /// APR Calculations /// + const APR = 365 * (diff / 1 / dayDiff) * 100; - const rewardsPerSecond = ethers.utils.formatEther( - ( - await sdk.api.abi.call({ + const rewardsPerSecond = (await sdk.api.abi.call({ target: TOKEN_FARM_ADDRESS[chain], abi: TokenFarmABI.filter( ({ name }) => name === 'poolRewardsPerSec' @@ -89,8 +100,7 @@ const poolsFunction = async () => { chain: `${chain}`, params: [0], }) - ).output[3].toString() - ); + ).output const poolTotal = ethers.utils.formatEther( ( @@ -103,16 +113,22 @@ const poolsFunction = async () => { ).output ); - const diff = current - historical; - const APR = 365 * (diff / historical / 14) * 100; - - if (poolTotal > 0) { - VELA_APR = - ((rewardsPerSecond * secondsPerYear * velaPrice) / - (poolTotal * parseFloat(ethers.utils.formatUnits(current, 5)))) * + let rewardTokens = [VELA_ADDRESS[chain]] + let apyReward = + ((ethers.utils.formatEther(rewardsPerSecond.rewardsPerSec[0]) * secondsPerYear * velaPrice) / + (poolTotal * current)) * + 100; + + for (let i = 1; i < rewardsPerSecond.addresses.length; i++) { + rewardTokens.push(rewardsPerSecond.addresses[i]) + const price = ( + await utils.getPrices([rewardsPerSecond.addresses[i]], chain) + ).pricesByAddress + + apyReward += + ((ethers.utils.formatEther(rewardsPerSecond.rewardsPerSec[i]) * secondsPerYear * Object.values(price)[0]) / + (poolTotal * current)) * 100; - } else { - VELA_APR = 0; } const VLPPool = { @@ -121,13 +137,12 @@ const poolsFunction = async () => { project: 'vela-exchange', symbol: 'USDC', poolMeta: 'VLP', - tvlUsd: (Number(totalSupply) / 1e18) * (Number(current) / 1e5), + tvlUsd: (Number(totalSupply) / 1e18) * (Number(current)), apyBase: APR, underlyingTokens: [BRIDGED_USDC_ADDRESS[chain]], - rewardTokens: [VELA_ADDRESS[chain]], - apyReward: VELA_APR, - poolMeta: - 'Reward tokens are issued after staking VLP and rewards are in $esVela. Users can deposit $esVELA into the vesting contract in order to claim the same amount of $VELA. This is unlocked continuously throughout a one year (365 days) linear vesting period.', + rewardTokens, + apyReward, + poolMeta: 'VLP', }; pools.push(VLPPool); diff --git a/src/adaptors/yieldlend/index.js b/src/adaptors/yieldlend/index.js new file mode 100644 index 0000000000..5004c868ce --- /dev/null +++ b/src/adaptors/yieldlend/index.js @@ -0,0 +1,261 @@ +const superagent = require('superagent'); +const { request, gql } = require('graphql-request'); +const sdk = require('@defillama/sdk4'); + +const utils = require('../utils'); +const { aTokenAbi } = require('../aave-v3/abi'); +const poolAbi = require('../aave-v3/poolAbi'); + +const SECONDS_PER_YEAR = 31536000; + +const chainUrlParam = { + base: 'proto_base_v3', +}; + +const oraclePriceABI = { + inputs: [ + { + internalType: 'address', + name: 'asset', + type: 'address', + }, + ], + name: 'getAssetPrice', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', +}; + +const getPrices = async (addresses) => { + const yieldAddr = '0x3f7a11bb98959966260347233bfe6559a1067dbf'; + const aaveOracleAddr = '0x0B9252d63cb44eFa7f18911Ee2259cB40d0c2965'; + + const _prices = ( + await superagent.get( + `https://coins.llama.fi/prices/current/${addresses + .join(',') + .toLowerCase()}` + ) + ).body.coins; + + const oraclePrice = ( + await sdk.api.abi.call({ + target: aaveOracleAddr, + abi: oraclePriceABI, + chain: 'base', + params: [yieldAddr], + }) + ).output; + + const _yield = { + [`base:${yieldAddr}`]: { + decimals: 18, + symbol: 'YIELD', + price: Number(oraclePrice) / 1e8, + timestamp: Date.now(), + confidence: 0.99, + }, + }; + + const prices = { ..._prices, ..._yield }; + + const pricesBySymbol = Object.entries(prices).reduce( + (acc, [name, price]) => ({ + ...acc, + [price.symbol.toLowerCase()]: price.price, + }), + {} + ); + + const pricesByAddress = Object.entries(prices).reduce( + (acc, [name, price]) => ({ + ...acc, + [name.split(':')[1]]: price.price, + }), + {} + ); + + return { pricesByAddress, pricesBySymbol }; +}; + +const API_URLS = { + base: 'https://api.studio.thegraph.com/query/60668/yieldlend/version/latest', +}; + +const query = gql` + query ReservesQuery { + reserves(where: { name_not: "" }) { + name + borrowingEnabled + aToken { + id + rewards(where: { distributionEnd_gt: 0 }) { + id + emissionsPerSecond + rewardToken + rewardTokenDecimals + rewardTokenSymbol + distributionEnd + } + underlyingAssetAddress + underlyingAssetDecimals + } + vToken { + rewards(where: { distributionEnd_gt: 0 }) { + emissionsPerSecond + rewardToken + rewardTokenDecimals + rewardTokenSymbol + distributionEnd + } + } + symbol + liquidityRate + variableBorrowRate + baseLTVasCollateral + isFrozen + } + } +`; + +const apy = async () => { + let data = await Promise.all( + Object.entries(API_URLS).map(async ([chain, url]) => [ + chain, + (await request(url, query)).reserves, + ]) + ); + + data = data.map(([chain, reserves]) => [ + chain, + reserves.filter((p) => !p.isFrozen), + ]); + + const totalSupply = await Promise.all( + data.map(async ([chain, reserves]) => + ( + await sdk.api.abi.multiCall({ + chain: chain, + abi: aTokenAbi.find(({ name }) => name === 'totalSupply'), + calls: reserves.map((reserve) => ({ + target: reserve.aToken.id, + })), + }) + ).output.map(({ output }) => output) + ) + ); + + const underlyingBalances = await Promise.all( + data.map(async ([chain, reserves]) => + ( + await sdk.api.abi.multiCall({ + chain: chain, + abi: aTokenAbi.find(({ name }) => name === 'balanceOf'), + calls: reserves.map((reserve, i) => ({ + target: reserve.aToken.underlyingAssetAddress, + params: [reserve.aToken.id], + })), + }) + ).output.map(({ output }) => output) + ) + ); + + const underlyingTokens = data.map(([chain, reserves]) => + reserves.map((pool) => `${chain}:${pool.aToken.underlyingAssetAddress}`) + ); + + const rewardTokens = data.map(([chain, reserves]) => + reserves.map((pool) => + pool.aToken.rewards.map((rew) => `${chain}:${rew.rewardToken}`) + ) + ); + + const { pricesByAddress, pricesBySymbol } = await getPrices( + underlyingTokens.flat().concat(rewardTokens.flat(Infinity)) + ); + + const pools = data.map(([chain, markets], i) => { + const chainPools = markets.map((pool, idx) => { + const supply = totalSupply[i][idx]; + const currentSupply = underlyingBalances[i][idx]; + const totalSupplyUsd = + (supply / 10 ** pool.aToken.underlyingAssetDecimals) * + (pricesByAddress[pool.aToken.underlyingAssetAddress] || + pricesBySymbol[pool.symbol]); + const tvlUsd = + (currentSupply / 10 ** pool.aToken.underlyingAssetDecimals) * + (pricesByAddress[pool.aToken.underlyingAssetAddress] || + pricesBySymbol[pool.symbol]); + const { rewards } = pool.aToken; + + const rewardPerYear = rewards.reduce( + (acc, rew) => + acc + + (rew.emissionsPerSecond / 10 ** rew.rewardTokenDecimals) * + SECONDS_PER_YEAR * + pricesByAddress[rew.rewardToken] || + pricesBySymbol[rew.rewardTokenSymbol], + 0 + ); + + const { rewards: rewardsBorrow } = pool.vToken; + const rewardPerYearBorrow = rewardsBorrow.reduce( + (acc, rew) => + acc + + (rew.emissionsPerSecond / 10 ** rew.rewardTokenDecimals) * + SECONDS_PER_YEAR * + pricesByAddress[rew.rewardToken] || + pricesBySymbol[rew.rewardTokenSymbol], + 0 + ); + let totalBorrowUsd = totalSupplyUsd - tvlUsd; + totalBorrowUsd = totalBorrowUsd < 0 ? 0 : totalBorrowUsd; + + const supplyRewardEnd = pool.aToken.rewards[0]?.distributionEnd; + const borrowRewardEnd = pool.vToken.rewards[0]?.distributionEnd; + + return { + pool: `${pool.aToken.id}-${chain}`.toLowerCase(), + chain: utils.formatChain('base'), + project: 'yieldlend', + symbol: pool.symbol, + tvlUsd, + apyBase: (pool.liquidityRate / 10 ** 27) * 100, + apyReward: + supplyRewardEnd * 1000 > new Date() + ? (rewardPerYear / totalSupplyUsd) * 100 + : null, + rewardTokens: + supplyRewardEnd * 1000 > new Date() + ? rewards.map((rew) => rew.rewardToken) + : null, + underlyingTokens: [pool.aToken.underlyingAssetAddress], + totalSupplyUsd, + totalBorrowUsd, + apyBaseBorrow: Number(pool.variableBorrowRate) / 1e25, + apyRewardBorrow: + borrowRewardEnd * 1000 > new Date() + ? (rewardPerYearBorrow / totalBorrowUsd) * 100 + : null, + ltv: Number(pool.baseLTVasCollateral) / 10000, + url: `https://use.yieldlend.xyz/reserve-overview/?underlyingAsset=${pool.aToken.underlyingAssetAddress}&marketName=${chainUrlParam[chain]}&utm_source=defillama&utm_medium=listing&utm_campaign=external`, + borrowable: pool.borrowingEnabled, + }; + }); + + return chainPools; + }); + + return pools.flat().filter((p) => !!p.tvlUsd); +}; + +module.exports = { + timetravel: false, + apy, +}; diff --git a/src/handlers/triggerLSDRates.js b/src/handlers/triggerLSDRates.js index d25e2c1a88..7effb2f6b5 100644 --- a/src/handlers/triggerLSDRates.js +++ b/src/handlers/triggerLSDRates.js @@ -81,8 +81,8 @@ const lsdTokens = [ { name: 'Bifrost Liquid Staking', symbol: 'vETH', - // address: '0x4Bc3263Eb5bb2Ef7Ad9aB6FB68be80E43b43801F', // vETH - address: '0x74bAA141B18D5D1eeF1591abf37167FbeCE23B72', // Staking Liquidity Protocol Contract + address: '0x4Bc3263Eb5bb2Ef7Ad9aB6FB68be80E43b43801F', // vETH + addressExchangeRate: '0x74bAA141B18D5D1eeF1591abf37167FbeCE23B72', // Staking Liquidity Protocol Contract type: a, }, { @@ -116,8 +116,8 @@ const lsdTokens = [ { name: 'Tranchess Ether', symbol: 'qETH', - // address: '0x93ef1Ea305D11A9b2a3EbB9bB4FCc34695292E7d', // qETH - address: '0xA6aeD7922366611953546014A3f9e93f058756a2', // QueenRateProvider + address: '0x93ef1Ea305D11A9b2a3EbB9bB4FCc34695292E7d', // qETH + addressExchangeRate: '0xA6aeD7922366611953546014A3f9e93f058756a2', // QueenRateProvider type: a, // fee: 0.1, }, @@ -125,21 +125,30 @@ const lsdTokens = [ name: 'Stakehouse', symbol: 'dETH', address: '0x3d1e5cf16077f349e999d6b21a4f646e83cd90c5', - type: r, + type: a, fee: 0, }, { name: 'Stader', symbol: 'ETHx', - address: '0xcf5EA1b38380f6aF39068375516Daf40Ed70D299', + address: '0xA35b1B31Ce002FBF2058D22F30f95D405200A15b', + addressExchangeRate: '0xcf5EA1b38380f6aF39068375516Daf40Ed70D299', type: a, fee: 0.1, }, { name: 'NodeDAO', symbol: 'nETH', - // address: '0xC6572019548dfeBA782bA5a2093C836626C7789A', - address: '0x8103151E2377e78C04a3d2564e20542680ed3096', + address: '0xC6572019548dfeBA782bA5a2093C836626C7789A', + addressExchangeRate: '0x8103151E2377e78C04a3d2564e20542680ed3096', + type: a, + fee: 0.1, + }, + { + name: 'Bedrock uniETH', + symbol: 'uniETH', + address: '0xF1376bceF0f78459C0Ed0ba5ddce976F1ddF51F4', + addressExchangeRate: '0x4beFa2aA9c305238AA3E0b5D17eB20C045269E9d', type: a, fee: 0.1, }, @@ -309,6 +318,14 @@ const getExpectedRates = async () => { type: 'function', }; + const uniETHAbi = { + inputs: [], + name: 'exchangeRatio', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }; + // --- cbETH const cbETHRate = Number((await axios.get(cbETHRateUrl)).data.amount); @@ -401,7 +418,8 @@ const getExpectedRates = async () => { const qETH = ( await sdk.api.abi.call({ - target: lsdTokens.find((lsd) => lsd.name === 'Tranchess Ether').address, + target: lsdTokens.find((lsd) => lsd.name === 'Tranchess Ether') + .addressExchangeRate, chain: 'ethereum', abi: qETHAbi, }) @@ -411,7 +429,7 @@ const getExpectedRates = async () => { ( await sdk.api.abi.call({ target: lsdTokens.find((lsd) => lsd.name === 'Bifrost Liquid Staking') - .address, + .addressExchangeRate, chain: 'ethereum', abi: vETHAbi, params: [BigInt(1e18)], @@ -421,7 +439,8 @@ const getExpectedRates = async () => { const ETHx = ( await sdk.api.abi.call({ - target: lsdTokens.find((lsd) => lsd.name === 'Stader').address, + target: lsdTokens.find((lsd) => lsd.name === 'Stader') + .addressExchangeRate, chain: 'ethereum', abi: ETHxAbi, }) @@ -430,12 +449,23 @@ const getExpectedRates = async () => { const nETH = ( await sdk.api.abi.call({ - target: lsdTokens.find((lsd) => lsd.name === 'NodeDAO').address, + target: lsdTokens.find((lsd) => lsd.name === 'NodeDAO') + .addressExchangeRate, chain: 'ethereum', abi: nETHAbi, }) ).output / 1e18; + const uniETH = + ( + await sdk.api.abi.call({ + target: lsdTokens.find((lsd) => lsd.name === 'Bedrock uniETH') + .addressExchangeRate, + chain: 'ethereum', + abi: uniETHAbi, + }) + ).output / 1e18; + return lsdTokens.map((lsd) => ({ ...lsd, expectedRate: @@ -461,8 +491,10 @@ const getExpectedRates = async () => { ? vETH : lsd.name === 'Stader' ? ETHx - : lsd.name === 'NodeDao' + : lsd.name === 'NodeDAO' ? nETH + : lsd.name === 'Bedrock uniETH' + ? uniETH : 1, })); }; diff --git a/src/utils/exclude.js b/src/utils/exclude.js index 04a213e2f9..25722f10ec 100644 --- a/src/utils/exclude.js +++ b/src/utils/exclude.js @@ -38,7 +38,10 @@ const excludeAdaptors = [ 'vendor-v1', // empty 'uncx-network-v2', 'rocifi-v2', - 'steadefi', // hacked + 'steadefi', // hacked, + 'bank-of-chain', + 'zunami-protocol', // hacked + 'myso-v1', ]; const excludePools = [