def calculate_apy(gauge, swap): crv_price = uniswap.price_router(crv, uniswap.usdc) gauge_controller = interface.CurveGaugeController(gauge.controller()) working_supply = gauge.working_supply() / 1e18 relative_weight = gauge_controller.gauge_relative_weight(gauge) / 1e18 inflation_rate = gauge.inflation_rate() / 1e18 virtual_price = swap.get_virtual_price() / 1e18 base_price = 1 if str(swap) in constants.CURVE_BTC_SWAPS: base_price *= uniswap.price_router(uniswap.wbtc, uniswap.usdc) try: rate = (inflation_rate * relative_weight * 86400 * 365 / working_supply * 0.4) / (virtual_price * base_price) except ZeroDivisionError: rate = 0 return { 'crv price': crv_price, 'relative weight': relative_weight, 'inflation rate': inflation_rate, 'virtual price': virtual_price, 'base price': base_price, 'crv reward rate': rate, 'crv apy': rate * crv_price, }
def calculate_apy(gauge, swap): crv_price = uniswap.price_router(crv, uniswap.usdc) gauge_controller = interface.CurveGaugeController(gauge.controller()) working_supply = gauge.working_supply() / 1e18 relative_weight = gauge_controller.gauge_relative_weight(gauge) / 1e18 inflation_rate = gauge.inflation_rate() / 1e18 virtual_price = swap.get_virtual_price() / 1e18 base_price = get_base_price(swap) try: rate = (inflation_rate * relative_weight * 86400 * 365 / working_supply * 0.4) / (virtual_price * base_price) except ZeroDivisionError: rate = 0 return { "crv price": crv_price, "relative weight": relative_weight, "inflation rate": inflation_rate, "virtual price": virtual_price, "base price": base_price, "crv reward rate": rate, "crv apy": rate * crv_price, "token price": base_price * virtual_price, }
def simple(vault: Union[VaultV1, VaultV2], samples: ApySamples) -> Apy: lp_token = vault.token.address pool_address = get_pool(lp_token) gauge_addresses = curve_registry.get_gauges(pool_address) gauge_address = gauge_addresses[0][0] # FIXME: crvUSDP doesn't have a gauge connected in the registry if vault.vault.address == "0x1B5eb1173D2Bf770e50F10410C9a96F7a8eB6e75": gauge = "0x055be5DDB7A925BfEF3417FC157f53CA77cA7222" gauge = interface.CurveGauge(gauge_address) controller = gauge.controller() controller = interface.CurveGaugeController(controller) gauge_working_supply = gauge.working_supply() gauge_weight = controller.gauge_relative_weight(gauge_address) gauge_inflation_rate = gauge.inflation_rate() pool_price = curve_registry.get_virtual_price_from_lp_token(lp_token) underlying_coins = get_underlying_coins(lp_token) btc_like = any([coin in BTC_LIKE for coin in underlying_coins]) eth_like = any([coin in ETH_LIKE for coin in underlying_coins]) base_asset = WBTC if btc_like else WETH if eth_like else underlying_coins[0] base_asset_price = get_price(base_asset) or 1 crv_price = get_price(CRV) y_working_balance = gauge.working_balances(YVECRV_VOTER) y_gauge_balance = gauge.balanceOf(YVECRV_VOTER) base_apr = ( gauge_inflation_rate * gauge_weight * (SECONDS_PER_YEAR / gauge_working_supply) * (PER_MAX_BOOST / pool_price) * crv_price ) / base_asset_price if y_gauge_balance > 0: boost = y_working_balance / (PER_MAX_BOOST * y_gauge_balance) or 1 else: boost = MAX_BOOST # FIXME: The HBTC v1 vault is currently still earning yield, but it is no longer boosted. if vault.vault.address == "0x46AFc2dfBd1ea0c0760CAD8262A5838e803A37e5": boost = 1 boosted_apr = base_apr * boost if hasattr(gauge, "reward_contract"): try: reward_address = gauge.reward_contract() assert reward_address != ZERO_ADDRESS reward_apr = rewards(reward_address, pool_price, base_asset_price) except ValueError: reward_apr = 0 else: reward_apr = 0 price_per_share = curve_registry.get_virtual_price_from_lp_token now_price = price_per_share(lp_token, block_identifier=samples.now) try: week_ago_price = price_per_share(lp_token, block_identifier=samples.week_ago) except ValueError: raise ApyError("crv", "insufficient data") now_point = SharePricePoint(samples.now, now_price) week_ago_point = SharePricePoint(samples.week_ago, week_ago_price) pool_apr = calculate_roi(now_point, week_ago_point) pool_apy = (((pool_apr / 365) + 1) ** 365) - 1 # FIXME: crvANKR's pool apy going crazy if vault.vault.address == "0xE625F5923303f1CE7A43ACFEFd11fd12f30DbcA4": pool_apy = 0 if type(vault) is VaultV2: contract = vault.vault keep_crv = sum([strategy.keepCRV() for strategy in vault.strategies if hasattr(strategy, "keepCRV")]) performance = (contract.performanceFee() * 2) if hasattr(contract, "performanceFee") else 0 management = contract.managementFee() if hasattr(contract, "managementFee") else 0 else: strategy = vault.strategy strategist_performance = strategy.performanceFee() if hasattr(strategy, "performanceFee") else 0 strategist_reward = strategy.strategistReward() if hasattr(strategy, "strategistReward") else 0 treasury = strategy.treasuryFee() if hasattr(strategy, "treasuryFee") else 0 keep_crv = strategy.keepCRV() if hasattr(strategy, "keepCRV") else 0 performance = strategist_reward + strategist_performance + treasury management = 0 keep_crv /= 1e4 performance /= 1e4 management /= 1e4 lose_crv = 1 - keep_crv gross_farmed_apy = ( (boosted_apr * keep_crv) + (((boosted_apr * lose_crv + reward_apr) / COMPOUNDING) + 1) ** COMPOUNDING ) - 1 apy = (gross_farmed_apy + 1) * (pool_apy + 1) - 1 net_curve_apr = (boosted_apr * lose_crv + reward_apr) * (1 - performance) - management net_farmed_apy = ((net_curve_apr / COMPOUNDING) + 1) ** COMPOUNDING - 1 net_apy = (net_farmed_apy + 1) * (pool_apy + 1) - 1 fees = ApyFees(performance=performance, management=management, keep_crv=keep_crv) composite = { "boost": boost, "pool_apy": pool_apy, "boosted_apr": boosted_apr, "base_apr": base_apr, "rewards_apr": reward_apr, } return Apy("crv", apy, net_apy, fees, composite=composite)
from brownie import interface from yearn import constants from yearn import uniswap crv = interface.ERC20('0xD533a949740bb3306d119CC777fa900bA034cd52') gauge_controller = interface.CurveGaugeController( '0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB') voting_escrow = interface.CurveVotingEscrow( '0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2') def calculate_boost(gauge, addr): gauge_balance = gauge.balanceOf(addr) / 1e18 gauge_total = gauge.totalSupply() / 1e18 working_balance = gauge.working_balances(addr) / 1e18 working_supply = gauge.working_supply() / 1e18 vecrv_balance = voting_escrow.balanceOf(addr) / 1e18 vecrv_total = voting_escrow.totalSupply() / 1e18 boost = working_balance / gauge_balance * 2.5 min_vecrv = vecrv_total * gauge_balance / gauge_total lim = gauge_balance * 0.4 + gauge_total * min_vecrv / vecrv_total * 0.6 lim = min(gauge_balance, lim) _working_supply = working_supply + lim - working_balance noboost_lim = gauge_balance * 0.4 noboost_supply = working_supply + noboost_lim - working_balance max_boost_possible = (lim / _working_supply) / (noboost_lim / noboost_supply)