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
예제 #2
0
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
예제 #4
0
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