Beispiel #1
0
    def setup_method(self):
        self.web3 = web3()
        self.our_address = our_address(self.web3)
        self.keeper_address = keeper_address(self.web3)
        self.other_address = other_address(self.web3)
        self.gal_address = gal_address(self.web3)
        self.mcd = mcd(self.web3)
        self.flopper = self.mcd.flopper
        self.flopper.approve(
            self.mcd.vat.address,
            approval_function=hope_directly(from_address=self.keeper_address))
        self.flopper.approve(
            self.mcd.vat.address,
            approval_function=hope_directly(from_address=self.other_address))

        self.keeper = AuctionKeeper(args=args(
            f"--eth-from {self.keeper_address} "
            f"--type flop "
            f"--from-block 1 "
            f"--bid-check-interval 0.05 "
            f"--model ./bogus-model.sh"),
                                    web3=self.web3)
        self.keeper.approve()

        reserve_dai(self.mcd, self.mcd.collaterals['ETH-C'],
                    self.keeper_address, Wad.from_number(200.00000))
        reserve_dai(self.mcd, self.mcd.collaterals['ETH-C'],
                    self.other_address, Wad.from_number(200.00000))

        self.sump = self.mcd.vow.sump()  # Rad
Beispiel #2
0
    def setup_method(self):
        self.web3 = web3()
        self.our_address = our_address(self.web3)
        self.keeper_address = keeper_address(self.web3)
        self.other_address = other_address(self.web3)
        self.gal_address = gal_address(self.web3)
        self.mcd = mcd(self.web3)
        self.flopper = self.mcd.flopper
        self.flopper.approve(
            self.mcd.vat.address,
            approval_function=hope_directly(from_address=self.keeper_address))
        self.flopper.approve(
            self.mcd.vat.address,
            approval_function=hope_directly(from_address=self.other_address))

        self.keeper = AuctionKeeper(args=args(
            f"--eth-from {self.keeper_address} "
            f"--type flop "
            f"--from-block 1 "
            f"--model ./bogus-model.sh"),
                                    web3=self.web3)
        self.keeper.approve()

        assert isinstance(self.keeper.gas_price, DynamicGasPrice)
        self.default_gas_price = self.keeper.gas_price.get_gas_price(0)

        reserve_dai(self.mcd, self.mcd.collaterals['ETH-C'],
                    self.keeper_address, Wad.from_number(200.00000))
        reserve_dai(self.mcd, self.mcd.collaterals['ETH-C'],
                    self.other_address, Wad.from_number(200.00000))

        self.sump = self.mcd.vow.sump()  # Rad
Beispiel #3
0
 def setup_class(cls):
     cls.web3 = web3()
     cls.our_address = our_address(cls.web3)
     cls.gal_address = gal_address(cls.web3)
     cls.keeper_address = keeper_address(cls.web3)
     cls.other_address = other_address(cls.web3)
     cls.mcd = mcd(cls.web3)
     cls.flopper = cls.mcd.flopper
     cls.flopper.approve(
         cls.mcd.vat.address,
         approval_function=hope_directly(from_address=cls.keeper_address))
     cls.flopper.approve(
         cls.mcd.vat.address,
         approval_function=hope_directly(from_address=cls.other_address))
Beispiel #4
0
 def approve(self):
     self.strategy.approve()
     time.sleep(2)
     if self.dai_join:
         self.dai_join.approve(hope_directly(), self.vat.address)
         time.sleep(2)
         self.dai_join.dai().approve(self.dai_join.address).transact()
Beispiel #5
0
def kick(web3: Web3, mcd: DssDeployment, gal_address, other_address) -> int:
    joy = mcd.vat.dai(mcd.vow.address)
    woe = (mcd.vat.sin(mcd.vow.address) - mcd.vow.sin()) - mcd.vow.ash()
    print(f'joy={str(joy)[:6]}, woe={str(woe)[:6]}')

    if woe < joy:
        # Bite gal CDP
        c = mcd.collaterals['ETH-B']
        unsafe_cdp = create_unsafe_cdp(mcd, c, Wad.from_number(2), other_address, draw_dai=False)
        flip_kick = bite(mcd, c, unsafe_cdp)

        # Generate some Dai, bid on and win the flip auction without covering all the debt
        reserve_dai(mcd, c, gal_address, Wad.from_number(100), extra_collateral=Wad.from_number(1.1))
        c.flipper.approve(mcd.vat.address, approval_function=hope_directly(from_address=gal_address))
        current_bid = c.flipper.bids(flip_kick)
        bid = Rad.from_number(1.9)
        assert mcd.vat.dai(gal_address) > bid
        assert c.flipper.tend(flip_kick, current_bid.lot, bid).transact(from_address=gal_address)
        time_travel_by(web3, c.flipper.ttl()+1)
        assert c.flipper.deal(flip_kick).transact()

    flog_and_heal(web3, mcd, past_blocks=1200, kiss=False)

    # Kick off the flop auction
    woe = (mcd.vat.sin(mcd.vow.address) - mcd.vow.sin()) - mcd.vow.ash()
    assert mcd.vow.sump() <= woe
    assert mcd.vat.dai(mcd.vow.address) == Rad(0)
    assert mcd.vow.flop().transact(from_address=gal_address)
    return mcd.flopper.kicks()
Beispiel #6
0
    def test_should_terminate_model_if_auction_expired_due_to_ttl_and_somebody_else_won_it(
            self, mcd, c, kick, other_address, keeper):
        # given
        (model, model_factory) = models(keeper, kick)
        flipper = c.flipper

        # when
        keeper.check_all_auctions()
        wait_for_other_threads()
        # then
        model_factory.create_model.assert_called_once()
        model.terminate.assert_not_called()

        # when
        flipper.approve(
            flipper.vat(),
            approval_function=hope_directly(from_address=other_address))
        new_bid_amount = Rad.from_number(85)
        self.tend_with_dai(mcd, c, flipper, kick, other_address,
                           new_bid_amount)
        # and
        time_travel_by(self.web3, flipper.ttl() + 1)
        # and
        keeper.check_all_auctions()
        wait_for_other_threads()
        # then
        model_factory.create_model.assert_called_once()
        model.terminate.assert_called_once()

        # cleanup
        assert flipper.deal(kick).transact()
