Exemplo n.º 1
0
def _find_contract(address: Any) -> Any:
    if address is None:
        return

    address = _resolve_address(address)
    if address in _contract_map:
        return _contract_map[address]
    if "chainid" in CONFIG.active_network:
        try:
            from brownie.network.contract import Contract

            return Contract(address)
        except ValueError:
            pass
Exemplo n.º 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
Exemplo n.º 3
0
def atomic_exit():
    """Use a flash loan to withdraw from a leveraged cyy3crv position."""
    load_dotenv(find_dotenv())

    # TODO: we need an account with private keys
    account = accounts.at(os.environ["LEVERAGE_ACCOUNT"])

    # TODO: prompt for slippage amount
    slippage = 0.1

    # TODO: use salts for the contracts once we figure out a way to store them. maybe 3box?

    # deploy our contracts if necessary
    argobytes_factory = get_or_create(account, ArgobytesFactory)
    argobytes_flash_borrower = get_or_create(account, ArgobytesFlashBorrower)
    exit_cyy3crv_action = get_or_create(account, ExitCYY3CRVAction)

    # get the clone for the account
    argobytes_clone = get_or_clone(account, argobytes_factory,
                                   argobytes_flash_borrower)

    assert account == argobytes_clone.owner(), "Wrong owner detected!"

    print("Preparing contracts...")
    # TODO: use multicall to get all the addresses?
    # TODO: do we need all these for exit? or just enter?
    dai = lazy_contract(exit_cyy3crv_action.DAI())
    usdc = lazy_contract(exit_cyy3crv_action.USDC())
    usdt = lazy_contract(exit_cyy3crv_action.USDT())
    threecrv = lazy_contract(exit_cyy3crv_action.THREE_CRV())
    threecrv_pool = lazy_contract(exit_cyy3crv_action.THREE_CRV_POOL())
    y3crv = lazy_contract(exit_cyy3crv_action.Y_THREE_CRV())
    cyy3crv = lazy_contract(exit_cyy3crv_action.CY_Y_THREE_CRV())
    fee_distribution = lazy_contract(
        exit_cyy3crv_action.THREE_CRV_FEE_DISTRIBUTION())
    lender = DyDxFlashLender

    # use multiple workers to fetch the contracts
    # there will still be some to fetch, but this speeds things up some
    # this can take some time since solc/vyper may have to download
    # poke_contracts([dai, usdc, usdt, threecrv, threecrv_pool, y3crv, cyy3crv, lender])

    # TODO: fetch other tokens?
    tokens = [cyy3crv]

    # TODO: calculate/prompt for these
    min_remove_liquidity_dai = 1
    tip_dai = 0
    tip_address = _resolve_address(
        "tip.satoshiandkin.eth"
    )  # TODO: put this on a subdomain and uses an immutable
    # TODO: this should be False in the default case
    exit_from_account = False
    # min_cream_liquidity = 1

    if exit_from_account:
        exit_from = account
        exit_to = ZERO_ADDRESS

        balances = get_balances(exit_from, tokens)
        print(f"{exit_from} balances")

        raise NotImplementedError(
            "we need an approve so CY_DAI.repayBorrowBehalf is allowed")
    else:
        exit_from = ZERO_ADDRESS
        exit_to = account

        balances = get_balances(argobytes_clone, tokens)
        print(f"{argobytes_clone} balances")

    pprint(balances)

    # TODO: ii think this might not be right
    flash_loan_amount = int(
        exit_cyy3crv_action.calculateExit.call(exit_from) * (1 + slippage))

    print(f"flash_loan_amount: {flash_loan_amount}")

    dai_flash_fee = lender.flashFee(dai, flash_loan_amount)

    exit_data = ExitData(
        min_remove_liquidity_dai=min_remove_liquidity_dai,
        tip_dai=tip_dai,
        dai_flash_fee=dai_flash_fee,
        tip_address=tip_address,
        exit_from=exit_from,
        # TODO: allow exiting to an arbitrary account
        exit_to=account,
    )

    pprint(exit_data)

    extra_balances = {}

    approve(account, balances, extra_balances, argobytes_clone)

    # flashloan through the clone
    exit_tx = argobytes_clone.flashBorrow(
        lender,
        dai,
        flash_loan_amount,
        Action(
            exit_cyy3crv_action,
            CallType.DELEGATE,
            False,
            "exit",
            *exit_data,
        ).tuple,
    )

    print("exit success!")
    exit_tx.info()

    num_events = len(enter_tx.events)
    print(f"num events: {num_events}")

    print("clone balances")
    pprint(get_balances(argobytes_clone, tokens))

    print("account balances")
    pprint(get_balances(account, tokens))
