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)
示例#4
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)