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
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
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))
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()
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()
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()
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
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)
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)
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)
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)
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()
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))
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))
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()
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)
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))
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)
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)
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)
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)
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
def approve(self, gas_price: GasPrice): self.flopper.approve(self.flopper.vat(), hope_directly(gas_price=gas_price))
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)
def approve(self): self.flopper.approve(hope_directly())
def approve(self): self.flipper.approve(self.flipper.vat(), hope_directly())
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)
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)
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)
def approve(self, gas_price: GasPrice): assert isinstance(gas_price, GasPrice) self.clipper.approve(self.clipper.vat.address, hope_directly(gas_price=gas_price))