Exemplo n.º 4
0
def atomic_enter():
    """Use a flash loan to deposit into leveraged cyy3crv position."""
    # TODO: we need an account with private keys
    account = accounts.at(os.environ["LEVERAGE_ACCOUNT"])
    print(f"Hello, {account}")

    min_3crv_to_claim = os.environ.get("MIN_3CRV_TO_CLAIM", 50)

    # deploy our contracts if necessary
    argobytes_factory = get_or_create(account, ArgobytesFactory)
    argobytes_flash_borrower = get_or_create(account, ArgobytesFlashBorrower)
    enter_cyy3crv_action = get_or_create(account, EnterCYY3CRVAction)

    # get the clone for the account
    argobytes_clone = get_or_clone(account, argobytes_factory, argobytes_flash_borrower)

    print(f"clone: {argobytes_clone}")

    assert account == argobytes_clone.owner(), "Wrong owner detected!"

    print("Preparing contracts...")
    # TODO: use multicall to get all the addresses?
    dai = lazy_contract(enter_cyy3crv_action.DAI())
    usdc = lazy_contract(enter_cyy3crv_action.USDC())
    usdt = lazy_contract(enter_cyy3crv_action.USDT())
    threecrv = lazy_contract(enter_cyy3crv_action.THREE_CRV())
    threecrv_pool = lazy_contract(enter_cyy3crv_action.THREE_CRV_POOL())
    y3crv = lazy_contract(enter_cyy3crv_action.Y_THREE_CRV())
    cyy3crv = lazy_contract(enter_cyy3crv_action.CY_Y_THREE_CRV())
    fee_distribution = lazy_contract(enter_cyy3crv_action.THREE_CRV_FEE_DISTRIBUTION())
    lender = DyDxFlashLender

    # use multiple workers to fetch the contracts
    # there will still be some to fetch, but this speeds things up some
    # this can take some time since solc/vyper may have to download
    # TODO: i think doing this in parallel might be confusiing things
    # poke_contracts([dai, usdc, usdt, threecrv, threecrv_pool, y3crv, cyy3crv, lender])

    tokens = [dai, usdc, usdt, threecrv, y3crv, cyy3crv]

    balances = get_balances(account, tokens)
    print(f"{account} balances")
    print_token_balances(balances)

    claimable_3crv = get_claimable_3crv(account, fee_distribution, min_3crv_to_claim)

    # TODO: calculate/prompt for these
    min_3crv_mint_amount = 1
    tip_3crv = 0
    tip_address = _resolve_address(
        "tip.satoshiandkin.eth"
    )  # TODO: put this on a subdomain and uses an immutable
    min_cream_liquidity = 1000

    enter_data = EnterData(
        dai=balances[dai],
        dai_flash_fee=None,
        usdc=balances[usdc],
        usdt=balances[usdt],
        min_3crv_mint_amount=min_3crv_mint_amount,
        threecrv=balances[threecrv],
        tip_3crv=tip_3crv,
        y3crv=balances[y3crv],
        min_cream_liquidity=min_cream_liquidity,
        sender=account,
        tip_address=tip_address,
        claim_3crv=claimable_3crv > min_3crv_to_claim,
    )

    # TODO: do this properly. use virtualprice and yearn's price calculation
    print("warning! summed_balances is not actually priced in USD")
    summed_balances = (
        enter_data.dai
        + enter_data.usdc
        + enter_data.usdt
        + enter_data.threecrv
        + enter_data.y3crv
        + claimable_3crv
    )

    assert summed_balances > 100, "no coins"

    # TODO: figure out the actual max leverage, then prompt the user for it (though i dont see much reason not to go the full amount here)
    flash_loan_amount = int(summed_balances * 7.4)

    print(f"flash_loan_amount: {flash_loan_amount}")

    assert flash_loan_amount > 0, "no flash loan calculated"

    enter_data = enter_data._replace(
        dai_flash_fee=lender.flashFee(dai, flash_loan_amount)
    )

    pprint(enter_data)

    extra_balances = {}

    if enter_data.claim_3crv:
        extra_balances[threecrv.address] = claimable_3crv

    approve(account, balances, extra_balances, argobytes_clone)

    # flashloan through the clone
    enter_tx = argobytes_clone.flashBorrow(
        lender,
        dai,
        flash_loan_amount,
        Action(
            enter_cyy3crv_action, CallType.DELEGATE, False, "enter", enter_data,
        ).tuple,
    )

    print(f"enter success! {enter_tx.return_value}")

    enter_tx.info()

    num_events = len(enter_tx.events)
    print(f"num events: {num_events}")

    enter_return = to_int(enter_tx.return_value)

    print(f"return value: {enter_return}")

    assert enter_return > 0, "no cyy3ccrv returned!"

    print(f"clone ({argobytes_clone.address}) balances")
    balances = get_balances(argobytes_clone, tokens)
    print_token_balances(balances)

    # TODO: why is this not working? we return cyy3crv.balanceOf!
    # assert balances[cyy3crv] == enter_tx.return_value

    # TODO: make sure the clone has cyy3crv?

    print(f"account ({account}) balances")
    print_token_balances(get_balances(account, tokens))