Beispiel #7
0
def create_flop_auction(mcd: DssDeployment, deployment_address: Address,
                        our_address: Address):
    assert isinstance(mcd, DssDeployment)
    assert isinstance(deployment_address, Address)
    assert isinstance(our_address, Address)

    flopper = mcd.flopper
    print(f"Before Debt: {mcd.vat.sin(mcd.vow.address)}")
    if mcd.vow.woe() <= mcd.vow.sump():
        create_debt(mcd.web3, mcd, our_address, deployment_address)
    print(f"After Debt: {mcd.vat.sin(mcd.vow.address)}")

    # Kick off the flop auction
    kicks = flopper.kicks()
    assert kicks == 0
    assert len(flopper.active_auctions()) == 0
    assert mcd.vat.dai(mcd.vow.address) == Rad(0)
    assert mcd.vow.flop().transact()
    kicks = flopper.kicks()
    assert kicks == 1
    assert len(flopper.active_auctions()) == 1
    check_active_auctions(flopper)
    current_bid = flopper.bids(kicks)

    bid = Wad.from_number(0.000005)
    flopper.approve(mcd.vat.address,
                    approval_function=hope_directly(from_address=our_address))
    assert mcd.vat.can(our_address, flopper.address)
    dent(flopper, kicks, our_address, bid, current_bid.bid)
    current_bid = flopper.bids(kicks)
    assert current_bid.guy == our_address
Beispiel #8
0
    def liquidate_urn(cls, web3, mcd, c, gal_address, our_address):
        # Ensure the CDP isn't safe
        urn = mcd.vat.urn(c.ilk, gal_address)
        dart = max_dart(mcd, c, gal_address) - Wad.from_number(1)
        assert mcd.vat.frob(c.ilk, gal_address, Wad(0),
                            dart).transact(from_address=gal_address)
        set_collateral_price(mcd, c, Wad.from_number(66))
        assert not is_cdp_safe(mcd.vat.ilk(c.ilk.name), urn)

        # Bite and kick off the auction
        kick = bite(mcd, c, urn)
        assert kick > 0

        # Bid on and win the auction
        auction = c.flipper.bids(kick)
        bid = Wad(auction.tab) + Wad(1)
        reserve_dai(mcd, c, our_address, bid)
        c.flipper.approve(
            mcd.vat.address,
            approval_function=hope_directly(from_address=our_address))
        assert c.flipper.tend(kick, auction.lot,
                              auction.tab).transact(from_address=our_address)
        time_travel_by(web3, c.flipper.ttl() + 1)
        assert c.flipper.deal(kick).transact()

        set_collateral_price(mcd, c, Wad.from_number(200))
        urn = mcd.vat.urn(c.ilk, gal_address)
        assert urn.ink == Wad(0)
        assert urn.art == Wad(0)
Beispiel #9
0
    def test_frob_other_account(self, web3, mcd, other_address):
        # given
        collateral = mcd.collaterals['ETH-A']
        collateral.approve(other_address)
        mcd.dai_adapter.approve(hope_directly(from_address=other_address),
                                mcd.vat.address)
        urn = mcd.vat.urn(collateral.ilk, other_address)
        assert urn.address == other_address

        # when
        wrap_eth(mcd, other_address, Wad(10))
        assert collateral.gem.balance_of(other_address) >= Wad(10)
        assert collateral.gem == collateral.adapter.gem()
        collateral.gem.approve(collateral.adapter.address)
        assert collateral.adapter.join(
            other_address, Wad(3)).transact(from_address=other_address)
        assert mcd.vat.frob(collateral.ilk, other_address, Wad(3),
                            Wad(10)).transact(from_address=other_address)

        # then
        assert mcd.vat.urn(collateral.ilk,
                           other_address).art == urn.art + Wad(10)

        # rollback
        cleanup_urn(mcd, collateral, other_address)
Beispiel #10
0
    def eliminate_queued_debt(cls, web3, mcd, keeper_address):
        if mcd.vat.sin(mcd.vow.address) == Rad(0):
            return

        # given the existence of queued debt
        c = mcd.collaterals['ETH-A']
        kick = c.flipper.kicks()
        last_bite = mcd.cat.past_bites(10)[0]

        # when a bid covers the CDP debt
        auction = c.flipper.bids(kick)
        reserve_dai(mcd, c, keeper_address, Wad(auction.tab) + Wad(1))
        c.flipper.approve(
            c.flipper.vat(),
            approval_function=hope_directly(from_address=keeper_address))
        c.approve(keeper_address)
        assert c.flipper.tend(
            kick, auction.lot,
            auction.tab).transact(from_address=keeper_address)
        time_travel_by(web3, c.flipper.ttl() + 1)
        assert c.flipper.deal(kick).transact()

        # when a bid covers the vow debt
        assert mcd.vow.sin_of(last_bite.era(web3)) > Rad(0)
        assert mcd.vow.flog(
            last_bite.era(web3)).transact(from_address=keeper_address)
        assert mcd.vow.heal(mcd.vat.sin(mcd.vow.address)).transact()

        # then ensure queued debt has been auctioned off
        assert mcd.vat.sin(mcd.vow.address) == Rad(0)
