Ejemplo n.º 1
0
def get_price(token, block=None):
    token = Contract(token)
    underlying, exchange_rate, decimals = fetch_multicall(
        [token, 'underlying'],
        [token, 'exchangeRateCurrent'],
        [token, 'decimals'],
        block=block
    )
    exchange_rate /= 1e18
    under_decimals = Contract(underlying).decimals()
    return [exchange_rate * 10 ** (decimals - under_decimals), underlying]
Ejemplo n.º 2
0
 def total_value_at(self, block=None):
     vaults = self.active_vaults_at_block(block)
     prices = Parallel(8, "threading")(
         delayed(magic.get_price)(vault.token, block=block)
         for vault in vaults)
     results = fetch_multicall(*[[vault.vault, "pool"] for vault in vaults],
                               block=block)
     return {
         vault.name: assets * price / vault.scale
         for vault, assets, price in zip(vaults, results, prices)
     }
Ejemplo n.º 3
0
 def describe(self, block=None):
     vaults = self.active_vaults_at(block)
     share_prices = fetch_multicall(*[[vault.vault, "getPricePerFullShare"]
                                      for vault in vaults],
                                    block=block)
     vaults = [
         vault for vault, share_price in zip(vaults, share_prices)
         if share_price
     ]
     data = Parallel(8, "threading")(delayed(vault.describe)(block=block)
                                     for vault in vaults)
     return {vault.name: desc for vault, desc in zip(vaults, data)}
Ejemplo n.º 4
0
def get_markets():
    comptroller = Contract("0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B")
    creamtroller = Contract('0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258')
    ironbankroller = Contract("0xAB1c342C7bf5Ec5F02ADEA1c2270670bCa144CbB")
    
    results = fetch_multicall(
        [comptroller, 'getAllMarkets'],
        [creamtroller, 'getAllMarkets'],
        [ironbankroller, 'getAllMarkets'],
    )
    names = ['compound', 'cream', 'ironbank']
    return dict(zip(names, results))
Ejemplo n.º 5
0
    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]
Ejemplo n.º 6
0
 def metapools_by_factory(self):
     """
     Read cached pools spawned by each factory.
     TODO Update on factory events
     """
     metapool_factories = [contract(factory) for factory in self.identifiers[3]]
     pool_counts = fetch_multicall(
         *[[factory, 'pool_count'] for factory in metapool_factories]
     )
     pool_lists = iter(
         fetch_multicall(
             *[
                 [factory, 'pool_list', i]
                 for factory, pool_count in zip(metapool_factories, pool_counts)
                 for i in range(pool_count)
             ]
         )
     )
     return {
         str(factory): list(islice(pool_lists, pool_count))
         for factory, pool_count in zip(metapool_factories, pool_counts)
     }
Ejemplo n.º 7
0
 def total_value_at(self, block=None):
     vaults = self.active_vaults_at(block)
     balances = fetch_multicall(*[[vault.vault, "balance"]
                                  for vault in vaults],
                                block=block)
     # skip vaults with zero or erroneous balance
     vaults = [(vault, balance) for vault, balance in zip(vaults, balances)
               if balance]
     prices = Parallel(8, "threading")(delayed(vault.get_price)(block)
                                       for (vault, balance) in vaults)
     return {
         vault.name: balance * price / 10**vault.decimals
         for (vault, balance), price in zip(vaults, prices)
     }
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
 def active_vaults_at(self, block=None):
     vaults = self.vaults + self.experiments
     if block:
         vaults = [
             vault for vault in vaults
             if contract_creation_block(str(vault.vault)) <= block
         ]
     # fixes edge case: a vault is not necessarily initialized on creation
     activations = fetch_multicall(*[[vault.vault, 'activation']
                                     for vault in vaults],
                                   block=block)
     return [
         vault for vault, activation in zip(vaults, activations)
         if activation
     ]
Ejemplo n.º 10
0
    def describe(self, block=None):
        results = fetch_multicall(
            *[[self.strategy, view] for view in self._views],
            [self.vault.vault, "strategies", self.strategy],
            block=block)
        info = dict(zip(self._views, results))
        info.update(results[-1].dict())
        for view in STRATEGY_VIEWS_SCALED:
            if view in info:
                info[view] = (info[view] or 0) / self.vault.scale
        # unwrap structs
        for view in info:
            if hasattr(info[view], '_dict'):
                info[view] = info[view].dict()

        return info
Ejemplo n.º 11
0
    def get_coins(self, pool):
        """
        Get coins of pool.
        """
        factory = self.get_factory(pool)

        if factory:
            coins = contract(factory).get_coins(pool)
        else:
            coins = self.registry.get_coins(pool)

        # pool not in registry
        if set(coins) == {ZERO_ADDRESS}:
            coins = fetch_multicall(
                *[[contract(pool), 'coins', i] for i in range(8)]
            )

        return [coin for coin in coins if coin not in {None, ZERO_ADDRESS}]
Ejemplo n.º 12
0
    def get_decimals(self, pool):
        factory = self.get_factory(pool)
        if factory:
            source = contract(factory)
        elif self.crypto_swap_registry_supports_pool(pool):
            source = self.crypto_swap_registry
        else:
            source = source = 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]
Ejemplo n.º 13
0
 def lp_price(self, address, block=None):
     """Get Uniswap/Sushiswap LP token price."""
     pair = contract(address)
     token0, token1, supply, reserves = fetch_multicall(
         [pair, "token0"],
         [pair, "token1"],
         [pair, "totalSupply"],
         [pair, "getReserves"],
         block=block,
     )
     tokens = [Contract(token) for token in [token0, token1]]
     scales = [10**token.decimals() for token in tokens]
     prices = [self.get_price(token, block=block) for token in tokens]
     supply = supply / 1e18
     balances = [
         res / scale * price
         for res, scale, price in zip(reserves, scales, prices)
     ]
     return sum(balances) / supply
