def test_should_overbid_itself_if_model_has_updated_the_price(self, kick): # given (model, model_factory) = models(self.keeper, kick) # when simulate_model_output(model=model, price=Wad.from_number(100.0)) # and self.keeper.check_all_auctions() self.keeper.check_for_bids() wait_for_other_threads() # then assert round(Rad(self.flopper.bids(kick).lot), 2) == round(self.sump / Rad.from_number(100.0), 2) # when simulate_model_output(model=model, price=Wad.from_number(110.0)) self.keeper.check_all_auctions() self.keeper.check_for_bids() wait_for_other_threads() # then assert self.lot_implies_price(kick, Wad.from_number(110.0)) # cleanup time_travel_by(self.web3, self.flopper.ttl() + 1) assert self.flopper.deal(kick).transact()
def __init__(self, lognote: LogNote): self.guy = Address(lognote.usr) self.id = Web3.toInt(lognote.arg1) self.lot = Wad(Web3.toInt(lognote.arg2)) self.bid = Rad(Web3.toInt(lognote.get_bytes_at_index(2))) self.block = lognote.block self.tx_hash = lognote.tx_hash
def test_skim(self, mcd, our_address): ilk = mcd.collaterals['ETH-A'].ilk urn = mcd.vat.urn(ilk, our_address) owe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate * mcd.end.tag(ilk) assert owe > Ray(0) wad = min(Ray(urn.ink), owe) print(f"owe={owe} wad={wad}") assert mcd.end.skim(ilk, our_address).transact() assert mcd.vat.urn(ilk, our_address).art == Wad(0) assert mcd.vat.urn(ilk, our_address).ink > Wad(0) assert mcd.vat.sin(mcd.vow.address) > Rad(0) assert mcd.vat.debt() > Rad(0) assert mcd.vat.vice() > Rad(0)
def __init__(self, log): args = log['args'] self.id = args['id'] self.lot = Rad(args['lot']) self.bid = Wad(args['bid']) self.block = log['blockNumber'] self.tx_hash = log['transactionHash'].hex()
def test_should_bid_even_if_there_is_already_a_bidder(self, kick): # given (model, model_factory) = models(self.keeper, kick) mkr_before = self.mcd.mkr.balance_of(self.keeper_address) # and lot = Wad.from_number(0.000016) assert self.flopper.dent(kick, lot, self.sump).transact(from_address=self.other_address) assert self.flopper.bids(kick).lot == lot # when simulate_model_output(model=model, price=Wad.from_number(825.0)) # and self.keeper.check_all_auctions() self.keeper.check_for_bids() wait_for_other_threads() # then auction = self.flopper.bids(kick) assert auction.lot != lot assert round(auction.bid / Rad(auction.lot), 2) == round(Rad.from_number(825.0), 2) mkr_after = self.mcd.mkr.balance_of(self.keeper_address) assert mkr_before == mkr_after # cleanup time_travel_by(self.web3, self.flopper.ttl() + 1) assert self.flopper.deal(kick).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_start_a_new_model_and_provide_it_with_info_on_auction_kick( self, kick): # given (model, model_factory) = models(self.keeper, kick) # when self.keeper.check_all_auctions() wait_for_other_threads() # then model_factory.create_model.assert_called_once_with( Parameters(flipper=None, flapper=None, flopper=self.flopper.address, id=kick)) # and status = model.send_status.call_args[0][0] assert status.id == kick assert status.flipper is None assert status.flapper is None assert status.flopper == self.flopper.address assert status.bid > Rad.from_number(0) assert status.lot == self.mcd.vow.dump() assert status.tab is None assert status.beg > Wad.from_number(1) assert status.guy == self.mcd.vow.address assert status.era > 0 assert status.end < status.era + self.flopper.tau() + 1 assert status.tic == 0 assert status.price == Wad(status.bid / Rad(status.lot))
def test_should_change_gas_strategy_when_model_output_changes(self, kick): # given (model, model_factory) = models(self.keeper, kick) lot = self.flapper.bids(kick).lot # when first_bid = Wad.from_number(0.0000009) simulate_model_output(model=model, price=first_bid, gas_price=2000) # and self.keeper.check_all_auctions() self.keeper.check_for_bids() wait_for_other_threads() # then assert self.web3.eth.getBlock( 'latest', full_transactions=True).transactions[0].gasPrice == 2000 # when second_bid = Wad.from_number(0.0000006) simulate_model_output(model=model, price=second_bid) # and self.keeper.check_all_auctions() self.keeper.check_for_bids() wait_for_other_threads() # then assert self.flapper.bids(kick).bid == Wad(lot / Rad(second_bid)) assert self.web3.eth.getBlock('latest', full_transactions=True).transactions[0].gasPrice == \ self.default_gas_price # when third_bid = Wad.from_number(0.0000003) new_gas_price = int(self.default_gas_price * 1.25) simulate_model_output(model=model, price=third_bid, gas_price=new_gas_price) # and self.keeper.check_all_auctions() self.keeper.check_for_bids() wait_for_other_threads() # then assert self.flapper.bids(kick).bid == Wad(lot / Rad(third_bid)) assert self.web3.eth.getBlock( 'latest', full_transactions=True).transactions[0].gasPrice == new_gas_price # cleanup time_travel_by(self.web3, self.flapper.ttl() + 1) assert self.flapper.deal(kick).transact()
def validate_frob(self, ilk: Ilk, address: Address, dink: Wad, dart: Wad): """Helps diagnose `frob` transaction failures by asserting on `require` conditions in the contract""" assert isinstance(ilk, Ilk) assert isinstance(address, Address) assert isinstance(dink, Wad) assert isinstance(dart, Wad) urn = self.urn(ilk, address) ilk = self.ilk(ilk.name) logger.debug( f"urn.ink={urn.ink}, urn.art={urn.art}, dink={dink}, dart={dart}, " f"ilk.rate={ilk.rate}, debt={str(self.debt())}") ink = urn.ink + dink art = urn.art + dart ilk_art = ilk.art + dart rate = ilk.rate gem = self.gem(ilk, urn.address) - dink dai = self.dai(urn.address) + Rad(rate * dart) debt = self.debt() + Rad(rate * dart) # stablecoin debt does not increase cool = dart <= Wad(0) # collateral balance does not decrease firm = dink >= Wad(0) nice = cool and firm # Vault remains under both collateral and total debt ceilings under_collateral_debt_ceiling = Rad(ilk_art * rate) <= ilk.line if not under_collateral_debt_ceiling: logger.warning( f"Vault would exceed collateral debt ceiling of {ilk.line}") under_total_debt_ceiling = debt < self.line() if not under_total_debt_ceiling: logger.warning( f"Vault would exceed total debt ceiling of {self.line()}") calm = under_collateral_debt_ceiling and under_total_debt_ceiling safe = (urn.art * rate) <= ink * ilk.spot assert calm or cool assert nice or safe assert Rad(ilk_art * rate) >= ilk.dust or (art == Wad(0)) assert rate != Ray(0) assert self.live()
def __init__(self, log): self.ilk = Ilk.fromBytes(log['args']['ilk']) self.urn = Urn(Address(log['args']['urn'])) self.ink = Wad(log['args']['ink']) self.art = Wad(log['args']['art']) self.tab = Rad(log['args']['tab']) self.flip = Address(log['args']['flip']) self.raw = log
def simulate_frob(mcd: DssDeployment, collateral: Collateral, address: Address, dink: Wad, dart: Wad): assert isinstance(mcd, DssDeployment) assert isinstance(collateral, Collateral) assert isinstance(address, Address) assert isinstance(dink, Wad) assert isinstance(dart, Wad) urn = mcd.vat.urn(collateral.ilk, address) ilk = mcd.vat.ilk(collateral.ilk.name) print( f"urn.ink={urn.ink}, urn.art={urn.art}, ilk.art={ilk.art}, dink={dink}, dart={dart}" ) ink = urn.ink + dink art = urn.art + dart ilk_art = ilk.art + dart rate = ilk.rate gem = mcd.vat.gem(collateral.ilk, urn.address) - dink dai = mcd.vat.dai(urn.address) + Rad(rate * dart) debt = mcd.vat.debt() + Rad(rate * dart) # stablecoin debt does not increase cool = dart <= Wad(0) # collateral balance does not decrease firm = dink >= Wad(0) nice = cool and firm # CDP remains under both collateral and total debt ceilings under_collateral_debt_ceiling = Rad(ilk_art * rate) <= ilk.line if not under_collateral_debt_ceiling: print(f"CDP would exceed collateral debt ceiling of {ilk.line}") under_total_debt_ceiling = debt < mcd.vat.line() if not under_total_debt_ceiling: print(f"CDP would exceed total debt ceiling of {mcd.vat.line()}") calm = under_collateral_debt_ceiling and under_total_debt_ceiling safe = (urn.art * rate) <= ink * ilk.spot assert calm or cool assert nice or safe assert Rad(ilk_art * rate) >= ilk.dust or (art == Wad(0)) assert rate != Ray(0) assert mcd.vat.live()
def test_pack(self, mcd, our_address): assert mcd.end.bag(our_address) == Wad(0) assert mcd.end.debt() > Rad(0) assert mcd.dai.approve(mcd.end.address).transact() assert mcd.vat.dai(our_address) >= Rad.from_number(10) # FIXME: `pack` fails, possibly because we're passing 0 to `vat.flux` assert mcd.end.pack(Wad.from_number(10)).transact() assert mcd.end.bag(our_address) == Wad.from_number(10)
def test_gem(self, our_address: Address, d: DssDeployment): collateral = d.collaterals[0] # when assert collateral.adapter.join(Urn(our_address), Wad(10)).transact() # then assert d.vat.gem(collateral.ilk, our_address) == Rad(Wad(10))
def reconcile_debt(self, joy: Rad, ash: Rad, woe: Rad): assert isinstance(joy, Rad) assert isinstance(ash, Rad) assert isinstance(woe, Rad) if ash > Rad(0): if joy > ash: self.vow.kiss(ash).transact(gas_price=self.gas_price) else: self.vow.kiss(joy).transact(gas_price=self.gas_price) return if woe > Rad(0): joy = self.vat.dai(self.vow.address) if joy > woe: self.vow.heal(woe).transact(gas_price=self.gas_price) else: self.vow.heal(joy).transact(gas_price=self.gas_price)
def __init__(self, lognote: LogNote): assert isinstance(lognote, LogNote) self.src = Address(Web3.toHex(lognote.arg1)[26:]) self.dst = Address(Web3.toHex(lognote.arg2)[26:]) self.dart = Rad(int.from_bytes(lognote.get_bytes_at_index(2), byteorder="big", signed=True)) self.block = lognote.block self.tx_hash = lognote.tx_hash
def max_dart(mcd: DssDeployment, collateral: Collateral, our_address: Address) -> Wad: assert isinstance(mcd, DssDeployment) assert isinstance(collateral, Collateral) assert isinstance(our_address, Address) urn = mcd.vat.urn(collateral.ilk, our_address) ilk = mcd.vat.ilk(collateral.ilk.name) # change in art = (collateral balance * collateral price with safety margin) - CDP's stablecoin debt dart = urn.ink * ilk.spot - Wad(Ray(urn.art) * ilk.rate) # change in debt must also take the rate into account dart = dart * Wad(Ray.from_number(1) / ilk.rate) # prevent the change in debt from exceeding the collateral debt ceiling if (Rad(urn.art) + Rad(dart)) >= ilk.line: print("max_dart is avoiding collateral debt ceiling") dart = Wad(ilk.line - Rad(urn.art)) # prevent the change in debt from exceeding the total debt ceiling debt = mcd.vat.debt() + Rad(ilk.rate * dart) line = Rad(ilk.line) if (debt + Rad(dart)) >= line: print("max_dart is avoiding total debt ceiling") dart = Wad(debt - Rad(urn.art)) assert dart > Wad(0) return dart
def max_dart(mcd: DssDeployment, collateral: Collateral, our_address: Address) -> Wad: """Determines how much stablecoin should be reserved in an `urn` to make it as poorly collateralized as possible, such that a small change to the collateral price could trip the liquidation ratio.""" assert isinstance(mcd, DssDeployment) assert isinstance(collateral, Collateral) assert isinstance(our_address, Address) urn = mcd.vat.urn(collateral.ilk, our_address) ilk = mcd.vat.ilk(collateral.ilk.name) # change in art = (collateral balance * collateral price with safety margin) - CDP's stablecoin debt dart = urn.ink * ilk.spot - Wad(Ray(urn.art) * ilk.rate) # change in debt must also take the rate into account dart = dart * Wad(Ray.from_number(1) / ilk.rate) # prevent the change in debt from exceeding the collateral debt ceiling if (Rad(urn.art) + Rad(dart)) >= ilk.line: print("max_dart is avoiding collateral debt ceiling") dart = Wad(ilk.line - Rad(urn.art)) # prevent the change in debt from exceeding the total debt ceiling debt = mcd.vat.debt() + Rad(ilk.rate * dart) line = Rad(ilk.line) if (debt + Rad(dart)) >= line: print("max_dart is avoiding total debt ceiling") dart = Wad(debt - Rad(urn.art)) assert dart > Wad(0) return dart
def __init__(self, log): self.ilk = Ilk.fromBytes(log['args']['ilk']) self.urn = Urn(Address(log['args']['urn'])) self.ink = Wad(log['args']['ink']) self.art = Wad(log['args']['art']) self.due = Rad(log['args']['due']) self.clip = Address(log['args']['clip']) self.id = int(log['args']['id']) self.raw = log
def mint_dai(mcd: DssDeployment, amount: Wad, ilkName: str, our_address: Address): startingAmount = mcd.dai.balance_of(our_address) dai = amount # Add collateral to our CDP and draw internal Dai collateral=mcd.collaterals[ilkName] ilk = mcd.vat.ilk(collateral.ilk.name) dink = Wad.from_number(1) dart = Wad( Rad(dai) / Rad(ilk.rate)) wrap_eth(mcd, our_address, dink) assert collateral.gem.balance_of(our_address) >= dink assert collateral.gem.approve(collateral.adapter.address).transact(from_address=our_address) assert collateral.adapter.join(our_address, dink).transact(from_address=our_address) frob(mcd, collateral, our_address, dink=dink, dart=dart) # Exit to Dai Token and make some checks assert mcd.vat.hope(mcd.dai_adapter.address).transact(from_address=our_address) assert mcd.dai_adapter.exit(our_address, dai).transact(from_address=our_address) assert mcd.dai.balance_of(our_address) == dai + startingAmount
def repay_urn(mcd, c: Collateral, address: Address) -> bool: assert isinstance(c, Collateral) assert isinstance(address, Address) mcd.approve_dai(address) urn = mcd.vat.urn(c.ilk, address) if urn.art > Wad(0): vat_dai = mcd.vat.dai(address) tab: Wad = urn.art * c.ilk.rate wipe: Wad = mcd.vat.get_wipe_all_dart(c.ilk, address) # if we have any Dai, repay all or part of the urn if vat_dai > Rad(0): vat_dai = vat_dai / Rad( c.ilk.rate) # adjust for Dai available for repayment repay_amount = min(wipe, tab, Wad(vat_dai)) print(f"wipe={wipe}, tab={tab}, vat_dai={vat_dai}") print(f"{c.ilk.name} dust is {float(c.ilk.dust)}") print( f"repaying {repay_amount} Dai on {c.ilk.name} urn {address}; art={urn.art}" ) assert mcd.vat.frob(c.ilk, address, Wad(0), repay_amount * -1).transact(from_address=address) urn = mcd.vat.urn(c.ilk, address) else: print(f"{address} has no Dai to repay tab of {float(tab)}") else: print(f"{c.ilk.name} urn {address} has no debt") min_ink = Wad(Ray(urn.art) * c.ilk.rate / c.ilk.spot) + Wad(1) if urn.ink > min_ink: ink_to_withdraw = urn.ink - min_ink print( f"withdrawing {float(ink_to_withdraw)} {c.ilk.name} from urn {address}" ) assert mcd.vat.frob(c.ilk, address, ink_to_withdraw * -1, Wad(0)).transact(from_address=address) urn = mcd.vat.urn(c.ilk, address) if urn.ink == Wad(0) and urn.art == Wad(0): print(f"{c.ilk.name} urn {address} was fully repaid") return True else: print(f"{c.ilk.name} urn {address} left ink={urn.ink} art={urn.art}") return False
def bid(self, id: int, price: Wad ) -> Tuple[Optional[Wad], Optional[Transact], Optional[Rad]]: assert isinstance(id, int) assert isinstance(price, Wad) bid = self.flipper.bids(id) # dent phase if bid.bid == bid.tab: our_lot = Wad(bid.bid / Rad(price)) if our_lot < self.min_lot: self.logger.debug( f"dent lot {our_lot} less than minimum {self.min_lot} for auction {id}" ) return None, None, None if (our_lot * self.beg <= bid.lot) and (our_lot < bid.lot): return price, self.flipper.dent(id, our_lot, bid.bid), bid.bid else: self.logger.debug( f"dent lot {our_lot} would not exceed the bid increment for auction {id}" ) return None, None, None # tend phase else: if bid.lot < self.min_lot: self.logger.debug( f"tend lot {bid.lot} less than minimum {self.min_lot} for auction {id}" ) return None, None, None our_bid = Rad.min(Rad(bid.lot) * price, bid.tab) our_price = price if our_bid < bid.tab else Wad(bid.bid) / bid.lot if (our_bid >= bid.bid * self.beg or our_bid == bid.tab) and our_bid > bid.bid: return our_price, self.flipper.tend(id, bid.lot, our_bid), Rad(our_bid) else: self.logger.debug( f"tend bid {our_bid} would not exceed the bid increment for auction {id}" ) return None, None, None
def status(self, id: int) -> (bool, Ray, Wad, Rad): """Indicates current state of the auction Args: id: Auction identifier. """ assert isinstance(id, int) (needs_redo, price, lot, tab) = self._contract.functions.getStatus(id).call() logging.debug(f"Auction {id} {'needs redo ' if needs_redo else ''}with price={float(Ray(price))} " f"lot={float(Wad(lot))} tab={float(Rad(tab))}") return needs_redo, Ray(price), Wad(lot), Rad(tab)
def test_ilk(self, mcd): assert mcd.vat.ilk('XXX') == Ilk('XXX', rate=Ray(0), ink=Wad(0), art=Wad(0), spot=Ray(0), line=Rad(0), dust=Rad(0)) ilk = mcd.collaterals["ETH-C"].ilk assert ilk.line == Rad.from_number(15000000) assert ilk.dust == Rad(0) representation = repr(ilk) assert "ETH-C" in representation
def test_move(self, mcd, our_address, other_address): # given collateral = mcd.collaterals['ETH-A'] collateral.approve(our_address) our_urn = mcd.vat.urn(collateral.ilk, our_address) wrap_eth(mcd, our_address, Wad(10)) assert collateral.adapter.join(our_address, Wad(3)).transact() assert mcd.vat.frob(collateral.ilk, our_address, Wad(3), Wad(10)).transact() other_balance_before = mcd.vat.dai(other_address) # when assert mcd.vat.move(our_address, other_address, Rad(Wad(10))).transact() # then other_balance_after = mcd.vat.dai(other_address) assert other_balance_before + Rad(Wad(10)) == other_balance_after # rollback cleanup_urn(mcd, collateral, our_address)
def can_bite(self, ilk: Ilk, urn: Urn) -> bool: """ Determine whether a vault can be liquidated Args: ilk: Collateral type urn: Identifies the vault holder or proxy """ assert isinstance(ilk, Ilk) assert isinstance(urn, Urn) ilk = self.vat.ilk(ilk.name) urn = self.vat.urn(ilk, urn.address) rate = ilk.rate # Collateral value should be less than the product of our stablecoin debt and the debt multiplier safe = Ray(urn.ink) * ilk.spot >= Ray(urn.art) * rate if safe: return False # Ensure there's room in the litter box box: Rad = self.box() litter: Rad = self.litter() room: Rad = box - litter if litter >= box: logger.debug( f"biting {urn.address} would exceed maximum Dai out for liquidation" ) return False if room < ilk.dust: return False # Prevent null auction (ilk.dunk [Rad], ilk.rate [Ray], ilk.chop [Wad]) assert self.chop(ilk) > Wad( 0 ) # ensure liquidations are enabled and this uses flipper instead of clipper dart: Wad = min( urn.art, Wad( min(self.dunk(ilk), room) / Rad(ilk.rate) / Rad(self.chop(ilk)))) dink: Wad = min(urn.ink, urn.ink * dart / urn.art) return dart > Wad(0) and dink > Wad(0)
def print_missed_flops(): total = 0 for i in range(mcd.flopper.kicks()): auction = mcd.flopper.bids(i) if auction.bid != Rad(0): total += 1 print( f"id={i}, bid={auction.bid}, lot={auction.lot}, guy={auction.guy}" ) print( f"{total} flops were missed, accounting for {str(mcd.vow.sump()*total)[:9]} Dai in debt" )
def open_cdp(mcd: DssDeployment, collateral: Collateral, address: Address, debtMultiplier: int = 1): assert isinstance(mcd, DssDeployment) assert isinstance(collateral, Collateral) assert isinstance(address, Address) collateral.approve(address) wrap_eth(mcd, address, Wad.from_number(20)) assert collateral.adapter.join(address, Wad.from_number(20)).transact(from_address=address) frob(mcd, collateral, address, Wad.from_number(20), Wad.from_number(20 * debtMultiplier)) assert mcd.vat.debt() >= Rad(Wad.from_number(20 * debtMultiplier)) assert mcd.vat.dai(address) >= Rad.from_number(20 * debtMultiplier)
def take_with_dai(self, id: int, price: Ray, address: Address): assert isinstance(id, int) assert isinstance(price, Ray) assert isinstance(address, Address) lot = self.clipper.sales(id).lot assert lot > Wad(0) cost = Wad(price * Ray(lot)) logging.debug(f"reserving {cost} Dai to bid on auction {id}") reserve_dai(self.mcd, self.dai_collateral, address, cost) assert self.mcd.vat.dai(address) >= Rad(cost) logging.debug(f"attempting to take clip {id} at {price}") self.clipper.validate_take(id, lot, price, address) assert self.clipper.take(id, lot, price, address).transact(from_address=address) # confirm that take finished the auction (needs_redo, auction_price, lot, tab) = self.clipper.status(id) assert not needs_redo assert lot == Wad(0) or tab == Rad(0)
def can_bite(self, ilk: Ilk, urn: Urn, box: Rad, dunk: Rad, chop: Wad) -> bool: # Typechecking intentionally omitted to improve performance rate = ilk.rate # Collateral value should be less than the product of our stablecoin debt and the debt multiplier safe = Ray(urn.ink) * ilk.spot >= Ray(urn.art) * rate if safe: return False # Ensure there's room in the litter box litter = self.cat.litter() room: Rad = box - litter if litter >= box: return False if room < ilk.dust: return False # Prevent null auction (ilk.dunk [Rad], ilk.rate [Ray], ilk.chop [Wad]) dart: Wad = min(urn.art, Wad(min(dunk, room) / Rad(ilk.rate) / Rad(chop))) dink: Wad = min(urn.ink, urn.ink * dart / urn.art) return dart > Wad(0) and dink > Wad(0)
def bids(self, id: int) -> Bid: """Returns the auction details. Args: id: Auction identifier. Returns: The auction details. """ assert(isinstance(id, int)) array = self._contract.call().bids(id) return Flipper.Bid(bid=Rad(array[0]), lot=Wad(array[1]), guy=Address(array[2]), tic=int(array[3]), end=int(array[4]), usr=Address(array[5]), gal=Address(array[6]), tab=Rad(array[7]))