Beispiel #11
0
    def liquidate_urn(cls, web3, mcd, c, gal_address, our_address):
        # Ensure the CDP isn't safe
        urn = mcd.vat.urn(c.ilk, gal_address)
        dart = max_dart(mcd, c, gal_address) - Wad.from_number(1)
        assert mcd.vat.frob(c.ilk, gal_address, Wad(0), dart).transact(from_address=gal_address)
        set_collateral_price(mcd, c, Wad.from_number(66))
        assert not is_cdp_safe(mcd.vat.ilk(c.ilk.name), urn)

        # Determine how many bites will be required
        dunk = Wad(mcd.cat.dunk(c.ilk))
        urn = mcd.vat.urn(c.ilk, gal_address)
        bites_required = math.ceil(urn.art / dunk)
        print(f"art={urn.art} and dunk={dunk} so {bites_required} bites are required")
        c.flipper.approve(mcd.vat.address, approval_function=hope_directly(from_address=our_address))
        first_kick = c.flipper.kicks() + 1

        # Bite and bid on each auction
        for i in range(bites_required):
            kick = bite(mcd, c, urn)
            assert kick > 0
            auction = c.flipper.bids(kick)
            print(f"biting {i} of {bites_required} and bidding tab of {auction.tab}")
            bid = Wad(auction.tab) + Wad(1)
            reserve_dai(mcd, c, our_address, bid)
            print(f"bidding tab of {auction.tab}")
            assert c.flipper.tend(kick, auction.lot, auction.tab).transact(from_address=our_address)

        time_travel_by(web3, c.flipper.ttl())
        for kick in range(first_kick, c.flipper.kicks()):
            assert c.flipper.deal(kick).transact()

        set_collateral_price(mcd, c, Wad.from_number(200))
        urn = mcd.vat.urn(c.ilk, gal_address)
Beispiel #12
0
def create_debt(web3: Web3, mcd: DssDeployment, our_address: Address, deployment_address: Address):
    assert isinstance(web3, Web3)
    assert isinstance(mcd, DssDeployment)
    assert isinstance(our_address, Address)
    assert isinstance(deployment_address, Address)

    # Create a vault
    collateral = mcd.collaterals['ETH-A']
    ilk = collateral.ilk
    wrap_eth(mcd, deployment_address, Wad.from_number(1))
    collateral.approve(deployment_address)
    assert collateral.adapter.join(deployment_address, Wad.from_number(1)).transact(
        from_address=deployment_address)
    frob(mcd, collateral, deployment_address, dink=Wad.from_number(1), dart=Wad(0))
    dart = max_dart(mcd, collateral, deployment_address) - Wad(1)
    frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)
    assert not mcd.cat.can_bite(ilk, mcd.vat.urn(collateral.ilk, deployment_address))

    # Undercollateralize by dropping the spot price, and then bite the vault
    to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)
    set_collateral_price(mcd, collateral, to_price)
    urn = mcd.vat.urn(collateral.ilk, deployment_address)
    assert urn.ink is not None and urn.art is not None
    assert ilk.spot is not None
    safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(urn.ink) * ilk.spot
    assert not safe
    assert mcd.cat.can_bite(collateral.ilk, urn)
    assert mcd.cat.bite(collateral.ilk, urn).transact()
    flip_kick = collateral.flipper.kicks()

    # Generate some Dai, bid on and win the flip auction without covering all the debt
    wrap_eth(mcd, our_address, Wad.from_number(10))
    collateral.approve(our_address)
    assert collateral.adapter.join(our_address, Wad.from_number(10)).transact(from_address=our_address)
    web3.eth.defaultAccount = our_address.address
    frob(mcd, collateral, our_address, dink=Wad.from_number(10), dart=Wad.from_number(200))
    collateral.flipper.approve(mcd.vat.address, approval_function=hope_directly())
    current_bid = collateral.flipper.bids(flip_kick)
    urn = mcd.vat.urn(collateral.ilk, our_address)
    assert Rad(urn.art) > current_bid.tab
    bid = Rad.from_number(6)
    TestFlipper.tend(collateral.flipper, flip_kick, our_address, current_bid.lot, bid)
    mcd.vat.can(our_address, collateral.flipper.address)
    wait(mcd, our_address, collateral.flipper.ttl()+1)
    assert collateral.flipper.deal(flip_kick).transact()

    # Raise debt from the queue (note that vow.wait is 0 on our testchain)
    bites = mcd.cat.past_bites(100)
    for bite in bites:
        era_bite = bite.era(web3)
        assert era_bite > int(datetime.now().timestamp()) - 120
        assert mcd.vow.sin_of(era_bite) > Rad(0)
        assert mcd.vow.flog(era_bite).transact()
        assert mcd.vow.sin_of(era_bite) == Rad(0)
    # Cancel out surplus and debt
    dai_vow = mcd.vat.dai(mcd.vow.address)
    assert dai_vow <= mcd.vow.woe()
    assert mcd.vow.heal(dai_vow).transact()
    assert mcd.vow.woe() >= mcd.vow.sump()
Beispiel #13
0
    def approve(self, usr: Address):
        """
        Allows the user to move this collateral into and out of their CDP.

        Args
            usr: User making transactions with this collateral
        """
        self.adapter.approve(hope_directly(from_address=usr), self.flipper.vat())
        self.adapter.approve_token(directly(from_address=usr))
Beispiel #14
0
    def approve(self, usr: Address, **kwargs):
        """
        Allows the user to move this collateral into and out of their CDP.

        Args
            usr: User making transactions with this collateral
        """
        gas_price = kwargs['gas_price'] if 'gas_price' in kwargs else DefaultGasPrice()
        self.adapter.approve(hope_directly(from_address=usr, gas_price=gas_price), self.flipper.vat())
        self.adapter.approve_token(directly(from_address=usr, gas_price=gas_price))
Beispiel #15
0
    def approve_dai(self, usr: Address):
        """
        Allows the user to draw Dai from and repay Dai to their CDPs.

        Args
            usr: Recipient of Dai from one or more CDPs
        """
        assert isinstance(usr, Address)

        self.dai_adapter.approve(approval_function=hope_directly(from_address=usr), source=self.vat.address)
        self.dai.approve(self.dai_adapter.address).transact()
