def apy(self, _: ApySamples) -> Apy: curve_3_pool = contract("0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7") curve_reward_distribution = contract( "0xA464e6DCda8AC41e03616F95f4BC98a13b8922Dc") curve_voting_escrow = contract( "0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2") voter = "0xF147b8125d2ef93FB6965Db97D6746952a133934" crv_price = magic.get_price( "0xD533a949740bb3306d119CC777fa900bA034cd52") yvecrv_price = magic.get_price( "0xc5bDdf9843308380375a611c18B50Fb9341f502A") total_vecrv = curve_voting_escrow.totalSupply() yearn_vecrv = curve_voting_escrow.balanceOf(voter) vault_supply = self.vault.totalSupply() week = 7 * 86400 epoch = math.floor(time() / week) * week - week tokens_per_week = curve_reward_distribution.tokens_per_week( epoch) / 1e18 virtual_price = curve_3_pool.get_virtual_price() / 1e18 # although we call this APY, this is actually APR since there is no compounding apy = (tokens_per_week * virtual_price * 52) / ( (total_vecrv / 1e18) * crv_price) vault_boost = (yearn_vecrv / vault_supply) * (crv_price / yvecrv_price) composite = { "currentBoost": vault_boost, "boostedApy": apy * vault_boost, "totalApy": apy * vault_boost, "poolApy": apy, "baseApy": apy, } return Apy("backscratcher", apy, apy, ApyFees(), composite=composite)
def calculate_apy(self, gauge, lp_token, block=None): crv_price = magic.get_price(self.crv) pool = contract(self.get_pool(lp_token)) results = fetch_multicall( [gauge, "working_supply"], [self.gauge_controller, "gauge_relative_weight", gauge], [gauge, "inflation_rate"], [pool, "get_virtual_price"], block=block, ) results = [x / 1e18 for x in results] working_supply, relative_weight, inflation_rate, virtual_price = results token_price = magic.get_price(lp_token, block=block) try: rate = (inflation_rate * relative_weight * 86400 * 365 / working_supply * 0.4) / token_price except ZeroDivisionError: rate = 0 return { "crv price": crv_price, "relative weight": relative_weight, "inflation rate": inflation_rate, "virtual price": virtual_price, "crv reward rate": rate, "crv apy": rate * crv_price, "token price": token_price, }
def staking(address: str, pool_price: int, base_asset_price: int, block: Optional[int] = None) -> float: staking_rewards = contract(address) end = staking_rewards.periodFinish(block_identifier=block) current_time = time() if block is None else get_block_timestamp(block) if end < current_time: return 0 snx_address = staking_rewards.snx( block_identifier=block) if hasattr(staking_rewards, "snx") else None reward_token = staking_rewards.rewardToken( block_identifier=block) if hasattr(staking_rewards, "rewardToken") else None rewards_token = staking_rewards.rewardsToken( block_identifier=block) if hasattr(staking_rewards, "rewardsToken") else None token = reward_token or rewards_token or snx_address total_supply = staking_rewards.totalSupply( block_identifier=block) if hasattr(staking_rewards, "totalSupply") else 0 rate = staking_rewards.rewardRate(block_identifier=block) if hasattr( staking_rewards, "rewardRate") else 0 if token and rate: # Single reward token token_price = get_price(token, block=block) return (SECONDS_PER_YEAR * (rate / 1e18) * token_price) / ( (pool_price / 1e18) * (total_supply / 1e18) * base_asset_price) else: # Multiple reward tokens queue = 0 apr = 0 try: token = staking_rewards.rewardTokens(queue, block_identifier=block) except ValueError: token = None while token and token != ZERO_ADDRESS: try: data = staking_rewards.rewardData(token, block_identifier=block) except ValueError: token = None rate = data.rewardRate / 1e18 if data else 0 token_price = get_price(token, block=block) or 0 apr += SECONDS_PER_YEAR * rate * token_price / ( (pool_price / 1e18) * (total_supply / 1e18) * token_price) queue += 1 try: token = staking_rewards.rewardTokens(queue, block_identifier=block) except ValueError: token = None return apr
def staking(address: str, pool_price: int, base_asset_price: int) -> float: staking_rewards = Contract(address) end = staking_rewards.periodFinish() if end < time(): return 0 snx_address = staking_rewards.snx() if hasattr(staking_rewards, "snx") else None reward_token = staking_rewards.rewardToken() if hasattr( staking_rewards, "rewardToken") else None rewards_token = staking_rewards.rewardsToken() if hasattr( staking_rewards, "rewardsToken") else None token = reward_token or rewards_token or snx_address total_supply = staking_rewards.totalSupply() if hasattr( staking_rewards, "totalSupply") else 0 rate = staking_rewards.rewardRate() if hasattr(staking_rewards, "rewardRate") else 0 if token and rate: # Single reward token token_price = get_price(token) return (SECONDS_PER_YEAR * (rate / 1e18) * token_price) / ( (pool_price / 1e18) * (total_supply / 1e18) * base_asset_price) else: # Multiple reward tokens queue = 0 apr = 0 try: token = staking_rewards.rewardTokens(queue) except ValueError: token = None while token and token != ZERO_ADDRESS: try: data = staking_rewards.rewardData(token) except ValueError: token = None rate = data.rewardRate / 1e18 if data else 0 token_price = get_price(token) or 0 apr += SECONDS_PER_YEAR * rate * token_price / ( (pool_price / 1e18) * (total_supply / 1e18) * token_price) queue += 1 try: token = staking_rewards.rewardTokens(queue) except ValueError: token = None return apr
def multi(address: str, pool_price: int, base_asset_price: int, block: Optional[int] = None) -> float: multi_rewards = contract(address) total_supply = multi_rewards.totalSupply( block_identifier=block) if hasattr(multi_rewards, "totalSupply") else 0 queue = 0 apr = 0 if hasattr(multi_rewards, "rewardsToken"): token = multi_rewards.rewardTokens(queue, block_identifier=block) else: token = None while token and token != ZERO_ADDRESS: try: data = multi_rewards.rewardData(token, block_identifier=block) except ValueError: token = None if data.periodFinish >= time(): rate = data.rewardRate / 1e18 if data else 0 token_price = get_price(token, block=block) or 0 apr += SECONDS_PER_YEAR * rate * token_price / ( (pool_price / 1e18) * (total_supply / 1e18) * token_price) queue += 1 try: token = multi_rewards.rewardTokens(queue, block_identifier=block) except ValueError: token = None return apr
def describe(self, block=None): try: results = fetch_multicall(*[[self.vault, view] for view in self._views], block=block) info = dict(zip(self._views, results)) for name in info: if name in VAULT_VIEWS_SCALED: info[name] /= self.scale info["strategies"] = {} except ValueError as e: info = {"strategies": {}} for strategy in self.strategies: info["strategies"][strategy.unique_name] = strategy.describe( block=block) info["token price"] = magic.get_price(self.token, block=block) if "totalAssets" in info: info["tvl"] = info["token price"] * info["totalAssets"] info["experimental"] = self.is_experiment info["address"] = self.vault info["version"] = "v2" return info
def appendmore(token_address, amount, wallet, category=False, wallet_label=False): token = contract(token_address) price = magic.get_price(token_address, block) if category and not wallet_label: return df.append( pd.DataFrame({ 'token_address': [token.address], 'name': [token.name()], 'symbol': [token.symbol()], 'balance': [amount], 'category': [category], 'wallet': [wallet], 'wallet_label': [YEARN_WALLETS[wallet]], 'price': [price], 'value': [amount * price], })) if wallet_label and not category: return df.append( pd.DataFrame({ 'token_address': [token.address], 'name': [token.name()], 'symbol': [token.symbol()], 'balance': [amount], 'category': [CATEGORY_MAPPINGS[token.address]], 'wallet': [wallet], 'wallet_label': [wallet_label], 'price': [price], 'value': [amount * price], })) if wallet_label and category: return df.append( pd.DataFrame({ 'token_address': [token.address], 'name': [token.name()], 'symbol': [token.symbol()], 'balance': [amount], 'category': [category], 'wallet': [wallet], 'wallet_label': [wallet_label], 'price': [price], 'value': [amount * price], })) return df.append( pd.DataFrame({ 'token_address': [token.address], 'name': [token.name()], 'symbol': [token.symbol()], 'balance': [amount], 'category': [CATEGORY_MAPPINGS[token.address]], 'wallet': [wallet], 'wallet_label': [YEARN_WALLETS[wallet]], 'price': [price], 'value': [amount * price], }))
def tvl(self, block=None): total_assets = self.vault.totalAssets(block_identifier=block) try: price = magic.get_price(self.token, block=None) except PriceError: price = None tvl = total_assets * price / 10**self.vault.decimals( block_identifier=block) if price else None return Tvl(total_assets, price, tvl)
def describe(self, block=None): crv_locked = curve.voting_escrow.balanceOf["address"]( self.proxy, block_identifier=block) / 1e18 crv_price = magic.get_price(curve.crv, block=block) return { 'totalSupply': crv_locked, 'token price': crv_price, 'tvl': crv_locked * crv_price, }
def describe(self, block=None): yfi_locked = self.token.balanceOf(self.vault, block_identifier=block) / 1e18 yfi_price = magic.get_price(str(self.token), block=block) return { 'totalAssets': yfi_locked, 'token price': yfi_price, 'tvl': yfi_locked * yfi_price, }
def get_tvl(self, pool, block=None): """ Get total value in Curve pool. """ balances = self.get_balances(pool, block=block) if balances is None: return None return sum(balances[coin] * magic.get_price(coin, block=block) for coin in balances)
def test_crypto_pool_eur_usd_assets(pool): lp_token = eur_usd_crypto_pool_tokens[pool] pool = curve.curve.crypto_swap_registry.get_pool_from_lp_token(lp_token) coins = curve.curve.crypto_swap_registry.get_coins(pool) non_zero_coins = list(filter(lambda coin: coin != ZERO_ADDRESS, coins)) underlying_coin_prices = map(lambda coin: get_price(coin), non_zero_coins) summed_coin_prices = sum(underlying_coin_prices) price = curve.curve.get_price(lp_token) # the price of the crypto pool token should be approximately equal to the sum of the # underlying coins, provided they are roughly equally balanced in the pool assert price == pytest.approx(summed_coin_prices, rel=2e-1)
def _get_price(event, vault): while True: try: try: return magic.get_price(vault.address, event.block_number) except TypeError: # magic.get_price fails because all liquidity was removed for testing and `share_price` returns None return magic.get_price(vault.token(), event.block_number) except ConnectionError as e: # Try again print(f'ConnectionError: {str(e)}') time.sleep(1) except ValueError as e: print(f'ValueError: {str(e)}') if str(e) in [ "Failed to retrieve data from API: {'status': '0', 'message': 'NOTOK', 'result': 'Max rate limit reached'}", "Failed to retrieve data from API: {'status': '0', 'message': 'NOTOK', 'result': 'Max rate limit reached, please use API Key for higher rate limit'}", ]: # Try again print('trying again...') time.sleep(5) else: print(f'vault: {vault.address}') raise Exception(str(e))
def unit_collateral(self, block=None) -> dict: if block and block < 11315910: return # ychad = contract('ychad.eth') ychad = contract('0xfeb4acf3df3cdea7399794d0869ef76a6efaff52') unitVault = contract("0xb1cff81b9305166ff1efc49a129ad2afcd7bcf19") yfi = "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e" bal = unitVault.collaterals(yfi, ychad, block_identifier=block) collateral = { yfi: { 'balance': bal / 10**18, 'usd value': bal / 10**18 * get_price(yfi, block), } } return collateral
def get_price(token, block=None): pool = Contract(token) tokens, supply = fetch_multicall([pool, "getCurrentTokens"], [pool, "totalSupply"], block=block) supply = supply / 1e18 balances = fetch_multicall(*[[pool, "getBalance", token] for token in tokens], block=block) balances = [ balance / 10**Contract(token).decimals() for balance, token in zip(balances, tokens) ] total = sum(balance * magic.get_price(token, block=block) for balance, token in zip(balances, tokens)) return total / supply
def get_price(self, token, block=None): pool = self.get_pool(token) # crypto pools can have different tokens, use slow method if hasattr(contract(pool), 'price_oracle'): tvl = self.get_tvl(pool, block=block) if tvl is None: return None supply = contract(token).totalSupply(block_identifier=block) / 1e18 return tvl / supply # approximate by using the most common base token we find coins = self.get_underlying_coins(pool) try: coin = (set(coins) & BASIC_TOKENS).pop() except KeyError: coin = coins[0] virtual_price = contract(pool).get_virtual_price(block_identifier=block) / 1e18 return virtual_price * magic.get_price(coin, block)
def maker_collateral(self, block=None) -> dict: proxy_registry = contract('0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4') cdp_manager = contract('0x5ef30b9986345249bc32d8928B7ee64DE9435E39') # ychad = contract('ychad.eth') ychad = contract('0xfeb4acf3df3cdea7399794d0869ef76a6efaff52') vat = contract('0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B') proxy = proxy_registry.proxies(ychad) cdp = cdp_manager.first(proxy) urn = cdp_manager.urns(cdp) ilk = encode_single('bytes32', b'YFI-A') ink = vat.urns(ilk, urn, block_identifier=block).dict()["ink"] yfi = "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e" collateral = { yfi: { 'balance': ink / 10**18, 'usd value': ink / 10**18 * get_price(yfi, block) if ink > 0 else 0, } } return collateral
def _get_price(token, block=None): SKIP_PRICE = [ # shitcoins "0xa9517B2E61a57350D6555665292dBC632C76adFe", "0xb07de4b2989E180F8907B8C7e617637C26cE2776", "0x1368452Bfb5Cd127971C8DE22C58fBE89D35A6BF", "0x5cB5e2d7Ab9Fd32021dF8F1D3E5269bD437Ec3Bf", "0x11068577AE36897fFaB0024F010247B9129459E6", "0x9694EED198C1b7aB81ADdaf36255Ea58acf13Fab", "0x830Cbe766EE470B67F77ea62a56246863F75f376", "0x8F49cB69ee13974D6396FC26B0c0D78044FCb3A7", "0x53d345839E7dF5a6c8Cf590C5c703AE255E44816", "0xcdBb37f84bf94492b44e26d1F990285401e5423e", "0xE256CF1C7caEff4383DabafEe6Dd53910F97213D", "0x528Ff33Bf5bf96B5392c10bc4748d9E9Fb5386B2", ] if token in SKIP_PRICE: return 0 try: price = get_price(token, block) except AttributeError: logger.warn( f"AttributeError while getting price for {contract(token).symbol()} {token}" ) raise except PriceError: logger.warn( f"PriceError while getting price for {contract(token).symbol()} {token}" ) price = 0 except ValueError: logger.warn( f"ValueError while getting price for {contract(token).symbol()} {token}" ) price = 0 return price
def held_assets(self, block=None) -> dict: balances = {} for address in self.addresses: # get token balances tokens = self.token_list(address, block=block) token_balances = fetch_multicall( *[[contract(token), "balanceOf", address] for token in tokens], block=block, ) decimals = fetch_multicall(*[[contract(token), "decimals"] for token in tokens], block=block) token_balances = [ balance / 10**decimal if decimal else 0 for balance, decimal in zip(token_balances, decimals) ] token_prices = Parallel(8, 'threading')( delayed(_get_price)(token, block) for token in tokens) token_balances = [{ 'balance': balance, 'usd value': balance * price } for balance, price in zip(token_balances, token_prices)] balances[address] = dict(zip(tokens, token_balances)) # then, add eth if block: balance = ( web3.eth.get_balance(address, block_identifier=block) / 10**18) else: balance = web3.eth.get_balance(address) / 10**18 balances[address]['ETH'] = { 'balance': balance, 'usd value': balance * get_price(weth, block), } return balances
def total_value_at(self, block=None): yfi_locked = self.token.balanceOf(self.vault, block_identifier=block) / 1e18 yfi_price = magic.get_price(str(self.token), block=block) return yfi_locked * yfi_price
def simple(vault, samples: ApySamples) -> Apy: lp_token = vault.token.address pool_address = curve.get_pool(lp_token) gauge_address = curve.get_gauge(pool_address) if gauge_address is None: raise ApyError("crv", "no gauge") gauge = contract(gauge_address) try: controller = gauge.controller() controller = contract(controller) except: # newer gauges do not have a 'controller' method controller = curve.gauge_controller block = samples.now gauge_weight = controller.gauge_relative_weight.call( gauge_address, block_identifier=block) gauge_working_supply = gauge.working_supply(block_identifier=block) if gauge_working_supply == 0: raise ApyError("crv", "gauge working supply is zero") gauge_inflation_rate = gauge.inflation_rate(block_identifier=block) pool = contract(pool_address) pool_price = pool.get_virtual_price(block_identifier=block) base_asset_price = get_price(lp_token, block=block) or 1 crv_price = get_price(curve.crv, block=block) yearn_voter = addresses[chain.id]['yearn_voter_proxy'] y_working_balance = gauge.working_balances(yearn_voter, block_identifier=block) y_gauge_balance = gauge.balanceOf(yearn_voter, block_identifier=block) 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 # TODO: come up with cleaner way to deal with these new gauge rewards reward_apr = 0 if hasattr(gauge, "reward_contract"): reward_address = gauge.reward_contract() if reward_address != ZERO_ADDRESS: reward_apr = rewards(reward_address, pool_price, base_asset_price, block=block) elif hasattr(gauge, "reward_data" ): # this is how new gauges, starting with MIM, show rewards # get our token # TODO: consider adding for loop with [gauge.reward_tokens(i) for i in range(gauge.reward_count())] for multiple rewards tokens gauge_reward_token = gauge.reward_tokens(0) if gauge_reward_token in [ZERO_ADDRESS]: print("no reward token") else: reward_data = gauge.reward_data(gauge_reward_token) rate = reward_data['rate'] period_finish = reward_data['period_finish'] total_supply = gauge.totalSupply() token_price = 0 if gauge_reward_token == addresses[chain.id]['rkp3r_rewards']: rKP3R_contract = interface.rKP3R(gauge_reward_token) discount = rKP3R_contract.discount(block_identifier=block) token_price = get_price(addresses[chain.id]['kp3r'], block=block) * (100 - discount) / 100 else: token_price = get_price(gauge_reward_token, block=block) current_time = time() if block is None else get_block_timestamp( block) if period_finish < current_time: reward_apr = 0 else: reward_apr = (SECONDS_PER_YEAR * (rate / 1e18) * token_price) / ( (pool_price / 1e18) * (total_supply / 1e18) * base_asset_price) else: reward_apr = 0 price_per_share = pool.get_virtual_price now_price = price_per_share(block_identifier=samples.now) try: week_ago_price = price_per_share(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 # prevent circular import for partners calculations from yearn.v2.vaults import Vault as VaultV2 if isinstance(vault, VaultV2): vault_contract = vault.vault if len(vault.strategies) > 0 and hasattr(vault.strategies[0].strategy, "keepCRV"): crv_keep_crv = vault.strategies[0].strategy.keepCRV( block_identifier=block) / 1e4 elif len(vault.strategies) > 0 and hasattr( vault.strategies[0].strategy, "keepCrvPercent"): crv_keep_crv = vault.strategies[0].strategy.keepCrvPercent( block_identifier=block) / 1e4 else: crv_keep_crv = 0 performance = (vault_contract.performanceFee(block_identifier=block) * 2) / 1e4 if hasattr(vault_contract, "performanceFee") else 0 management = vault_contract.managementFee( block_identifier=block) / 1e4 if hasattr(vault_contract, "managementFee") else 0 else: strategy = vault.strategy strategist_performance = strategy.performanceFee( block_identifier=block) if hasattr(strategy, "performanceFee") else 0 strategist_reward = strategy.strategistReward( block_identifier=block) if hasattr(strategy, "strategistReward") else 0 treasury = strategy.treasuryFee( block_identifier=block) if hasattr(strategy, "treasuryFee") else 0 crv_keep_crv = strategy.keepCRV( block_identifier=block) / 1e4 if hasattr(strategy, "keepCRV") else 0 performance = (strategist_reward + strategist_performance + treasury) / 1e4 management = 0 if isinstance(vault, VaultV2) and len(vault.strategies) == 2: crv_strategy = vault.strategies[0].strategy cvx_strategy = vault.strategies[1].strategy convex_voter = addresses[chain.id]['convex_voter_proxy'] cvx_working_balance = gauge.working_balances(convex_voter, block_identifier=block) cvx_gauge_balance = gauge.balanceOf(convex_voter, block_identifier=block) if cvx_gauge_balance > 0: cvx_boost = cvx_working_balance / (PER_MAX_BOOST * cvx_gauge_balance) or 1 else: cvx_boost = MAX_BOOST cvx_booster = contract(addresses[chain.id]['convex_booster']) cvx_lock_incentive = cvx_booster.lockIncentive(block_identifier=block) cvx_staker_incentive = cvx_booster.stakerIncentive( block_identifier=block) cvx_earmark_incentive = cvx_booster.earmarkIncentive( block_identifier=block) cvx_fee = (cvx_lock_incentive + cvx_staker_incentive + cvx_earmark_incentive) / 1e4 cvx_keep_crv = cvx_strategy.keepCRV(block_identifier=block) / 1e4 total_cliff = 1e3 max_supply = 1e2 * 1e6 * 1e18 # ? reduction_per_cliff = 1e23 cvx = contract(addresses[chain.id]['cvx']) supply = cvx.totalSupply(block_identifier=block) cliff = supply / reduction_per_cliff if supply <= max_supply: reduction = total_cliff - cliff cvx_minted_as_crv = reduction / total_cliff cvx_price = get_price(cvx, block=block) converted_cvx = cvx_price / crv_price cvx_printed_as_crv = cvx_minted_as_crv * converted_cvx else: cvx_printed_as_crv = 0 cvx_apr = ((1 - cvx_fee) * cvx_boost * base_apr) * (1 + cvx_printed_as_crv) + reward_apr cvx_apr_minus_keep_crv = ((1 - cvx_fee) * cvx_boost * base_apr) * ( (1 - cvx_keep_crv) + cvx_printed_as_crv) crv_debt_ratio = vault.vault.strategies(crv_strategy)[2] / 1e4 cvx_debt_ratio = vault.vault.strategies(cvx_strategy)[2] / 1e4 else: cvx_apr = 0 cvx_apr_minus_keep_crv = 0 cvx_keep_crv = 0 crv_debt_ratio = 1 cvx_debt_ratio = 0 crv_apr = base_apr * boost + reward_apr crv_apr_minus_keep_crv = base_apr * boost * (1 - crv_keep_crv) gross_apr = (1 + (crv_apr * crv_debt_ratio + cvx_apr * cvx_debt_ratio)) * ( 1 + pool_apy) - 1 cvx_net_apr = (cvx_apr_minus_keep_crv + reward_apr) * (1 - performance) - management cvx_net_farmed_apy = (1 + (cvx_net_apr / COMPOUNDING))**COMPOUNDING - 1 cvx_net_apy = ((1 + cvx_net_farmed_apy) * (1 + pool_apy)) - 1 crv_net_apr = (crv_apr_minus_keep_crv + reward_apr) * (1 - performance) - management crv_net_farmed_apy = (1 + (crv_net_apr / COMPOUNDING))**COMPOUNDING - 1 crv_net_apy = ((1 + crv_net_farmed_apy) * (1 + pool_apy)) - 1 net_apy = crv_net_apy * crv_debt_ratio + cvx_net_apy * cvx_debt_ratio # 0.3.5+ should never be < 0% because of management if isinstance(vault, VaultV2) and net_apy < 0 and Version( vault.api_version) >= Version("0.3.5"): net_apy = 0 fees = ApyFees(performance=performance, management=management, keep_crv=crv_keep_crv, cvx_keep_crv=cvx_keep_crv) composite = { "boost": boost, "pool_apy": pool_apy, "boosted_apr": crv_apr, "base_apr": base_apr, "cvx_apr": cvx_apr, "rewards_apr": reward_apr, } return Apy("crv", gross_apr, net_apy, fees, composite=composite)
def test_uniswap_v3(token): price = v3.uniswap_v3.get_price(token) alt_price = magic.get_price(token) print(token, price, alt_price) assert price == pytest.approx(alt_price, rel=5e-2)
def test_uniswap_v1(token): price = v1.uniswap_v1.get_price(token) alt_price = magic.get_price(token) print(token, price, alt_price) # check if price is within 5% range assert price == pytest.approx(alt_price, rel=5e-2)
def getprice(token_address): if token_address == '0x27d22a7648e955e510a40bdb058333e9190d12d4': # PPOOL return magic.get_price( '0x0cec1a9154ff802e7934fc916ed7ca50bde6844e', block) return magic.get_price(token_address, block)
def total_value_at(self, block=None): crv_locked = voting_escrow.balanceOf["address"](self.proxy, block_identifier=block) / 1e18 crv_price = magic.get_price(crv, block=block) return crv_locked * crv_price
def walletdataframe(wallet, block): # NOTE: Moralis API is returning absurd values for token balances, # so we will disreagrd balances returned by the API. We only use # the API to fetch a list of tokens in the wallet. We then use the # token list to query correct balances from the blockchain. url = f'{moralis}{wallet}/erc20' df = pd.DataFrame(get(url, headers=headers).json()) # NOTE: Remove spam tokens df = df[~df.token_address.isin(SPAM_TOKENS)] def getcategory(token_address): try: return CATEGORY_MAPPINGS[token_address] except KeyError: return def getbalance(token_address): logging.debug(f'token: {token_address}') return contract(token_address).balanceOf(wallet, block_identifier=block) def getprice(token_address): if token_address == '0x27d22a7648e955e510a40bdb058333e9190d12d4': # PPOOL return magic.get_price( '0x0cec1a9154ff802e7934fc916ed7ca50bde6844e', block) return magic.get_price(token_address, block) # NOTE: Add some details df['wallet'] = wallet df['wallet_label'] = YEARN_WALLETS[wallet] df['category'] = df['token_address'].apply(getcategory) # NOTE: Do some maths df['balance'] = df['token_address'].apply( getbalance) / 10**df['decimals'].apply(int) df['price'] = df['token_address'].apply(getprice) df['value'] = df['balance'] * df['price'] # NOTE: Get rid of columns we don't need df = df.drop(columns=['logo', 'thumbnail', 'decimals']) # NOTE: Get rid of tokens with 0 balance df = df[df['balance'] != 0] ethbal = web3.eth.get_balance(convert.to_address(wallet), block_identifier=block) / 10**18 if ethbal > 0: ethprice = magic.get_price( '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', block) return df.append( pd.DataFrame({ 'token_address': [None], 'name': ['Ethereum'], 'symbol': ['ETH'], 'balance': [ethbal], 'category': ['ETH'], 'wallet': [wallet], 'wallet_label': [YEARN_WALLETS[wallet]], 'price': [ethprice], 'value': [ethbal * ethprice], })) return df
def get_price(self, block=None): if self.name == "aLINK": return magic.get_price(self.vault.underlying(), block=block) return magic.get_price(self.token, block=block)
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)
def get_price(self, vault: Union[VaultV1, VaultV2], block=None): if isinstance(vault, VaultV1) and vault.name == "aLINK": return magic.get_price(self.vault.underlying(), block=block) else: return magic.get_price(self.token, block=block)