def __init__(self): self.registry = interface.YRegistry(web3.ens.resolve("registry.ychad.eth")) addresses_provider = contract("0x9be19Ee7Bc4099D62737a7255f5c227fBcd6dB93") addresses_generator_v1_vaults = contract(addresses_provider.addressById("ADDRESSES_GENERATOR_V1_VAULTS")) # NOTE: we assume no more v1 vaults are deployed self.vaults = [VaultV1(vault_address, *self.registry.getVaultInfo(vault_address)) for vault_address in addresses_generator_v1_vaults.assetsAddresses()]
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 get_balances(self, pool, block=None): """ Get {token: balance} of liquidity in the pool. """ factory = self.get_factory(pool) coins = self.get_coins(pool) decimals = self.get_decimals(pool) try: if factory: source = contract(factory) elif self.crypto_swap_registry_supports_pool(pool): source = self.crypto_swap_registry else: source = self.registry balances = source.get_balances(pool, block_identifier=block) # fallback for historical queries except ValueError: balances = fetch_multicall(*[[contract(pool), 'balances', i] for i, _ in enumerate(coins)]) if not any(balances): raise ValueError(f'could not fetch balances {pool} at {block}') return { coin: balance / 10**dec for coin, balance, dec in zip(coins, balances, decimals) if coin != ZERO_ADDRESS }
def __init__(self): if chain.id not in addresses: raise UnsupportedNetwork( 'compound is not supported on this network') conf = addresses[chain.id] self.factory = contract(conf['factory']) self.quoter = contract(conf['quoter']) self.fee_tiers = conf['fee_tiers']
def load_registry(self): if chain.id == Network.Mainnet: return self.load_from_ens() elif chain.id == Network.Fantom: return [contract('0x727fe1759430df13655ddb0731dE0D0FDE929b04')] elif chain.id == Network.Arbitrum: return [contract('0xC8f17f8E15900b6D6079680b15Da3cE5263f62AA')] else: raise UnsupportedNetwork( 'yearn v2 is not available on this network')
def get_address(self, name): """ Get contract from Synthetix registry. See also https://docs.synthetix.io/addresses/ """ address_resolver = contract(addresses[chain.id]) address = address_resolver.getAddress( encode_single('bytes32', name.encode())) proxy = contract(address) return contract(proxy.target()) if hasattr(proxy, 'target') else proxy
def test_get_v1_strategy(): old_block = 10532708 vault = next(x for x in registry.vaults if x.vault == '0x597aD1e0c13Bfe8025993D9e79C69E1c0233522e') assert vault.get_controller(old_block) == contract('0x31317F9A5E4cC1d231bdf07755C994015A96A37c') assert vault.get_strategy(old_block) is None assert vault.get_strategy(old_block + 100) == contract('0x003312E3eBBe6b0f25f1c03C2695d83075d9a9B8') assert vault.get_strategy(old_block + 1000) == contract('0xea061fDd1cd80176455bDb314C87d78570a8fb26') new_block = 10640746 assert vault.get_controller(new_block) == vault.controller
def __contains__(self, token): """ Check if a token is a synth. """ token = contract(token) if not hasattr(token, 'target'): return False target = token.target() if target in self.synths and contract(target).proxy() == token: return True return False
def unit_debt(self, block=None) -> dict: if block and block < 11315910: return # ychad = contract('ychad.eth') ychad = contract('0xfeb4acf3df3cdea7399794d0869ef76a6efaff52') unitVault = contract("0xb1cff81b9305166ff1efc49a129ad2afcd7bcf19") yfi = "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e" usdp = '0x1456688345527bE1f37E9e627DA0837D6f08C925' debt = unitVault.getTotalDebt(yfi, ychad, block_identifier=block) / 10**18 debt = {usdp: {'balance': debt, 'usd value': debt}} return debt
def get_gauge(self, pool): """ Get liquidity gauge address by pool. """ factory = self.get_factory(pool) if factory and hasattr(contract(factory), 'get_gauge'): gauge = contract(factory).get_gauge(pool) if gauge != ZERO_ADDRESS: return gauge gauges, types = self.registry.get_gauges(pool) if gauges[0] != ZERO_ADDRESS: return gauges[0]
def get_token_from_event(event): try: transfer = event['Transfer'] address = transfer[0].address # try to download the contract from etherscan contract(address) return address except ValueError: # some tokens have unverified sources with etherscan, skip them! return None except EventLookupError: logger.critical(event) raise
def get_decimals(self, pool): factory = self.get_factory(pool) source = contract(factory) if factory else self.registry decimals = source.get_decimals(pool) # pool not in registry if not any(decimals): coins = self.get_coins(pool) decimals = fetch_multicall( *[[contract(token), 'decimals'] for token in coins] ) return [dec for dec in decimals if dec != 0]
def __post_init__(self): self.vault = contract(self.vault) self.controller = contract(self.controller) self.strategy = contract(self.strategy) self.token = contract(self.token) if str(self.vault) not in constants.VAULT_ALIASES: logger.warning( "no vault alias for %s, reading from vault.sybmol()", self.vault) self.name = constants.VAULT_ALIASES.get(str(self.vault), self.vault.symbol()) self.decimals = self.vault.decimals( ) # vaults inherit decimals from token self.scale = 10**self.decimals
def __init__(self): if chain.id not in curve_contracts: raise UnsupportedNetwork("curve is not supported on this network") addrs = curve_contracts[chain.id] if chain.id == Network.Mainnet: self.crv = contract(addrs['crv']) self.voting_escrow = contract(addrs['voting_escrow']) self.gauge_controller = contract(addrs['gauge_controller']) self.pools = set() self.identifiers = defaultdict(list) self.addres_provider = contract(addrs['address_provider']) self.watch_events()
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 test_curve_lp_price_oracle_historical(name): if name in ['linkusd']: pytest.xfail('no active market') token = web3.toChecksumAddress(pooldata[name]['lp_token_address']) swap = web3.toChecksumAddress(pooldata[name]['swap_address']) deploy = contract_creation_block(swap) # sample 10 blocks over the pool lifetime blocks = [ int(block) for block in np.linspace(deploy + 10000, chain.height, 10) ] prices = [] for block in blocks: try: prices.append(curve.curve.get_price(token, block)) except (PriceError, TypeError): prices.append(None) virtual_prices = [ contract(swap).get_virtual_price(block_identifier=block) / 1e18 for block in blocks ] print( tabulate(list(zip(blocks, prices, virtual_prices)), headers=['block', 'price', 'vp']))
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 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 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 maker_debt(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') art = vat.urns(ilk, urn, block_identifier=block).dict()["art"] rate = vat.ilks(ilk, block_identifier=block).dict()["rate"] debt = art * rate / 1e45 dai = '0x6B175474E89094C44Da98b954EedeAC495271d0F' debt = {dai: {'balance': debt, 'usd value': debt}} return debt
def get_price(self, token, block=None): if block and block < contract_creation_block(UNISWAP_V3_QUOTER): return None if token == usdc: return 1 paths = [] if token != weth: paths += [[token, fee, weth, self.fee_tiers[0], usdc] for fee in self.fee_tiers] paths += [[token, fee, usdc] for fee in self.fee_tiers] scale = 10**contract(token).decimals() results = fetch_multicall( *[[self.quoter, 'quoteExactInput', self.encode_path(path), scale] for path in paths], block=block, ) outputs = [ amount / self.undo_fees(path) / 1e6 for amount, path in zip(results, paths) if amount ] return max(outputs) if outputs else None
def __init__(self): if chain.id not in registries: raise UnsupportedNetwork( 'chainlink is not supported on this network') self.registry = contract(registries[chain.id]) self.load_feeds()
def balances(self, blocks): bentobox = contract('0xF5BCE5077908a1b7370B9ae04AdC565EBd643966') vault = Vault.from_address(self.vault) balances = batch_call( [[bentobox, 'balanceOf', self.vault, self.wrapper, block] for block in blocks]) return [(balance or 0) / vault.scale for balance in balances]
def from_address(cls, address): vault = contract(address) instance = cls(vault=vault, token=vault.token(), api_version=vault.apiVersion()) instance.name = vault.name() return instance
def __init__(self, vault, api_version=None, token=None, registry=None, watch_events_forever=True): self._strategies = {} self._revoked = {} self._reports = [] self.vault = vault self.api_version = api_version if token is None: token = vault.token() self.token = contract(token) self.registry = registry self.scale = 10**self.vault.decimals() # multicall-safe views with 0 inputs and numeric output. self._views = safe_views(self.vault.abi) # load strategies from events and watch for freshly attached strategies self._topics = [[ encode_hex(event_abi_to_log_topic(event)) for event in self.vault.abi if event["type"] == "event" and event["name"] in STRATEGY_EVENTS ]] self._watch_events_forever = watch_events_forever self._done = threading.Event() self._thread = threading.Thread(target=self.watch_events, daemon=True)
def process_events(self, events): for event in events: logger.debug("%s %s %s", event.address, event.name, dict(event)) if event.name == "NewGovernance": self.governance = event["governance"] if event.name == "NewRelease": self.releases[event["api_version"]] = contract( event["template"]) if event.name == "NewVault": # experiment was endorsed if event["vault"] in self._experiments: vault = self._experiments.pop(event["vault"]) vault.name = f"{vault.vault.symbol()} {event['api_version']}" self._vaults[event["vault"]] = vault logger.debug("endorsed vault %s %s", vault.vault, vault.name) # we already know this vault from another registry elif event["vault"] not in self._vaults: vault = self.vault_from_event(event) vault.name = f"{vault.vault.symbol()} {event['api_version']}" self._vaults[event["vault"]] = vault logger.debug("new vault %s %s", vault.vault, vault.name) if event.name == "NewExperimentalVault": vault = self.vault_from_event(event) vault.name = f"{vault.vault.symbol()} {event['api_version']} {event['vault'][:8]}" self._experiments[event["vault"]] = vault logger.debug("new experiment %s %s", vault.vault, vault.name) if event.name == "VaultTagged": self.tags[event["vault"]] = event["tag"]
def get_price(self, token, block=None): if block is None or block >= self.registry_deploy_block: return self.registry.price(token, block_identifier=block) / 1e18 else: # fallback method for before registry deployment oracle = contract(self.registry.oracle()) ctoken = self.registry.cy(token) return oracle.getUnderlyingPrice(ctoken, block_identifier=block) / 1e18
def __init__(self): if chain.id not in addresses: raise UnsupportedNetwork("fixed forex is not supported on this network") self.registry = contract(addresses[chain.id]) self.registry_deploy_block = contract_creation_block(addresses[chain.id]) self.markets = self.registry.forex() logger.info(f'loaded {len(self.markets)} fixed forex markets')
def get_assets_metadata(vault_v2: list) -> dict: registry_v2_adapter = contract(web3.ens.resolve("lens.ychad.eth")) addresses = [str(vault.vault) for vault in vault_v2] assets_dynamic_data = registry_v2_adapter.assetsDynamic(addresses) assets_metadata = {} for datum in assets_dynamic_data: assets_metadata[datum[0]] = datum[-1] return assets_metadata
def get_strategy(self, block=None): if self.name in ["aLINK", "LINK"] or block is None: return self.strategy controller = self.get_controller(block) strategy = controller.strategies(self.token, block_identifier=block) if strategy != ZERO_ADDRESS: return contract(strategy)