Beispiel #16
0
    def tend_with_dai(mcd: DssDeployment, c: Collateral, flipper: Flipper, id: int, address: Address, bid: Rad):
        assert (isinstance(mcd, DssDeployment))
        assert (isinstance(c, Collateral))
        assert (isinstance(flipper, Flipper))
        assert (isinstance(id, int))
        assert (isinstance(bid, Rad))

        flipper.approve(flipper.vat(), approval_function=hope_directly(from_address=address))
        previous_bid = flipper.bids(id)
        c.approve(address)
        reserve_dai(mcd, c, address, Wad(bid), extra_collateral=Wad.from_number(2))
        TestAuctionKeeperFlipper.tend(flipper, id, address, previous_bid.lot, bid)
Beispiel #17
0
    def test_scenario(self, web3, mcd, flopper, our_address, other_address,
                      deployment_address):
        create_debt(web3, mcd, our_address, deployment_address)

        # Kick off the flop auction
        assert flopper.kicks() == 0
        assert len(flopper.active_auctions()) == 0
        assert mcd.vat.dai(mcd.vow.address) == Rad(0)
        assert mcd.vow.flop().transact()
        kick = flopper.kicks()
        assert kick == 1
        assert len(flopper.active_auctions()) == 1
        check_active_auctions(flopper)
        current_bid = flopper.bids(kick)
        log = self.last_log(flopper)
        assert isinstance(log, Flopper.KickLog)
        assert log.id == kick
        assert log.lot == current_bid.lot
        assert log.bid == current_bid.bid
        assert log.gal == mcd.vow.address

        # Allow the auction to expire, and then resurrect it
        time_travel_by(web3, flopper.tau() + 1)
        assert flopper.tick(kick).transact()
        assert flopper.bids(kick).lot == current_bid.lot * flopper.pad()

        # Bid on the resurrected auction
        bid = Wad.from_number(0.000005)
        flopper.approve(mcd.vat.address, hope_directly())
        assert mcd.vat.can(our_address, flopper.address)
        TestFlopper.dent(flopper, kick, our_address, bid, current_bid.bid)
        current_bid = flopper.bids(kick)
        assert current_bid.guy == our_address

        # Confirm victory
        time_travel_by(web3, flopper.ttl() + 1)
        assert flopper.live()
        now = int(datetime.now().timestamp())
        assert (current_bid.tic < now
                and current_bid.tic != 0) or current_bid.end < now
        mkr_before = mcd.mkr.balance_of(our_address)
        assert flopper.deal(kick).transact(from_address=our_address)
        mkr_after = mcd.mkr.balance_of(our_address)
        assert mkr_after > mkr_before
        log = self.last_log(flopper)
        assert isinstance(log, Flopper.DealLog)
        assert log.usr == our_address
        assert log.id == kick

        # Cleanup
        collateral = mcd.collaterals['ETH-A']
        set_collateral_price(mcd, collateral, Wad.from_number(230))
Beispiel #18
0
    def approve_dai(self, usr: Address, **kwargs):
        """
        Allows the user to draw Dai from and repay Dai to their CDPs.

        Args
            usr: Recipient of Dai from one or more CDPs
        """
        assert isinstance(usr, Address)

        gas_price = kwargs['gas_price'] if 'gas_price' in kwargs else DefaultGasPrice()
        self.dai_adapter.approve(approval_function=hope_directly(from_address=usr, gas_price=gas_price),
                                 source=self.vat.address)
        self.dai.approve(self.dai_adapter.address).transact(from_address=usr, gas_price=gas_price)
Beispiel #19
0
def create_flip_auction(mcd: DssDeployment, deployment_address: Address,
                        our_address: Address):
    assert isinstance(mcd, DssDeployment)
    assert isinstance(our_address, Address)
    assert isinstance(deployment_address, Address)

    # Create a CDP
    collateral = mcd.collaterals['ETH-A']
    ilk = collateral.ilk
    wrap_eth(mcd, deployment_address, Wad.from_number(1))
    collateral.approve(deployment_address)
    assert collateral.adapter.join(
        deployment_address,
        Wad.from_number(1)).transact(from_address=deployment_address)
    frob(mcd,
         collateral,
         deployment_address,
         dink=Wad.from_number(1),
         dart=Wad(0))
    dart = max_dart(mcd, collateral, deployment_address) - Wad(1)
    frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)

    # Undercollateralize and bite the CDP
    to_price = Wad(mcd.web3.toInt(collateral.pip.read())) / Wad.from_number(2)
    set_collateral_price(mcd, collateral, to_price)
    urn = mcd.vat.urn(collateral.ilk, deployment_address)
    ilk = mcd.vat.ilk(ilk.name)
    safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(urn.ink) * ilk.spot
    assert not safe
    assert mcd.cat.can_bite(collateral.ilk, Urn(deployment_address))
    assert mcd.cat.bite(collateral.ilk, Urn(deployment_address)).transact()
    flip_kick = collateral.flipper.kicks()

    # Generate some Dai, bid on the flip auction without covering all the debt
    wrap_eth(mcd, our_address, Wad.from_number(10))
    collateral.approve(our_address)
    assert collateral.adapter.join(
        our_address, Wad.from_number(10)).transact(from_address=our_address)
    mcd.web3.eth.defaultAccount = our_address.address
    frob(mcd,
         collateral,
         our_address,
         dink=Wad.from_number(10),
         dart=Wad.from_number(200))
    collateral.flipper.approve(mcd.vat.address,
                               approval_function=hope_directly())
    current_bid = collateral.flipper.bids(flip_kick)
    urn = mcd.vat.urn(collateral.ilk, our_address)
    assert Rad(urn.art) > current_bid.tab
    bid = Rad.from_number(6)
    tend(collateral.flipper, flip_kick, our_address, current_bid.lot, bid)