Exemplo n.º 5
0
def simple_enter():
    """Make a bunch of transactions to deposit into leveraged cyy3crv position."""
    # TODO: we need an account with private keys
    account = accounts.at(os.environ["LEVERAGE_ACCOUNT"])
    print(f"Hello, {account}")

    min_3crv_to_claim = os.environ.get("MIN_3CRV_TO_CLAIM", 50)

    # deploy our contracts if necessary
    enter_cyy3crv_action = get_or_create(account, EnterCYY3CRVAction)

    print("Preparing contracts...")
    # TODO: use multicall to get all the addresses?
    dai = lazy_contract(enter_cyy3crv_action.DAI())
    usdc = lazy_contract(enter_cyy3crv_action.USDC())
    usdt = lazy_contract(enter_cyy3crv_action.USDT())
    threecrv = lazy_contract(enter_cyy3crv_action.THREE_CRV())
    threecrv_pool = lazy_contract(enter_cyy3crv_action.THREE_CRV_POOL())
    y3crv = lazy_contract(enter_cyy3crv_action.Y_THREE_CRV())
    cyy3crv = lazy_contract(enter_cyy3crv_action.CY_Y_THREE_CRV())
    cydai = lazy_contract(enter_cyy3crv_action.CY_DAI())
    fee_distribution = lazy_contract(
        enter_cyy3crv_action.THREE_CRV_FEE_DISTRIBUTION())
    cream = lazy_contract(enter_cyy3crv_action.CREAM())

    tokens = [dai, usdc, usdt, threecrv, y3crv, cyy3crv]

    balances = get_balances(account, tokens)
    print_token_balances(balances, f"{account} balances")

    claimable_3crv = get_claimable_3crv(account, fee_distribution,
                                        min_3crv_to_claim)

    # TODO: calculate/prompt for these
    min_3crv_mint_amount = 1
    tip_3crv = 0
    tip_address = _resolve_address(
        "tip.satoshiandkin.eth"
    )  # TODO: put this on a subdomain and uses an immutable
    min_cream_liquidity = 1000

    # TODO: do this properly. use virtualprice and yearn's price calculation
    # print("warning! summed_balances is not actually priced in USD")
    # summed_balances = enter_data.dai + enter_data.usdc + enter_data.usdt + enter_data.threecrv + enter_data.y3crv + claimable_3crv
    # assert summed_balances > 100, "no coins"

    balances_for_3crv_pool = {
        dai: balances[dai],
        usdc: balances[usdc],
        usdt: balances[usdt],
    }

    approve(account, balances_for_3crv_pool, {}, threecrv_pool)

    threecrv_add_liquidity_tx = threecrv_pool.add_liquidity(
        [
            balances_for_3crv_pool[dai],
            balances_for_3crv_pool[usdc],
            balances_for_3crv_pool[usdt],
        ],
        min_3crv_mint_amount,
        {"from": account},
    )
    threecrv_add_liquidity_tx.info()

    if claimable_3crv >= min_3crv_to_claim:
        claim_tx = fee_distribution.claim({"from": account})
        claim_tx.info()

    # TODO: tip the developer in 3crv or ETH

    # deposit 3crv for y3crv
    balances_for_y3crv = get_balances(account, [threecrv])
    print_token_balances(balances, f"{account} balances_for_y3crv:")

    approve(account, balances_for_y3crv, {}, y3crv)

    y3crv_deposit_tx = y3crv.deposit(balances_for_y3crv[threecrv],
                                     {"from": account})
    y3crv_deposit_tx.info()

    # setup cream
    markets = []
    if not cream.checkMembership(account, cyy3crv):
        markets.append(cyy3crv)
    # TODO: do we need this? is this just for borrows?
    # if not cream.checkMembership(account, cydai):
    #     markets.append(cydai)

    if markets:
        enter_markets_tx = cream.enterMarkets(markets, {"from": account})
        enter_markets_tx.info()
    else:
        print("CREAM markets already entered")

    # deposit y3crv for cyy3crv
    balances_for_cyy3crv = get_balances(account, [y3crv])
    print_token_balances(balances_for_cyy3crv,
                         f"{account} balances for cyy3crv:")

    approve(account, balances_for_cyy3crv, {}, cyy3crv)

    mint_tx = cyy3crv.mint(balances_for_cyy3crv[y3crv], {"from": account})
    mint_tx.info()

    borrow_amount = int(balances_for_y3crv[threecrv] * 0.8)
    print(f"borrow_amount: {borrow_amount}")

    assert borrow_amount > 0

    # TODO: this could be better, figute out how to properly calculate the maximum safe borrow
    balances_before_borrow = get_balances(account, [cyy3crv, y3crv])
    print_token_balances(balances_before_borrow,
                         f"{account} balances before borrow:")

    # TODO: we could use `borrow_amount` here
    (
        cream_error,
        cream_liquidity,
        cream_shortfall,
    ) = cream.getHypotheticalAccountLiquidity(account, cydai, 0, 0)

    print(f"cream_error: {cream_error}")
    print(f"cream_liquidity: {cream_liquidity}")
    print(f"cream_shortfall: {cream_shortfall}")

    assert cream_error == 0, "cream error"
    assert cream_error == 0, "cream shortfall"
    assert cream_liquidity > borrow_amount, "no cream liquidity available for borrowing"

    borrow_tx = cydai.borrow(borrow_amount, {"from": account})

    print(f"enter simple success! {borrow_tx.return_value}")

    borrow_tx.info()

    num_events = len(borrow_tx.events)
    print(f"num events: {num_events}")

    balances = get_balances(account, tokens)
    print_token_balances(balances, f"{account} balances")

    # borrow returns non-zero on error
    assert borrow_tx.return_value == 0, "error borrowing DAI!"

    # TODO: where should these balances be?
    assert balances[cyy3crv] > 0
    assert balances[dai] == borrow_amount