Ejemplo n.º 14
0
    def markets(self):
        atoken_to_token = {}
        for version, provider in address_providers[chain.id].items():
            lending_pool = contract(contract(provider).getLendingPool())
            if version == 'v1':
                tokens = lending_pool.getReserves()
            elif version == 'v2':
                tokens = lending_pool.getReservesList()
            else:
                raise ValueError(f'unsupported aave version {version}')

            reserves = fetch_multicall(
                *[[lending_pool, 'getReserveData', token] for token in tokens])
            atoken_to_token.update({
                reserve['aTokenAddress']: token
                for token, reserve in zip(tokens, reserves)
            })

        return atoken_to_token
Ejemplo n.º 15
0
 def cache_token(self, token_address: str):
     i = 0
     while i < 10:
         try:
             self.cache_address(token_address)
             if not self.token_exists(token_address):
                 token = contract(token_address)
                 symbol, name, decimals = fetch_multicall(
                     [token, 'symbol'], [token, 'name'],
                     [token, 'decimals'])
                 self.cursor.execute(
                     f"INSERT INTO TOKENS (CHAINID, TOKEN_ADDRESS, SYMBOL, NAME, DECIMALS)\
                                         VALUES ({chainid},'{token_address}','{symbol}','{name}',{decimals})"
                 )
                 self.conn.commit()
                 print(f'{symbol} added to postgres')
             return
         except:
             i += 1
Ejemplo n.º 16
0
    def calculate_boost(self, gauge, addr, block=None):
        results = fetch_multicall(
            [gauge, "balanceOf", addr],
            [gauge, "totalSupply"],
            [gauge, "working_balances", addr],
            [gauge, "working_supply"],
            [self.voting_escrow, "balanceOf", addr],
            [self.voting_escrow, "totalSupply"],
            block=block,
        )
        results = [x / 1e18 for x in results]
        gauge_balance, gauge_total, working_balance, working_supply, vecrv_balance, vecrv_total = results
        try:
            boost = working_balance / gauge_balance * 2.5
        except ZeroDivisionError:
            boost = 1

        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
        try:
            max_boost_possible = (lim / _working_supply) / (noboost_lim /
                                                            noboost_supply)
        except ZeroDivisionError:
            max_boost_possible = 1

        return {
            "gauge balance": gauge_balance,
            "gauge total": gauge_total,
            "vecrv balance": vecrv_balance,
            "vecrv total": vecrv_total,
            "working balance": working_balance,
            "working total": working_supply,
            "boost": boost,
            "max boost": max_boost_possible,
            "min vecrv": min_vecrv,
        }
Ejemplo n.º 17
0
    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.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"]

        return info
Ejemplo n.º 18
0
    def describe(self, block=None):
        info = {}
        strategy = self.strategy
        if block is not None:
            strategy = self.get_strategy(block=block)

        # attrs are fetches as multicall and populate info
        attrs = {
            "vault balance": [self.vault, "balance"],
            "vault total": [self.vault, "totalSupply"],
            "strategy balance": [strategy, "balanceOf"],
            "share price": [self.vault, "getPricePerFullShare"],
        }

        # some of the oldest vaults don't implement these methods
        if hasattr(self.vault, "available"):
            attrs["available"] = [self.vault, "available"]

        if hasattr(self.vault, "min") and hasattr(self.vault, "max"):
            attrs["min"] = [self.vault, "min"]
            attrs["max"] = [self.vault, "max"]

        # new curve voter proxy vaults
        if self.is_curve_vault and hasattr(strategy, "proxy"):
            vote_proxy, gauge = fetch_multicall(
                [strategy, "voter"],  # voter is static, can pin
                [strategy, "gauge"],  # gauge is static per strategy, can cache
                block=block,
            )
            # guard historical queries where there are no vote_proxy and gauge
            # for block <= 10635293 (2020-08-11)
            if vote_proxy and gauge:
                vote_proxy = interface.CurveYCRVVoter(vote_proxy)
                gauge = contract(gauge)
                info.update(
                    curve.calculate_boost(gauge, vote_proxy, block=block))
                info.update(curve.calculate_apy(gauge, self.token,
                                                block=block))
                attrs["earned"] = [gauge, "claimable_tokens",
                                   vote_proxy]  # / scale

        if hasattr(strategy, "earned"):
            attrs["lifetime earned"] = [strategy, "earned"]  # /scale

        if strategy._name == "StrategyYFIGovernance":
            ygov = interface.YearnGovernance(strategy.gov())
            attrs["earned"] = [ygov, "earned", strategy]
            attrs["reward rate"] = [ygov, "rewardRate"]
            attrs["ygov balance"] = [ygov, "balanceOf", strategy]
            attrs["ygov total"] = [ygov, "totalSupply"]

        # fetch attrs as multicall
        results = fetch_multicall(*attrs.values(), block=block)
        scale_overrides = {"share price": 1e18}
        for name, attr in zip(attrs, results):
            if attr is not None:
                info[name] = attr / scale_overrides.get(name, self.scale)
            else:
                logger.warning("attr %s rekt %s", name, attr)

        # some additional post-processing
        if "min" in info:
            info["strategy buffer"] = info.pop("min") / info.pop("max")

        if "token price" not in info:
            info["token price"] = self.get_price(block=block)

        info["tvl"] = info["vault balance"] * info["token price"]

        return info