def _fetch_rates(coin_list): # fetch the current USD rates for a list of coins if not _rate_cache: _rate_cache[ETH_ADDRESS.lower()] = requests.get( 'https://api.coingecko.com/api/v3/simple/price', params={ "ids": "ethereum", 'vs_currencies': "usd" }, ).json()['ethereum']['usd'] response = requests.get( "https://api.coingecko.com/api/v3/simple/token_price/ethereum", params={ 'contract_addresses': ','.join(COINS), 'vs_currencies': "usd" }).json() for addr in response: _rate_cache[addr.lower()] = response[addr]['usd'] rates = {} for coin in [i for i in coin_list if i in _rate_cache]: rates[coin] = _rate_cache[coin] if len(rates) < len(coin_list): # for coins where a rate is unavailable, we assume it to be the average # rate of the other coins within the pool. when no rates are available, # everything is assumed to be worth $1 avg_rate = sum(rates.values()) / len(rates.values()) if rates else 1 for coin in [i for i in coin_list if i not in _rate_cache]: rates[coin] = avg_rate return rates
def load_contract(token_name_or_address: str): if token_name_or_address.lower() == "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": # USDC does weird things to get their implementation # TODO: don't hard code this!!! impl = Contract("0xB7277a6e95992041568D9391D09d0122023778A2") proxy = Contract("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") contract = Contract.from_abi(proxy._name, proxy.address, impl.abi) return contract if token_name_or_address.lower() in ["eth", ZERO_ADDRESS, ETH_ADDRESS.lower()]: # just use weth for eth token_name_or_address = "weth" elif token_name_or_address.lower() == "ankreth": # TODO: find a tokenlist with this on it token_name_or_address = "0xe95a203b1a91a908f9b9ce46459d101078c2c3cb" elif token_name_or_address.lower() == "obtc": # boring DAO btc # TODO: find a tokenlist with this on it token_name_or_address = "0x8064d9Ae6cDf087b1bcd5BDf3531bD5d8C537a68" # TODO: why was this erroring at the top level imports? from brownie.network.web3 import _resolve_address # this raises a ValueError if this is not an address or ENS name address = _resolve_address(token_name_or_address) contract = Contract(address) # TODO: we shouldn't need from_explorer, but i'm seeing weird things were DAI loads as IWETH9 if ( contract._name == "IWETH9" and address != "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" ): warn(f"Reloading contract supposedly named IWETH9: {address}") contract = Contract.from_explorer(address) # check if this is a proxy contract # TODO: theres other ways to have an impl too. usdc has one that uses storage impl = None if hasattr(contract, "implementation"): impl = Contract(contract.implementation.call()) elif hasattr(contract, "target"): impl = Contract(contract.target.call()) if impl: contract = Contract.from_abi(contract._name, address, impl.abi) return contract
def _get_admin_balances(pool, coin_list): admin_balances = [] rates = _fetch_rates(coin_list) for i, coin in enumerate(coin_list): if hasattr(pool, "admin_balances"): balance = pool.admin_balances(i) else: balance = Contract(coin).balanceOf(pool) - pool.balances(i) if coin != ETH_ADDRESS.lower(): decimals = Contract(coin).decimals() else: decimals = 18 balance = balance / 10**decimals * rates[coin] admin_balances.append(balance) return admin_balances
def load_contract(token_name_or_address: str, owner=None, block=None, force=False): if block == "latest": block = None elif isinstance(token_name_or_address, str) and "." in token_name_or_address: # TODO: PR against web3 to take a block argument # https://github.com/ethereum/web3.py/issues/1984 warn( f"Looking up {token_name_or_address} against the latest block, not {block}" ) block = None cache_key = (token_name_or_address, block) if cache_key in _contract_cache: contract = _contract_cache[cache_key] contract._owner = owner return contract if callable(token_name_or_address): # sometimes it is useful to get the address from a function try: token_name_or_address = token_name_or_address() except TypeError: # this is odd. i've seen `TypeError: 'Contract' object is not callable` pass if isinstance(token_name_or_address, Contract) or isinstance( token_name_or_address, EthContract): # we were given a contract rather than an address or token name. return early token_name_or_address._owner = owner _contract_cache[cache_key] = token_name_or_address return token_name_or_address if isinstance(token_name_or_address, int): token_name_or_address = to_checksum_address(hex(token_name_or_address)) # TODO: don't lower. use checksum addresses everywhere if token_name_or_address.lower() in [ "eth", ZERO_ADDRESS, ETH_ADDRESS.lower() ]: # TODO: just use weth for eth? # TODO: we need this to work on other chains like BSC and Matic contract = EthContract() # TODO: attach owner? _contract_cache[cache_key] = contract return contract elif token_name_or_address.lower() == "ankreth": # TODO: find a tokenlist with this on it token_name_or_address = to_checksum_address( "0xe95a203b1a91a908f9b9ce46459d101078c2c3cb") elif token_name_or_address.lower() == "obtc": # boring DAO BTC # TODO: find a tokenlist with this on it token_name_or_address = "0x8064d9Ae6cDf087b1bcd5BDf3531bD5d8C537a68" # this raises a ValueError if this is not an address or ENS name if "." in token_name_or_address: address = web3.ens.resolve(token_name_or_address) else: address = to_checksum_address(token_name_or_address) if force: contract = Contract.from_explorer(address) contract._owner = owner else: contract = Contract(address, owner=owner) # check if this is a proxy contract contract = check_for_proxy(contract, block, force=force) contract._owner = owner _contract_cache[cache_key] = contract return contract