Beispiel #20
0
    def test_should_provide_model_with_updated_info_after_somebody_else_bids(
            self, mcd, c, other_address, keeper):
        # given
        flipper = c.flipper
        kick = flipper.kicks()
        (model, model_factory) = models(keeper, kick)

        # when
        keeper.check_all_auctions()
        wait_for_other_threads()
        # then
        assert model.send_status.call_count == 1

        # when
        flipper.approve(
            flipper.vat(),
            approval_function=hope_directly(from_address=other_address))
        previous_bid = flipper.bids(kick)
        new_bid_amount = Rad.from_number(80)
        self.tend_with_dai(mcd, c, flipper, model.id, other_address,
                           new_bid_amount)
        # and
        keeper.check_all_auctions()
        wait_for_other_threads()
        # then
        assert model.send_status.call_count > 1
        # and
        status = model.send_status.call_args[0][0]
        assert status.id == kick
        assert status.flipper == flipper.address
        assert status.flapper is None
        assert status.flopper is None
        assert status.bid == new_bid_amount
        assert status.lot == previous_bid.lot
        assert status.tab == previous_bid.tab
        assert status.beg > Wad.from_number(1)
        assert status.guy == other_address
        assert status.era > 0
        assert status.end > status.era
        assert status.tic > status.era
        assert status.price == (Wad(new_bid_amount) / previous_bid.lot)
Beispiel #21
0
    def setup_class(cls):
        super().setup_class()
        assert cls.collateral.clipper
        assert not cls.collateral.flipper
        cls.keeper = AuctionKeeper(args=args(f"--eth-from {cls.keeper_address.address} "
                                              f"--type clip "
                                              f"--from-block 1 "
                                              f"--ilk {cls.collateral.ilk.name} "
                                              f"--model ./bogus-model.sh"), web3=cls.mcd.web3)
        cls.keeper.approve()

        # Clean up the urn used for bark testing such that it doesn't impact our flip tests
        assert get_collateral_price(cls.collateral) == Wad.from_number(200)
        if not repay_urn(cls.mcd, cls.collateral, cls.gal_address):
            liquidate_urn(cls.mcd, cls.collateral, cls.gal_address, cls.keeper_address)

        # approve another taker
        cls.collateral.approve(cls.other_address)
        cls.collateral.clipper.approve(cls.mcd.vat.address, hope_directly(from_address=cls.other_address))

        assert isinstance(cls.keeper.gas_price, DynamicGasPrice)
        cls.default_gas_price = cls.keeper.gas_price.get_gas_price(0)
Beispiel #22
0
    def test_scenario(self, web3, mcd, flopper, our_address, other_address,
                      deployment_address):
        self.create_debt(web3, mcd, our_address, deployment_address)

        # Kick off the flop auction
        assert flopper.kicks() == 0
        assert len(flopper.active_auctions()) == 0
        assert mcd.vat.dai(mcd.vow.address) == Rad(0)
        # TODO: Get bid_id return value from transaction rather than guessing bid_id==1
        assert mcd.vow.flop().transact()
        kick = flopper.kicks()
        assert kick == 1
        assert len(flopper.active_auctions()) == 1
        current_bid = flopper.bids(kick)

        # Allow the auction to expire, and then resurrect it
        wait(mcd, our_address, flopper.tau() + 1)
        assert flopper.tick(kick).transact()

        # Bid on the resurrected auction
        flopper.approve(mcd.vat.address, hope_directly())
        assert mcd.vat.can(our_address, flopper.address)
        TestFlopper.dent(flopper, kick, our_address, Wad.from_number(0.5),
                         current_bid.bid)
        current_bid = flopper.bids(kick)
        assert current_bid.guy == our_address

        # Confirm victory
        wait(mcd, our_address, flopper.ttl() + 1)
        assert flopper.live()
        now = int(datetime.now().timestamp())
        assert (current_bid.tic < now
                and current_bid.tic != 0) or current_bid.end < now
        mkr_before = mcd.mkr.balance_of(our_address)
        assert flopper.deal(kick).transact(from_address=our_address)
        mkr_after = mcd.mkr.balance_of(our_address)
        assert mkr_after > mkr_before
Beispiel #23
0
 def approve(self, gas_price: GasPrice):
     self.flopper.approve(self.flopper.vat(),
                          hope_directly(gas_price=gas_price))
