def buy_collateral_with_system_coin( geb: GfDeployment, c: Collateral, collateral_auction_house: FixedDiscountCollateralAuctionHouse, id: int, address: Address, bid_amount: Wad): assert (isinstance(geb, GfDeployment)) assert (isinstance(c, Collateral)) assert (isinstance(collateral_auction_house, FixedDiscountCollateralAuctionHouse)) assert (isinstance(id, int)) assert (isinstance(bid_amount, Wad)) collateral_auction_house.approve( collateral_auction_house.safe_engine(), approval_function=approve_safe_modification_directly( from_address=address)) previous_bid = collateral_auction_house.bids(id) c.approve(address) reserve_system_coin(geb, c, address, bid_amount, extra_collateral=Wad.from_number(2)) TestAuctionKeeperCollateralFlashSwap.buy_collateral( collateral_auction_house, id, address, bid_amount)
def test_should_increase_gas_price_of_pending_transactions_if_model_increases_gas_price(self, auction_id): # given collateral_auction_house = self.collateral.collateral_auction_house if not isinstance(collateral_auction_house, FixedDiscountCollateralAuctionHouse): return (model, model_factory) = models(self.keeper, auction_id) # when bid_price = Wad.from_number(20.0) reserve_system_coin(self.geb, self.collateral, self.keeper_address, bid_price * bid_size * 2, Wad.from_number(2)) self.keeper.rebalance_system_coin() simulate_model_output(model=model, price=bid_price, gas_price=10) # and self.start_ignoring_transactions() # and self.keeper.check_all_auctions() self.keeper.check_for_bids() # and self.end_ignoring_transactions() # and simulate_model_output(model=model, price=bid_price, gas_price=15) # and self.keeper.check_for_bids() wait_for_other_threads() # then #assert collateral_auction_house.bids(auction_id).raised_amount == Rad(bid_price * bid_size) assert self.web3.eth.getBlock('latest', full_transactions=True).transactions[0].gasPrice == 15
def cleanup_debt(cls, web3, geb, address): # Cancel out debt unqueued_unauctioned_debt = geb.accounting_engine.unqueued_unauctioned_debt( ) total_on_auction_debt = geb.accounting_engine.total_on_auction_debt() system_coin_needed = unqueued_unauctioned_debt + total_on_auction_debt if system_coin_needed == Rad(0): return # Add Wad(1) when going from Rad to Wad reserve_system_coin(geb, geb.collaterals['ETH-A'], get_our_address(web3), Wad(system_coin_needed) + Wad(1)) # transfer system coin to accounting engine geb.safe_engine.transfer_internal_coins( get_our_address(web3), geb.accounting_engine.address, system_coin_needed).transact(from_address=get_our_address(web3)) system_coin_accounting_engine = geb.safe_engine.coin_balance( geb.accounting_engine.address) assert system_coin_accounting_engine >= system_coin_needed assert geb.accounting_engine.settle_debt( unqueued_unauctioned_debt).transact() assert geb.accounting_engine.unqueued_unauctioned_debt() == Rad(0) assert geb.accounting_engine.total_queued_debt() == Rad(0) if geb.accounting_engine.total_on_auction_debt() > Rad(0): geb.accounting_engine.cancel_auctioned_debt_with_surplus( total_on_auction_debt).transact() assert geb.accounting_engine.total_on_auction_debt() == Rad(0) assert geb.safe_engine.debt_balance( geb.accounting_engine.address) == Rad(0)
def liquidate_safe(cls, web3, geb, c, auction_income_recipient_address, our_address): safe = geb.safe_engine.safe(c.collateral_type, auction_income_recipient_address) delta_debt = max_delta_debt(geb, c, auction_income_recipient_address) - Wad.from_number(1) assert geb.safe_engine.modify_safe_collateralization(c.collateral_type, auction_income_recipient_address, Wad(0), delta_debt).transact(from_address=auction_income_recipient_address) safe = geb.safe_engine.safe(c.collateral_type, auction_income_recipient_address) set_collateral_price(geb, c, Wad.from_number(10)) # Ensure the SAFE isn't safe assert not is_safe_safe(geb.safe_engine.collateral_type(c.collateral_type.name), safe) # Determine how many liquidations will be required liquidation_quantity = Wad(geb.liquidation_engine.liquidation_quantity(c.collateral_type)) liquidations_required = math.ceil(safe.generated_debt / liquidation_quantity) print(f"locked_collateral={safe.locked_collateral} generated_debt={safe.generated_debt} so {liquidations_required} liquidations are required") c.collateral_auction_house.approve(geb.safe_engine.address, approval_function=approve_safe_modification_directly(from_address=our_address)) # First auction that will be started first_auction_id = c.collateral_auction_house.auctions_started() + 1 # liquidate and bid on each auction for _ in range(liquidations_required): auction_id = liquidate(geb, c, safe) assert auction_id > 0 auction = c.collateral_auction_house.bids(auction_id) bid_amount = Wad(auction.amount_to_raise) + Wad(1) reserve_system_coin(geb, c, our_address, bid_amount) assert c.collateral_auction_house.increase_bid_size(auction_id, auction.amount_to_sell, auction.amount_to_raise).transact(from_address=our_address) time_travel_by(web3, c.collateral_auction_house.total_auction_length()+1) for auction_id in range(first_auction_id, c.collateral_auction_house.auctions_started()+1): assert c.collateral_auction_house.settle_auction(auction_id).transact() set_collateral_price(geb, c, Wad.from_number(200)) safe = geb.safe_engine.safe(c.collateral_type, auction_income_recipient_address)
def auction_id(web3: Web3, geb: GfDeployment, auction_income_recipient_address, other_address) -> int: total_surplus = geb.safe_engine.coin_balance(geb.accounting_engine.address) unqueued_unauctioned_debt = ( geb.safe_engine.debt_balance(geb.accounting_engine.address) - geb.accounting_engine.total_queued_debt() ) - geb.accounting_engine.total_on_auction_debt() print( f'total_surplus={str(total_surplus)[:6]}, unqueued_unauctioned_debt={str(unqueued_unauctioned_debt)[:6]}' ) if unqueued_unauctioned_debt < total_surplus or ( unqueued_unauctioned_debt == Rad(0) and total_surplus == Rad(0)): # Liquidate SAFE c = geb.collaterals['ETH-B'] critical_safe = create_critical_safe(geb, c, Wad.from_number(2), other_address, draw_system_coin=False) collateral_auction_id = liquidate(geb, c, critical_safe) # Generate some system coin, bid on and win the collateral auction without covering all the debt reserve_system_coin(geb, c, auction_income_recipient_address, Wad.from_number(100), extra_collateral=Wad.from_number(1.1)) c.collateral_auction_house.approve( geb.safe_engine.address, approval_function=approve_safe_modification_directly( from_address=auction_income_recipient_address)) current_bid = c.collateral_auction_house.bids(collateral_auction_id) bid_amount = Rad.from_number(1.9) assert geb.safe_engine.coin_balance( auction_income_recipient_address) > bid_amount #assert c.collateral_auction_house.increase_bid_size(collateral_auction_id, current_bid.amount_to_sell, bid_amount).transact(from_address=auction_income_recipient_address) #time_travel_by(web3, c.collateral_auction_house.bid_duration()+1) #assert c.collateral_auction_house.settle_auction(collateral_auction_id).transact() pop_debt_and_settle_debt(web3, geb, past_blocks=1200, cancel_auctioned_debt=False) # Start the debt auction unqueued_unauctioned_debt = ( geb.safe_engine.debt_balance(geb.accounting_engine.address) - geb.accounting_engine.total_queued_debt() ) - geb.accounting_engine.total_on_auction_debt() assert geb.accounting_engine.debt_auction_bid_size( ) <= unqueued_unauctioned_debt assert geb.safe_engine.coin_balance( geb.accounting_engine.address) == Rad(0) assert geb.accounting_engine.auction_debt().transact( from_address=auction_income_recipient_address) return geb.debt_auction_house.auctions_started()
def eliminate_queued_debt(cls, web3, geb, keeper_address): if geb.safe_engine.debt_balance( geb.accounting_engine.address) == Rad(0): return # given the existence of queued debt c = geb.collaterals['ETH-A'] auction_id = c.collateral_auction_house.auctions_started() last_liquidation = geb.liquidation_engine.past_liquidations(10)[0] # when a bid covers the SAFE debt auction = c.collateral_auction_house.bids(auction_id) reserve_system_coin(geb, c, keeper_address, Wad(auction.amount_to_raise) + Wad(1)) c.collateral_auction_house.approve( c.collateral_auction_house.safe_engine(), approval_function=approve_safe_modification_directly( from_address=keeper_address)) c.approve(keeper_address) if isinstance(c.collateral_auction_house, EnglishCollateralAuctionHouse): assert c.collateral_auction_house.increase_bid_size( auction_id, auction.amount_to_sell, auction.amount_to_raise).transact(from_address=keeper_address) time_travel_by(web3, c.collateral_auction_house.bid_duration() + 1) assert c.collateral_auction_house.settle_auction( auction_id).transact() elif isinstance(c.collateral_auction_house, FixedDiscountCollateralAuctionHouse): assert c.collateral_auction_house.buy_collateral( auction_id, Wad(auction.amount_to_raise) + Wad(1)).transact(from_address=keeper_address) elif isinstance(c.collateral_auction_house, IncreasingDiscountCollateralAuctionHouse): assert c.collateral_auction_house.buy_collateral( auction_id, Wad(auction.amount_to_raise) + Wad(1)).transact(from_address=keeper_address) # when a bid covers the vow debt assert geb.accounting_engine.debt_queue_of( last_liquidation.block_time(web3)) > Rad(0) assert geb.accounting_engine.pop_debt_from_queue( last_liquidation.block_time(web3)).transact( from_address=keeper_address) assert geb.accounting_engine.settle_debt( geb.safe_engine.debt_balance( geb.accounting_engine.address)).transact() # then ensure queued debt has been auctioned off assert geb.safe_engine.debt_balance( geb.accounting_engine.address) == Rad(0)
def simulate_model_bid(self, geb: GfDeployment, c: Collateral, model: object, gas_price: Optional[int] = None): assert (isinstance(geb, GfDeployment)) assert (isinstance(c, Collateral)) assert (isinstance(gas_price, int)) or gas_price is None collateral_auction_house = c.collateral_auction_house initial_bid = collateral_auction_house.bids(model.id) assert initial_bid.amount_to_sell > Wad(0) our_bid = Wad.from_number(500) * initial_bid.amount_to_sell reserve_system_coin(geb, c, self.keeper_address, our_bid, extra_collateral=Wad.from_number(2)) simulate_model_output(model=model, price=Wad.from_number(500), gas_price=gas_price)
def test_should_detect_debt_auction(self, web3, c, geb, other_address, keeper_address): # given a count of debt auctions reserve_system_coin(geb, c, keeper_address, Wad.from_number(230)) auctions_started = geb.debt_auction_house.auctions_started() # and an undercollateralized SAFE is liquidated critical_safe = create_critical_safe(geb, c, Wad.from_number(1), other_address, draw_system_coin=False) assert geb.liquidation_engine.liquidate_safe( critical_safe.collateral_type, critical_safe).transact() # when the auction ends without debt being covered if isinstance(c.collateral_auction_house, EnglishCollateralAuctionHouse): time_travel_by( web3, c.collateral_auction_house.total_auction_length() + 1) # then ensure testchain is in the appropriate state total_surplus = geb.safe_engine.coin_balance( geb.accounting_engine.address) total_debt = geb.safe_engine.debt_balance( geb.accounting_engine.address) unqueued_unauctioned_debt = ( geb.safe_engine.debt_balance(geb.accounting_engine.address) - geb.accounting_engine.total_queued_debt() ) - geb.accounting_engine.total_on_auction_debt() debt_queue = geb.accounting_engine.total_queued_debt() debt_auction_bid_size = geb.accounting_engine.debt_auction_bid_size() wait = geb.accounting_engine.pop_debt_delay() assert total_surplus < total_debt assert unqueued_unauctioned_debt + debt_queue >= debt_auction_bid_size assert wait == 0 # when self.keeper.check_debt() wait_for_other_threads() # then ensure another debt auction was started auction_id = geb.debt_auction_house.auctions_started() assert auction_id == auctions_started + 1 # clean up by letting someone else bid and waiting until the auction ends self.decrease_sold_amount(auction_id, self.other_address, Wad.from_number(0.000012), self.debt_auction_bid_size) time_travel_by(web3, geb.debt_auction_house.bid_duration() + 1)
def test_auction_deleted_after_our_full_bid(self, auction_id): # given collateral_auction_house = self.collateral.collateral_auction_house if not isinstance(collateral_auction_house, FixedDiscountCollateralAuctionHouse): return (model, model_factory) = models(self.keeper, auction_id) # when self.keeper.check_all_auctions() wait_for_other_threads() initial_status = collateral_auction_house.bids(model.id) # then assert model.send_status.call_count == 1 # when bidding the full amount our_bid = Wad(initial_status.amount_to_raise) + Wad(1) reserve_system_coin(self.geb, self.collateral, self.keeper_address, our_bid, Wad.from_number(2)) assert self.geb.safe_engine.coin_balance(self.keeper_address) >= initial_status.amount_to_raise simulate_model_output(model=model, price=None) self.keeper.check_for_bids() # and checking auction status and sending auction status to model self.keeper.check_all_auctions() wait_for_other_threads() # and self.keeper.check_all_auctions() wait_for_other_threads() # ensure our bid was processed and auction has been deleted current_status = collateral_auction_house.bids(model.id) assert current_status.raised_amount == Rad(0) assert current_status.sold_amount == Wad(0) assert current_status.amount_to_raise == Rad(0) assert current_status.amount_to_sell == Wad(0) assert current_status.auction_deadline == 0 assert current_status.raised_amount == Rad(0)
def setup_method(self): self.web3 = get_web3() self.our_address = get_our_address(self.web3) self.keeper_address = get_keeper_address(self.web3) self.other_address = get_other_address(self.web3) self.auction_income_recipient_address = get_auction_income_recipient_address( self.web3) self.geb = get_geb(self.web3) self.debt_auction_house = self.geb.debt_auction_house self.debt_auction_house.approve( self.geb.safe_engine.address, approval_function=approve_safe_modification_directly( from_address=self.keeper_address)) self.debt_auction_house.approve( self.geb.safe_engine.address, approval_function=approve_safe_modification_directly( from_address=self.other_address)) self.keeper = AuctionKeeper(args=args( f"--eth-from {self.keeper_address} " f"--type debt " 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_system_coin(self.geb, self.geb.collaterals['ETH-C'], self.keeper_address, Wad.from_number(200.00000)) reserve_system_coin(self.geb, self.geb.collaterals['ETH-C'], self.other_address, Wad.from_number(200.00000)) self.debt_auction_bid_size = self.geb.accounting_engine.debt_auction_bid_size( ) # Rad
# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys from pyflex.numeric import Wad, Ray, Rad from tests.conftest import keeper_address, geb, other_address, reserve_system_coin, web3 geb = geb(web3()) collateral = geb.collaterals['ETH-C'] keeper_address = keeper_address(web3()) seller = other_address(web3()) amount = Wad.from_number(float(sys.argv[1])) assert amount > Wad(0) web3().eth.defaultAccount = seller.address collateral.approve(seller) geb.approve_system_coin(seller) reserve_system_coin(geb, geb.collaterals['ETH-C'], seller, amount, Wad.from_number(2)) assert geb.system_coin_adapter.exit(seller, amount).transact(from_address=seller) assert geb.system_coin.transfer_from(seller, keeper_address, amount).transact(from_address=seller) print(f'Purchased {str(amount)} system coin, keeper token balance is {str(geb.system_coin.balance_of(keeper_address))}')
def test_should_provide_model_with_updated_info_after_our_partial_bid(self, auction_id): # given collateral_auction_house = self.collateral.collateral_auction_house if not isinstance(collateral_auction_house, FixedDiscountCollateralAuctionHouse): return (model, model_factory) = models(self.keeper, auction_id) # when self.keeper.check_all_auctions() wait_for_other_threads() initial_status = collateral_auction_house.bids(model.id) # then assert model.send_status.call_count == 1 # when bidding less than the full amount our_balance = Wad(initial_status.amount_to_raise) / Wad.from_number(2) reserve_system_coin(self.geb, self.collateral, self.keeper_address, our_balance) assert initial_status.amount_to_raise != Rad(0) assert self.geb.safe_engine.coin_balance(self.keeper_address) > Rad(0) # Make our balance lte half of the auction size half_amount_to_raise = initial_status.amount_to_raise / Rad.from_number(2) if self.geb.safe_engine.coin_balance(self.keeper_address) >= half_amount_to_raise: burn_amount = self.geb.safe_engine.coin_balance(self.keeper_address) - half_amount_to_raise assert burn_amount < self.geb.safe_engine.coin_balance(self.keeper_address) self.geb.safe_engine.transfer_internal_coins(self.keeper_address, Address("0x0000000000000000000000000000000000000000"), burn_amount).transact() assert self.geb.safe_engine.coin_balance(self.keeper_address) <= half_amount_to_raise assert self.geb.safe_engine.coin_balance(self.keeper_address) > Rad(0) simulate_model_output(model=model, price=None) self.keeper.check_for_bids() # and checking auction status and sending auction status to model self.keeper.check_all_auctions() wait_for_other_threads() # and self.keeper.check_all_auctions() wait_for_other_threads() # then assert model.send_status.call_count > 1 # ensure our bid was processed current_status = collateral_auction_house.bids(model.id) assert current_status.amount_to_raise == initial_status.amount_to_raise assert current_status.amount_to_sell == initial_status.amount_to_sell assert current_status.auction_deadline == initial_status.auction_deadline assert current_status.raised_amount == Rad(our_balance) # and the last status sent to our model reflects our bid status = model.send_status.call_args[0][0] assert status.id == auction_id assert status.collateral_auction_house == collateral_auction_house.address assert status.surplus_auction_house is None assert status.debt_auction_house is None assert status.amount_to_sell == initial_status.amount_to_sell assert status.amount_to_raise == initial_status.amount_to_raise assert status.raised_amount == Rad(our_balance) assert status.auction_deadline == initial_status.auction_deadline # and auction is still active final_status = collateral_auction_house.bids(model.id) assert final_status.amount_to_raise == initial_status.amount_to_raise assert final_status.amount_to_sell == initial_status.amount_to_sell assert final_status.auction_deadline == initial_status.auction_deadline assert final_status.raised_amount == Rad(our_balance) #cleanup our_balance = Wad(initial_status.amount_to_raise) + Wad(1) reserve_system_coin(self.geb, self.collateral, self.keeper_address, our_balance) assert self.geb.safe_engine.coin_balance(self.keeper_address) >= initial_status.amount_to_raise simulate_model_output(model=model, price=None) self.keeper.check_for_bids() self.keeper.check_all_auctions() wait_for_other_threads() # ensure auction has been deleted current_status = collateral_auction_house.bids(model.id) assert current_status.raised_amount == Rad(0) assert current_status.sold_amount == Wad(0) assert current_status.amount_to_raise == Rad(0) assert current_status.amount_to_sell == Wad(0) assert current_status.auction_deadline == 0 assert current_status.raised_amount == Rad(0)