def test_multiply_by_wad(self): assert Ray.from_number(2) * Wad.from_number(3) == Ray.from_number(6) assert Ray.from_number(2) * Wad(3) == Ray(6000000000) assert Ray(2) * Wad(3) == Ray(0) assert Ray(2) * Wad(999999999999999999) == Ray(1) assert Ray(2) * Wad(1000000000000000000) == Ray(2)
def test_multiply_by_int(self): assert Ray.from_number(2) * 3 == Ray.from_number(6) assert Ray.from_number(2) * 1 == Ray.from_number(2)
def test_should_have_nice_printable_representation(self): for ray in [Ray(1), Ray(100), Ray.from_number(2.5), Ray(-1)]: assert repr(ray) == f"Ray({ray.value})"
def test_multiply(self): assert Ray.from_number(2) * Ray.from_number(3) == Ray.from_number(6) assert Ray.from_number(2) * Ray(3) == Ray(6) assert Ray.from_number(2.5) * Ray(3) == Ray(7) assert Ray.from_number(2.99999) * Ray(3) == Ray(8)
def deploy(web3: Web3, debt_ceiling: Wad): assert isinstance(web3, Web3) vat = Vat.deploy(web3=web3) pit = Pit.deploy(web3=web3, vat=vat.address) assert pit.file_global_line(debt_ceiling).transact() # Global debt Ceiling assert vat.rely(pit.address).transact() dai = DSToken.deploy(web3=web3, symbol='DAI') dai_adapter = DaiAdapter.deploy(web3=web3, vat=vat.address, dai=dai.address) dai_move = DaiVat.deploy(web3=web3, vat=vat.address) assert vat.rely(dai_adapter.address).transact() assert vat.rely(dai_move.address).transact() mkr = DSToken.deploy(web3=web3, symbol='MKR') # TODO: use a DSProxy mom = DSGuard.deploy(web3) assert mom.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact() assert dai.set_authority(mom.address).transact() assert mkr.set_authority(mom.address).transact() vow = Vow.deploy(web3=web3) drip = Drip.deploy(web3=web3, vat=vat.address) flap = Flapper.deploy(web3=web3, dai=dai_move.address, gem=mkr.address) assert vow.file_vat(vat).transact() assert vow.file_flap(flap).transact() assert vow.file_bump(Wad.from_number(1000)).transact() assert vow.file_sump(Wad.from_number(10)).transact() assert drip.file_vow(vow).transact() assert vat.rely(vow.address).transact() assert vat.rely(drip.address).transact() assert vat.rely(flap.address).transact() cat = Cat.deploy(web3=web3, vat=vat.address) assert cat.file_vow(vow).transact() assert cat.file_pit(pit).transact() flop = Flopper.deploy(web3=web3, dai=dai_move.address, gem=mkr.address) assert vow.file_flop(flop).transact() assert vat.rely(cat.address).transact() assert vat.rely(flop.address).transact() assert vow.rely(cat.address).transact() assert flop.rely(vow.address).transact() config = DssDeployment.Config(mom, vat, vow, drip, pit, cat, flap, flop, dai, dai_adapter, dai_move, mkr) deployment = DssDeployment(web3, config) collateral = Collateral.deploy(web3=web3, name='WETH', vat=vat) deployment.deploy_collateral(collateral, debt_ceiling=Wad.from_number(100000), penalty=Ray.from_number(1), flop_lot=Wad.from_number(10000), ratio=Ray.from_number(1.5), initial_price=Wad.from_number(219)) return deployment
def __init__(self): web3 = Web3(HTTPProvider("http://localhost:8555")) web3.eth.defaultAccount = web3.eth.accounts[0] our_address = Address(web3.eth.defaultAccount) sai = DSToken.deploy(web3, 'DAI') sin = DSToken.deploy(web3, 'SIN') skr = DSToken.deploy(web3, 'PETH') gem = DSToken.deploy(web3, 'WETH') gov = DSToken.deploy(web3, 'MKR') pip = DSValue.deploy(web3) pep = DSValue.deploy(web3) pit = DSVault.deploy(web3) vox = Vox.deploy(web3, per=Ray.from_number(1)) tub = Tub.deploy(web3, sai=sai.address, sin=sin.address, skr=skr.address, gem=gem.address, gov=gov.address, pip=pip.address, pep=pep.address, vox=vox.address, pit=pit.address) tap = Tap.deploy(web3, tub.address) top = Top.deploy(web3, tub.address, tap.address) tub._contract.transact().turn(tap.address.address) otc = MatchingMarket.deploy(web3, 2600000000) etherdelta = EtherDelta.deploy(web3, admin=Address('0x1111100000999998888877777666665555544444'), fee_account=Address('0x8888877777666665555544444111110000099999'), account_levels_addr=Address('0x0000000000000000000000000000000000000000'), fee_make=Wad.from_number(0.01), fee_take=Wad.from_number(0.02), fee_rebate=Wad.from_number(0.03)) # set permissions dad = DSGuard.deploy(web3) dad.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact() tub.set_authority(dad.address).transact() for auth in [sai, sin, skr, gem, gov, pit, tap, top]: auth.set_authority(dad.address).transact() # whitelist pairs otc.add_token_pair_whitelist(sai.address, gem.address).transact() # approve tub.approve(directly()) tap.approve(directly()) # mint some GEMs gem.mint(Wad.from_number(1000000)).transact() self.snapshot_id = web3.manager.request_blocking("evm_snapshot", []) self.web3 = web3 self.our_address = our_address self.sai = sai self.sin = sin self.skr = skr self.gem = gem self.gov = gov self.vox = vox self.tub = tub self.tap = tap self.top = top self.otc = otc self.etherdelta = etherdelta
def test_should_identify_arbitrage_against_oasis_and_bust( self, deployment: Deployment): # given keeper = ArbitrageKeeper(args=args( f"--eth-from {deployment.our_address.address}" f" --tub-address {deployment.tub.address}" f" --tap-address {deployment.tap.address}" f" --oasis-address {deployment.otc.address}" f" --base-token {deployment.sai.address}" f" --min-profit 950.0 --max-engagement 14250.0"), web3=deployment.web3) # and # [we generate some bad debt available for `bust`] DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int( Wad.from_number(500).value).transact() deployment.tub.mold_cap(Wad.from_number(1000000)).transact() deployment.tub.mold_mat(Ray.from_number(2.0)).transact() deployment.tub.mold_axe(Ray.from_number(2.0)).transact() deployment.gem.mint(Wad.from_number(100)).transact() deployment.tub.join(Wad.from_number(100)).transact() deployment.tub.open().transact() deployment.tub.lock(1, Wad.from_number(100)).transact() deployment.tub.draw(1, Wad.from_number(25000)).transact() DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int( Wad.from_number(400).value).transact() deployment.tub.bite(1).transact() DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int( Wad.from_number(500).value).transact() assert deployment.tap.woe() == Wad.from_number(25000) assert deployment.tap.fog() == Wad.from_number(100) # and # [we add a boom/bust spread to make calculations a bit more difficult] deployment.tap.mold_gap(Wad.from_number(0.95)).transact() assert deployment.tap.ask(Wad.from_number(1)) == Wad.from_number(475.0) assert deployment.tap.bid(Wad.from_number(1)) == Wad.from_number(525.0) # and # [we have some SKR to cover rounding errors] deployment.skr.mint(Wad.from_number(0.000000000000000001)).transact() # and # [we should now have 30 SKR available for 14250 SAI on `bust`] # [now lets pretend somebody else placed an order on OASIS offering 15250 SAI for these 30 SKR] # [this will be an arbitrage opportunity which can make the bot earn 1000 SAI] deployment.sai.mint(Wad.from_number(15250)).transact() deployment.otc.approve([deployment.sai, deployment.skr], directly()) deployment.otc.add_token_pair_whitelist( deployment.skr.address, deployment.sai.address).transact() deployment.otc.make(deployment.sai.address, Wad.from_number(15250), deployment.skr.address, Wad.from_number(30)).transact() assert len(deployment.otc.get_orders()) == 1 # when keeper.approve() keeper.process_block() # then # [the order on Oasis has been taken by the keeper] assert len(deployment.otc.get_orders()) == 0 # and # [the amount of bad debt has decreased, so we know the keeper did call bust('14250.0')] # [the inequality below is to cater for rounding errors] assert deployment.tap.woe() < Wad.from_number(10800.0)
def test_beg(self): assert self.flipper.beg() == Ray.from_number(1.05)
def test_default_par(self, deployment: Deployment): # expect assert deployment.vox.par() == Ray.from_number(1)
def test_default_fix(self, deployment: Deployment): # expect assert deployment.top.fix() == Ray.from_number(0)
def test_tag(self, deployment: Deployment): # when DSValue(web3=deployment.web3, address=deployment.tub.pip()).poke_with_int(Wad.from_number(250.45).value).transact() # then assert deployment.tub.tag() == Ray.from_number(250.45)
def test_per(self, deployment: Deployment): assert deployment.tub.per() == Ray.from_number(1.0)
def test_scenario(self, web3, mcd, collateral, clipper, our_address, other_address, deployment_address): dirt_before = mcd.dog.dog_dirt() vice_before = mcd.vat.vice() sin_before = mcd.vow.sin() # Create a vault ilk = collateral.ilk ink = Wad.from_number(1) wrap_eth(mcd, deployment_address, ink) collateral.approve(deployment_address) assert collateral.adapter.join(deployment_address, ink).transact( from_address=deployment_address) frob(mcd, collateral, deployment_address, dink=ink, dart=Wad(0)) dart = max_dart(mcd, collateral, deployment_address) - Wad(1) frob(mcd, collateral, deployment_address, dink=Wad(0), dart=dart) # Mint and withdraw all the Dai mcd.approve_dai(deployment_address) assert mcd.dai_adapter.exit(deployment_address, dart).transact(from_address=deployment_address) # Undercollateralize the vault to_price = Wad(Web3.toInt(collateral.pip.read())) / Wad.from_number(2) set_collateral_price(mcd, collateral, to_price) urn = mcd.vat.urn(collateral.ilk, deployment_address) ilk = mcd.vat.ilk(ilk.name) safe = Ray(urn.art) * mcd.vat.ilk(ilk.name).rate <= Ray(urn.ink) * ilk.spot assert not safe assert clipper.active_count() == 0 # Bark the vault, which moves debt to the vow and kicks the clipper dai_before_bark: Rad = mcd.vat.dai(our_address) urn = mcd.vat.urn(collateral.ilk, deployment_address) assert urn.ink > Wad(0) tab = urn.art * ilk.rate # Wad assert tab == dart assert mcd.dog.bark(ilk, urn).transact() barks = mcd.dog.past_barks(1) assert len(barks) == 1 last_bite = barks[0] assert last_bite.due > Rad(0) assert clipper.active_count() == 1 kick = clipper.kicks() assert kick == 1 assert len(clipper.active_auctions()) == 1 assert clipper.active_auctions()[0].id == 1 assert mcd.active_auctions()['clips']['ETH-B'][0].id == 1 urn = mcd.vat.urn(collateral.ilk, deployment_address) (needs_redo, price, lot, tab) = clipper.status(kick) assert not needs_redo assert price == Ray.from_number(172.5) assert lot == ink assert float(tab) == 105.0 # Check vat, vow, and dog assert urn.ink == Wad(0) assert vice_before < mcd.vat.vice() assert sin_before < mcd.vow.sin() assert dirt_before < mcd.dog.dog_dirt() # Check the clipper current_sale = clipper.sales(kick) assert isinstance(current_sale, Clipper.Sale) assert current_sale.pos == 0 assert float(current_sale.tab) == 105.0 assert current_sale.lot == ink assert current_sale.usr == deployment_address assert current_sale.tic > 0 assert round(current_sale.top, 1) == price coin = Rad(clipper.tip() + (current_sale.tab * clipper.chip())) # Confirm we received our liquidation reward dai_after_bark: Rad = mcd.vat.dai(our_address) assert dai_after_bark == dai_before_bark + coin kick_log = self.last_log(clipper) assert isinstance(kick_log, Clipper.KickLog) assert kick_log.id == kick assert kick_log.top == current_sale.top assert kick_log.tab == current_sale.tab assert kick_log.lot == current_sale.lot assert kick_log.usr == deployment_address assert kick_log.kpr == our_address assert kick_log.coin == coin # Wrap some eth and handle approvals before bidding eth_required = Wad(current_sale.tab / Rad(ilk.spot)) * Wad.from_number(1.1) wrap_eth(mcd, our_address, eth_required) collateral.approve(our_address) assert collateral.adapter.join(our_address, eth_required).transact(from_address=our_address) clipper.approve(mcd.vat.address, approval_function=hope_directly(from_address=our_address)) # Ensure we cannot take collateral below the current price (needs_redo, price, lot, tab) = clipper.status(kick) with pytest.raises(AssertionError): clipper.validate_take(kick, ink, price - Ray.from_number(1)) assert not clipper.take(kick, ink, Ray.from_number(140)).transact(from_address=our_address) # Take some collateral with max above the top price clipper.validate_take(kick, Wad.from_number(0.07), Ray.from_number(180)) assert web3.eth.defaultAccount == our_address.address assert clipper.take(kick, Wad.from_number(0.07), Ray.from_number(180)).transact(from_address=our_address) (needs_redo, price, lot, tab) = clipper.status(kick) assert not needs_redo current_sale = clipper.sales(kick) assert current_sale.lot > Wad(0) assert current_sale.top > price assert Rad(0) < current_sale.tab < kick_log.tab first_take_log = self.last_log(clipper) assert first_take_log.id == 1 assert first_take_log.max == Ray.from_number(180) assert first_take_log.price == price assert first_take_log.lot == current_sale.lot assert first_take_log.usr == deployment_address assert first_take_log.sender == our_address assert round(first_take_log.owe, 18) == round(Rad.from_number(0.07) * Rad(price), 18) # Allow the auction to expire, and then resurrect it # TODO: If abaci contract is ever wrapped, read tau from it time_travel_by(web3, 24) (needs_redo, price, lot, tab) = clipper.status(kick) assert needs_redo assert len(clipper.active_auctions()) == clipper.active_count() assert clipper.redo(kick, our_address).transact() (needs_redo, price, lot, tab) = clipper.status(kick) assert not needs_redo current_sale = clipper.sales(kick) assert current_sale.lot > Wad(0) redo_log = self.last_log(clipper) assert isinstance(redo_log, Clipper.RedoLog) assert redo_log.id == kick assert redo_log.top == current_sale.top assert redo_log.tab == current_sale.tab assert redo_log.lot == current_sale.lot assert redo_log.usr == deployment_address assert redo_log.kpr == our_address coin = Rad(clipper.tip() + (current_sale.tab * clipper.chip())) assert round(float(redo_log.coin), 18) == round(float(coin), 18) # Sleep until price has gone down enough to bid with remaining Dai dai = mcd.vat.dai(our_address) last_price = price print(f"Bid cost={float(price * Ray(current_sale.lot))}, Dai balance={float(dai)}") while price * Ray(current_sale.lot) > Ray(dai): print(f"Bid cost {price * Ray(current_sale.lot)} exceeds Dai balance {dai}") time_travel_by(web3, 2) (needs_redo, price, lot, tab) = clipper.status(kick) assert price < last_price assert not needs_redo last_price = price clipper.validate_take(kick, current_sale.lot, price) assert clipper.take(kick, current_sale.lot, price).transact(from_address=our_address) current_sale = clipper.sales(kick) assert current_sale.lot == Wad(0) assert current_sale.tab == Rad(0) assert clipper.active_count() == 0 assert len(clipper.active_auctions()) == 0 # Ensure we can retrieve our collateral collateral_before = collateral.gem.balance_of(our_address) assert collateral.adapter.exit(our_address, ink).transact(from_address=our_address) collateral_after = collateral.gem.balance_of(our_address) assert collateral_before < collateral_after # Cleanup set_collateral_price(mcd, collateral, Wad.from_number(230)) cleanup_urn(mcd, collateral, our_address)