Beispiel #24
0
def liquidate_urn(mcd,
                  c: Collateral,
                  address: Address,
                  bidder: Address,
                  c_dai: Collateral = None):
    assert isinstance(c, Collateral)
    assert isinstance(address, Address)
    assert isinstance(bidder, Address)

    if c_dai is None:
        c_dai = c

    # Ensure the CDP isn't safe
    urn = mcd.vat.urn(c.ilk, address)
    if is_cdp_safe(c.ilk, urn):
        assert urn.ink > Wad(0)
        safe_price = urn.art / Wad(mcd.spotter.mat(c.ilk)) / urn.ink
        print(
            f"current_price={float(get_collateral_price(c))}, safe_price={float(safe_price)}"
        )
        set_collateral_price(mcd, c, safe_price / Wad.from_number(2))
        c.ilk = mcd.vat.ilk(c.ilk.name)
        assert not is_cdp_safe(c.ilk, urn)

    if c.clipper:
        c.clipper.approve(mcd.vat.address,
                          approval_function=hope_directly(from_address=bidder))
        # Bark to kick the auction
        assert mcd.dog.bark(c.ilk, urn).transact()
        kick = c.clipper.kicks()
        (needs_redo, auction_price, lot, tab) = c.clipper.status(kick)
        purchase_dai(Wad(tab) + Wad(1), address)
        assert mcd.dai_adapter.join(address,
                                    Wad(tab) +
                                    Wad(1)).transact(from_address=address)
        assert mcd.vat.dai(address) >= tab
        bid_price = tab / Rad(lot)
        while auction_price > bid_price:
            time_travel_by(mcd.web3, 1)
            (needs_redo, auction_price, lot, tab) = c.clipper.status(kick)
        print(
            f"taking lot {lot} on auction {kick} at {bid_price} with {mcd.vat.dai(bidder)} Dai remaining"
        )
        assert c.clipper.take(kick, lot,
                              bid_price).transact(from_address=address)

    elif c.flipper:
        c.flipper.approve(mcd.vat.address,
                          approval_function=hope_directly(from_address=bidder))
        # Determine how many bites will be required
        dunk: Rad = mcd.cat.dunk(c.ilk)
        box: Rad = mcd.cat.box()
        urn = mcd.vat.urn(c.ilk, address)
        bites_required = math.ceil(urn.art / Wad(dunk))
        print(
            f"art={float(urn.art)} and dunk={float(dunk)} so {bites_required} bites are required"
        )
        first_kick = c.flipper.kicks() + 1

        while mcd.cat.can_bite(c.ilk, urn):
            box_kick = c.flipper.kicks() + 1

            while mcd.cat.can_bite(c.ilk, urn):
                # Bite and bid on each auction
                next_kick = c.flipper.kicks() + 1
                print(
                    f"biting {next_kick} ({next_kick - first_kick + 1} of {bites_required})"
                )
                kick = bite(mcd, c, urn)
                auction = c.flipper.bids(kick)
                reserve_dai(mcd, c_dai, bidder, Wad(auction.tab) + Wad(1))
                print(
                    f"bidding tab {auction.tab} on auction {kick} for {auction.lot} with {mcd.vat.dai(bidder)} Dai remaining"
                )
                assert c.flipper.tend(
                    kick, auction.lot,
                    auction.tab).transact(from_address=bidder)
                urn = mcd.vat.urn(c.ilk, address)

            time_travel_by(mcd.web3, c.flipper.ttl() + 3)
            for kick in range(box_kick, c.flipper.kicks() + 1):
                print(
                    f"dealing {kick} ({kick - first_kick + 1} of {bites_required})"
                )
                assert c.flipper.deal(kick).transact()

    set_collateral_price(mcd, c, Wad.from_number(200))
    repay_urn(mcd, c, address)
    assert urn.art == Wad(0)
    assert urn.ink == Wad(0)
Beispiel #25
0
 def approve(self):
     self.flopper.approve(hope_directly())
Beispiel #26
0
 def approve(self):
     self.flipper.approve(self.flipper.vat(), hope_directly())
