def test_get_or_clone():
    account = accounts[0]

    argobytes_factory = get_or_create_factory(account)

    argobytes_proxy = get_or_create_flash_borrower(account)

    argobytes_clone = get_or_clone(account, argobytes_factory, argobytes_proxy)

    print(argobytes_clone)
Esempio n. 2
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))
def argobytes_flash_clone(argobytes_factory, argobytes_flash_borrower):
    # on mainnet we use the (bytes32) salt to generate custom addresses, but we dont need that in our tests
    return get_or_clone(accounts[0], argobytes_factory, argobytes_flash_borrower)
Esempio 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))
def atomic_exit(account, tip_eth, tip_3crv):
    """Use a flash loan to withdraw from a leveraged cyy3crv position."""
    # TODO: we need an account with private keys
    print(f"Hello, {account}")

    # 0.5 is 0.5%
    # slippage = ['small', 'medium', 'high', 'bad idea']
    slippage_pct = 0.5  # TODO: this should be a click arg

    # 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_factory(account)
    argobytes_flash_borrower = get_or_create_flash_borrower(account)
    exit_cyy3crv_action = get_or_create(
        account, ArgobytesBrownieProject.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(), account)
    usdc = lazy_contract(exit_cyy3crv_action.USDC(), account)
    usdt = lazy_contract(exit_cyy3crv_action.USDT(), account)
    threecrv = lazy_contract(exit_cyy3crv_action.THREE_CRV(), account)
    threecrv_pool = lazy_contract(exit_cyy3crv_action.THREE_CRV_POOL(),
                                  account,
                                  force=True)
    y3crv = lazy_contract(exit_cyy3crv_action.Y_THREE_CRV(), account)
    cyy3crv = lazy_contract(exit_cyy3crv_action.CY_Y_THREE_CRV(), account)
    cydai = lazy_contract(exit_cyy3crv_action.CY_DAI(), account)
    lender = DyDxFlashLender

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

    # 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(tokens + [threecrv_pool, lender])

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

    print_token_balances(balances)

    dai_borrowed = cydai.borrowBalanceCurrent.call(argobytes_clone)
    assert dai_borrowed > 0, "No DAI position to exit from"

    print(f"dai_borrowed:      {dai_borrowed}")

    borrow_rate_per_block = cydai.borrowRatePerBlock.call()

    # TODO: how many blocks of interest should we add on? base this on gas speed?
    # TODO: is this right? i think it might be overshooting. i don't think it matters that much though
    interest_slippage_blocks = int((5 * 60) / get_average_block_time())
    # https://www.geeksforgeeks.org/python-program-for-compound-interest/
    flash_loan_amount = int(dai_borrowed * (pow(
        (1 + borrow_rate_per_block / 1e18), interest_slippage_blocks)))

    print(f"flash_loan_amount: {flash_loan_amount}")

    # safety check. make sure our interest doesn't add a giant amount
    assert flash_loan_amount <= dai_borrowed * (1 + slippage_pct / 100)

    flash_loan_fee = lender.flashFee(dai, flash_loan_amount)

    print(f"flash_loan_fee: {flash_loan_fee}")

    # TODO: safety check on the flash loan fee?

    # TODO: this is slightly high because we add 5 minutes of interest. we could spend gas subtracting out the unused slack, but i doubt its worthwhile
    max_3crv_burned = (
        threecrv_pool.calc_token_amount(
            # dai, usdc, usdt
            [
                flash_loan_amount + flash_loan_fee,
                0,
                0,
            ],
            # is_deposit
            False,
        ) * (1 + slippage_pct / 100))

    exit_data = ExitData(
        dai_flash_fee=flash_loan_fee,
        max_3crv_burned=max_3crv_burned,
        tip_3crv=tip_3crv,
        sender=account,
    )

    pprint(exit_data)

    safe_token_approve(account, balances, argobytes_clone)

    # flashloan through the clone
    exit_tx = argobytes_clone.flashBorrow(
        lender,
        dai,
        flash_loan_amount,
        ArgobytesAction(
            exit_cyy3crv_action,
            ArgobytesActionCallType.DELEGATE,
            False,
            "exit",
            *exit_data,
        ).tuple,
        {
            "value": tip_eth,
        },
    )

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

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

    print(f"gas used: {exit_tx.gas_used}")

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

    print("account balances")
    print_token_balances(get_balances(account, tokens))