Beispiel #27
0
    def test_scenario(self, web3, mcd, collateral, clipper, our_address,
                      other_address, deployment_address):
        dirt_before = mcd.dog.dog_dirt()
        vice_before = mcd.vat.vice()
        sin_before = mcd.vow.sin()

        # Create a vault
        ilk = collateral.ilk
        ink = Wad.from_number(1)
        wrap_eth(mcd, deployment_address, ink)
        collateral.approve(deployment_address)
        assert collateral.adapter.join(
            deployment_address, ink).transact(from_address=deployment_address)
        frob(mcd, collateral, deployment_address, dink=ink, dart=Wad(0))
        dart = max_dart(mcd, collateral, deployment_address) - Wad(1)
        frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)

        # Mint and withdraw all the Dai
        mcd.approve_dai(deployment_address)
        assert mcd.dai_adapter.exit(
            deployment_address, dart).transact(from_address=deployment_address)

        # Undercollateralize the vault
        to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)
        set_collateral_price(mcd, collateral, to_price)
        urn = mcd.vat.urn(collateral.ilk, deployment_address)
        ilk = mcd.vat.ilk(ilk.name)
        safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(
            urn.ink) * ilk.spot
        assert not safe
        assert clipper.active_count() == 0

        # Bark the vault, which moves debt to the vow and kicks the clipper
        dai_before_bark: Rad = mcd.vat.dai(our_address)
        urn = mcd.vat.urn(collateral.ilk, deployment_address)
        assert urn.ink > Wad(0)
        tab = urn.art * ilk.rate  # Wad
        assert tab == dart
        assert mcd.dog.bark(ilk, urn).transact()
        barks = mcd.dog.past_barks(1)
        assert len(barks) == 1
        last_bite = barks[0]
        assert last_bite.due > Rad(0)
        assert clipper.active_count() == 1
        kick = clipper.kicks()
        assert kick == 1
        assert len(clipper.active_auctions()) == 1
        assert clipper.active_auctions()[0].id == 1
        assert mcd.active_auctions()['clips']['ETH-B'][0].id == 1
        urn = mcd.vat.urn(collateral.ilk, deployment_address)
        (needs_redo, price, lot, tab) = clipper.status(kick)
        assert not needs_redo
        assert price == Ray.from_number(172.5)
        assert lot == ink
        assert float(tab) == 105.0
        # Check vat, vow, and dog
        assert urn.ink == Wad(0)
        assert vice_before < mcd.vat.vice()
        assert sin_before < mcd.vow.sin()
        assert dirt_before < mcd.dog.dog_dirt()
        # Check the clipper
        current_sale = clipper.sales(kick)
        assert isinstance(current_sale, Clipper.Sale)
        assert current_sale.pos == 0
        assert float(current_sale.tab) == 105.0
        assert current_sale.lot == ink
        assert current_sale.usr == deployment_address
        assert current_sale.tic > 0
        assert round(current_sale.top, 1) == price
        coin = Rad(clipper.tip() + (current_sale.tab * clipper.chip()))
        # Confirm we received our liquidation reward
        dai_after_bark: Rad = mcd.vat.dai(our_address)
        assert dai_after_bark == dai_before_bark + coin
        kick_log = self.last_log(clipper)
        assert isinstance(kick_log, Clipper.KickLog)
        assert kick_log.id == kick
        assert kick_log.top == current_sale.top
        assert kick_log.tab == current_sale.tab
        assert kick_log.lot == current_sale.lot
        assert kick_log.usr == deployment_address
        assert kick_log.kpr == our_address
        assert kick_log.coin == coin

        # Wrap some eth and handle approvals before bidding
        eth_required = Wad(
            current_sale.tab / Rad(ilk.spot)) * Wad.from_number(1.1)
        wrap_eth(mcd, our_address, eth_required)
        collateral.approve(our_address)
        assert collateral.adapter.join(
            our_address, eth_required).transact(from_address=our_address)
        clipper.approve(
            mcd.vat.address,
            approval_function=hope_directly(from_address=our_address))

        # Ensure we cannot take collateral below the current price
        (needs_redo, price, lot, tab) = clipper.status(kick)
        with pytest.raises(AssertionError):
            clipper.validate_take(kick, ink, price - Ray.from_number(1))
        assert not clipper.take(
            kick, ink, Ray.from_number(140)).transact(from_address=our_address)

        # Take some collateral with max above the top price
        clipper.validate_take(kick, Wad.from_number(0.07),
                              Ray.from_number(180))
        assert web3.eth.defaultAccount == our_address.address
        assert clipper.take(
            kick, Wad.from_number(0.07),
            Ray.from_number(180)).transact(from_address=our_address)
        (needs_redo, price, lot, tab) = clipper.status(kick)
        assert not needs_redo
        current_sale = clipper.sales(kick)
        assert current_sale.lot > Wad(0)
        assert current_sale.top > price
        assert Rad(0) < current_sale.tab < kick_log.tab
        first_take_log = self.last_log(clipper)
        assert first_take_log.id == 1
        assert first_take_log.max == Ray.from_number(180)
        assert first_take_log.price == price
        assert first_take_log.lot == current_sale.lot
        assert first_take_log.usr == deployment_address
        assert first_take_log.sender == our_address
        assert round(first_take_log.owe,
                     18) == round(Rad.from_number(0.07) * Rad(price), 18)

        # Allow the auction to expire, and then resurrect it
        # TODO: If abaci contract is ever wrapped, read tau from it
        time_travel_by(web3, 24)
        (needs_redo, price, lot, tab) = clipper.status(kick)
        assert needs_redo
        assert len(clipper.active_auctions()) == clipper.active_count()
        assert clipper.redo(kick, our_address).transact()
        (needs_redo, price, lot, tab) = clipper.status(kick)
        assert not needs_redo
        current_sale = clipper.sales(kick)
        assert current_sale.lot > Wad(0)
        redo_log = self.last_log(clipper)
        assert isinstance(redo_log, Clipper.RedoLog)
        assert redo_log.id == kick
        assert redo_log.top == current_sale.top
        assert redo_log.tab == current_sale.tab
        assert redo_log.lot == current_sale.lot
        assert redo_log.usr == deployment_address
        assert redo_log.kpr == our_address
        coin = Rad(clipper.tip() + (current_sale.tab * clipper.chip()))
        assert round(float(redo_log.coin), 18) == round(float(coin), 18)

        # Sleep until price has gone down enough to bid with remaining Dai
        dai = mcd.vat.dai(our_address)
        last_price = price
        print(
            f"Bid cost={float(price * Ray(current_sale.lot))}, Dai balance={float(dai)}"
        )
        while price * Ray(current_sale.lot) > Ray(dai):
            print(
                f"Bid cost {price * Ray(current_sale.lot)} exceeds Dai balance {dai}"
            )
            time_travel_by(web3, 2)
            (needs_redo, price, lot, tab) = clipper.status(kick)
            assert price < last_price
            assert not needs_redo
            last_price = price
        clipper.validate_take(kick, current_sale.lot, price)
        assert clipper.take(kick, current_sale.lot,
                            price).transact(from_address=our_address)
        current_sale = clipper.sales(kick)
        assert current_sale.lot == Wad(0)
        assert current_sale.tab == Rad(0)
        assert clipper.active_count() == 0
        assert len(clipper.active_auctions()) == 0

        # Ensure we can retrieve our collateral
        collateral_before = collateral.gem.balance_of(our_address)
        assert collateral.adapter.exit(our_address,
                                       ink).transact(from_address=our_address)
        collateral_after = collateral.gem.balance_of(our_address)
        assert collateral_before < collateral_after

        # Cleanup
        set_collateral_price(mcd, collateral, Wad.from_number(230))
        cleanup_urn(mcd, collateral, our_address)
Beispiel #28
0
    def test_scenario(self, web3, mcd, collateral, flipper, our_address,
                      other_address, deployment_address):
        # Create a vault
        kicks_before = flipper.kicks()
        ilk = collateral.ilk
        ink = Wad.from_number(0.5)
        wrap_eth(mcd, deployment_address, ink)
        collateral.approve(deployment_address)
        assert collateral.adapter.join(
            deployment_address, ink).transact(from_address=deployment_address)
        frob(mcd, collateral, deployment_address, dink=ink, dart=Wad(0))
        dart = max_dart(mcd, collateral, deployment_address) - Wad(1)
        frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart)

        # Mint and withdraw all the Dai
        mcd.approve_dai(deployment_address)
        assert mcd.dai_adapter.exit(
            deployment_address, dart).transact(from_address=deployment_address)
        assert mcd.dai.balance_of(deployment_address) == dart

        # Undercollateralize the vault
        to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2)
        set_collateral_price(mcd, collateral, to_price)
        urn = mcd.vat.urn(collateral.ilk, deployment_address)
        ilk = mcd.vat.ilk(ilk.name)
        assert ilk.rate is not None
        assert ilk.spot is not None
        safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(
            urn.ink) * ilk.spot
        assert not safe
        assert len(flipper.active_auctions()) == 0
        litter_before = mcd.cat.litter()

        # Bite the vault, which moves debt to the vow and kicks the flipper
        urn = mcd.vat.urn(collateral.ilk, deployment_address)
        assert urn.ink > Wad(0)
        art = min(urn.art, Wad(mcd.cat.dunk(ilk)))  # Wad
        tab = art * ilk.rate  # Wad
        assert tab == dart
        assert mcd.cat.can_bite(ilk, urn)
        assert mcd.cat.bite(ilk, urn).transact()
        kick = flipper.kicks()
        assert kick == kicks_before + 1
        urn = mcd.vat.urn(collateral.ilk, deployment_address)
        # Check vat, vow, and cat
        assert urn.ink == Wad(0)
        assert urn.art == dart - art
        assert mcd.vat.vice() > Rad(0)
        assert mcd.vow.sin() == Rad(tab)
        bites = mcd.cat.past_bites(1)
        assert len(bites) == 1
        last_bite = bites[0]
        assert last_bite.tab > Rad(0)
        assert last_bite.id == 1
        litter_after = mcd.cat.litter()
        assert litter_before < litter_after
        # Check the flipper
        current_bid = flipper.bids(kick)
        assert isinstance(current_bid, Flipper.Bid)
        assert current_bid.lot > Wad(0)
        assert current_bid.tab > Rad(0)
        assert current_bid.bid == Rad(0)
        # Cat doesn't incorporate the liquidation penalty (chop), but the kicker includes it.
        # Awaiting word from @dc why this is so.
        #assert last_bite.tab == current_bid.tab
        log = self.last_log(flipper)
        assert isinstance(log, Flipper.KickLog)
        assert log.id == kick
        assert log.lot == current_bid.lot
        assert log.bid == current_bid.bid
        assert log.tab == current_bid.tab
        assert log.usr == deployment_address
        assert log.gal == mcd.vow.address

        # Allow the auction to expire, and then resurrect it
        time_travel_by(web3, flipper.tau() + 1)
        assert flipper.tick(kick).transact()

        # Wrap some eth and handle approvals before bidding
        eth_required = Wad(
            current_bid.tab / Rad(ilk.spot)) * Wad.from_number(1.13)
        wrap_eth(mcd, other_address, eth_required)
        collateral.approve(other_address)
        assert collateral.adapter.join(
            other_address, eth_required).transact(from_address=other_address)
        wrap_eth(mcd, our_address, eth_required)
        collateral.approve(our_address)
        assert collateral.adapter.join(
            our_address, eth_required).transact(from_address=our_address)

        # Test the _tend_ phase of the auction
        flipper.approve(
            mcd.vat.address,
            approval_function=hope_directly(from_address=other_address))
        # Add Wad(1) to counter precision error converting tab from Rad to Wad
        frob(mcd,
             collateral,
             other_address,
             dink=eth_required,
             dart=Wad(current_bid.tab) + Wad(1))
        urn = mcd.vat.urn(collateral.ilk, other_address)
        assert Rad(urn.art) >= current_bid.tab
        # Bid the tab to instantly transition to dent stage
        TestFlipper.tend(flipper, kick, other_address, current_bid.lot,
                         current_bid.tab)
        current_bid = flipper.bids(kick)
        assert current_bid.guy == other_address
        assert current_bid.bid == current_bid.tab
        assert len(flipper.active_auctions()) == 1
        check_active_auctions(flipper)
        log = self.last_log(flipper)
        assert isinstance(log, Flipper.TendLog)
        assert log.guy == current_bid.guy
        assert log.id == current_bid.id
        assert log.lot == current_bid.lot
        assert log.bid == current_bid.bid

        # Test the _dent_ phase of the auction
        flipper.approve(
            mcd.vat.address,
            approval_function=hope_directly(from_address=our_address))
        frob(mcd,
             collateral,
             our_address,
             dink=eth_required,
             dart=Wad(current_bid.tab) + Wad(1))
        lot = current_bid.lot - Wad.from_number(0.3)
        assert flipper.beg() * lot <= current_bid.lot
        TestFlipper.dent(flipper, kick, our_address, lot, current_bid.tab)
        current_bid = flipper.bids(kick)
        assert current_bid.guy == our_address
        assert current_bid.bid == current_bid.tab
        assert current_bid.lot == lot
        log = self.last_log(flipper)
        assert isinstance(log, Flipper.DentLog)
        assert log.guy == current_bid.guy
        assert log.id == current_bid.id
        assert log.lot == current_bid.lot
        assert log.bid == current_bid.bid

        # Exercise _deal_ after bid has expired
        time_travel_by(web3, flipper.ttl() + 1)
        now = datetime.now().timestamp()
        assert 0 < current_bid.tic < now or current_bid.end < now
        assert flipper.deal(kick).transact(from_address=our_address)
        assert len(flipper.active_auctions()) == 0
        log = self.last_log(flipper)
        assert isinstance(log, Flipper.DealLog)
        assert log.usr == our_address

        # Grab our collateral
        collateral_before = collateral.gem.balance_of(our_address)
        assert collateral.adapter.exit(
            our_address, current_bid.lot).transact(from_address=our_address)
        collateral_after = collateral.gem.balance_of(our_address)
        assert collateral_before < collateral_after

        # Cleanup
        set_collateral_price(mcd, collateral, Wad.from_number(230))
        cleanup_urn(mcd, collateral, other_address)
Beispiel #29
0
 def approve(self, address: Address):
     assert isinstance(address, Address)
     self.clipper.approve(self.clipper.vat.address, approval_function=hope_directly(from_address=address))
     self.collateral.approve(address)
Beispiel #30
0
 def approve(self, gas_price: GasPrice):
     assert isinstance(gas_price, GasPrice)
     self.clipper.approve(self.clipper.vat.address,
                          hope_directly(gas_price=gas_price))