def test_transfer(self): # when self.token.transfer(self.second_address, Wad(500)) # then self.token.balance_of(self.our_address) == Wad(999500) self.token.balance_of(self.second_address) == Wad(500)
def create_new_sell_order(self): """If our SAI engagement is below the minimum amount, create a new offer up to the maximum amount""" total_amount = self.total_amount(self.our_sell_orders()) if total_amount < self.min_sai_amount: our_balance = self.sai.balance_of( self.our_address) + self.etherdelta.balance_of_token( self.sai.address, self.our_address) have_amount = Wad.min(self.max_sai_amount, our_balance) - total_amount if have_amount > Wad(0): want_amount = self.fix_amount( have_amount * self.apply_sell_margin( self.target_rate(), self.avg_margin)) if self.offchain: self.etherdelta.place_order_offchain( token_get=EtherDelta.ETH_TOKEN, amount_get=want_amount, token_give=self.sai.address, amount_give=have_amount, expires=self.web3.eth.blockNumber + self.order_age) else: self.etherdelta.place_order_onchain( token_get=EtherDelta.ETH_TOKEN, amount_get=want_amount, token_give=self.sai.address, amount_give=have_amount, expires=self.web3.eth.blockNumber + self.order_age)
def __init__(self): super().__init__() self.offchain = self.arguments.offchain self.order_age = self.arguments.order_age self.max_eth_amount = Wad.from_number(self.arguments.max_eth_amount) self.min_eth_amount = Wad.from_number(self.arguments.min_eth_amount) self.max_sai_amount = Wad.from_number(self.arguments.max_sai_amount) self.min_sai_amount = Wad.from_number(self.arguments.min_sai_amount) self.eth_reserve = Wad.from_number(self.arguments.eth_reserve) self.min_margin = self.arguments.min_margin self.avg_margin = self.arguments.avg_margin self.max_margin = self.arguments.max_margin self.etherdelta_address = Address( self.config.get_config()["etherDelta"]["contract"]) self.etherdelta_api_server = self.config.get_config()["etherDelta"]["apiServer"][1] \ if "apiServer" in self.config.get_config()["etherDelta"] \ else None self.etherdelta = EtherDelta(web3=self.web3, address=self.etherdelta_address, api_server=self.etherdelta_api_server) if self.offchain and not self.etherdelta.supports_offchain_orders(): raise Exception( "Off-chain EtherDelta orders not supported on this chain")
def close_position(self): cup_id = 8 # (1) Exchange our ETH to SAI as we want to repay our SAI debt our_entire_gem = self.gem.balance_of(self.our_address) conversion = self.gem_to_sai_conversion() sequence = Sequence([conversion]) if our_entire_gem > Wad.from_number(0.0001): sequence.set_amounts(our_entire_gem) receipt = sequence.steps[0].execute() if receipt: logging.info(receipt.transfers) self.print_balances() our_debt = self.tub.tab(cup_id) print(our_debt) our_sai_balanace = self.sai.balance_of(self.our_address) if our_sai_balanace < our_debt: logging.info("NOT ENOUGH SAI TO REPAY OUR DEBT!") exit(-1) # (2) Repay the debt and get back our SKR # some surplus of SAI will be left, this is the profit we made # self.tub.wipe(cup_id, Wad.from_number(1)) logging.info(self.tub.shut(cup_id)) self.print_balances() # (3) Exchange SKR back to ETH if self.skr.balance_of(self.our_address) > Wad(0): logging.info(self.tub.exit(self.skr.balance_of(self.our_address))) self.print_balances()
def test_should_identify_multi_step_opportunities(self, token1, token2, token3, token4): # given conversion1 = Conversion(token1, token2, Ray.from_number(1.02), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token3, Ray.from_number(1.03), Wad.from_number(10000), 'met2') conversion3 = Conversion(token3, token4, Ray.from_number(1.05), Wad.from_number(10000), 'met3') conversion4 = Conversion(token4, token1, Ray.from_number(1.07), Wad.from_number(10000), 'met4') conversions = [conversion1, conversion2, conversion3, conversion4] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 1 assert len(opportunities[0].steps) == 4 assert opportunities[0].steps[0].method == "met1" assert opportunities[0].steps[1].method == "met2" assert opportunities[0].steps[2].method == "met3" assert opportunities[0].steps[3].method == "met4"
def test_tag(self, sai: SaiDeployment): # when DSValue(web3=sai.web3, address=sai.tub.pip()).poke_with_int( Wad.from_number(250.45).value) # then assert sai.tub.tag() == Wad.from_number(250.45)
def __init__(self): super().__init__() self.base_token = ERC20Token(web3=self.web3, address=ERC20Token.token_address_by_name( self.arguments.base_token)) self.min_profit = Wad.from_number(self.arguments.min_profit) self.max_engagement = Wad.from_number(self.arguments.max_engagement) if len(self.arguments.excluded_makers) > 0: self.excluded_makers = set( map(lambda maker: Address(maker), self.arguments.excluded_makers.split(','))) else: self.excluded_makers = set() self.max_errors = self.arguments.max_errors self.errors = 0 if self.arguments.tx_manager: self.tx_manager_address = Address(self.arguments.tx_manager) self.tx_manager = TxManager(web3=self.web3, address=self.tx_manager_address) if self.tx_manager.owner() != self.our_address: logging.info( f"The TxManager has to be owned by the address the keeper is operating from." ) exit(-1) else: self.tx_manager_address = None self.tx_manager = None
def get_offer(self, offer_id: int) -> Optional[OfferInfo]: """Get the offer details. Args: offer_id: The id of the offer to get the details of. Returns: An instance of `OfferInfo` if the offer is still active, or `None` if the offer has been already completely taken. """ # if an offer is None, it won't become not-None again for the same OTC instance if offer_id in self._none_offers: return None array = self._contract.call().offers(offer_id) if array[5] is not True: self._none_offers.add(offer_id) return None else: return OfferInfo(offer_id=offer_id, sell_how_much=Wad(array[0]), sell_which_token=Address(array[1]), buy_how_much=Wad(array[2]), buy_which_token=Address(array[3]), owner=Address(array[4]), timestamp=array[6])
def __init__(self, args): self.id = bytes_to_int(args['id']) self.maker = Address(args['maker']) self.have_token = Address(args['haveToken']) self.have_amount = Wad(args['haveAmount']) self.want_token = Address(args['wantToken']) self.want_amount = Wad(args['wantAmount']) self.timestamp = args['timestamp']
def __init__(self, args): self.token_get = Address(args['tokenGet']) self.amount_get = Wad(args['amountGet']) self.token_give = Address(args['tokenGive']) self.amount_give = Wad(args['amountGive']) self.expires = args['expires'] self.nonce = args['nonce'] self.user = Address(args['user'])
def test_cups(self, sai: SaiDeployment): # when sai.tub.open() # then assert sai.tub.cups(1).art == Wad(0) assert sai.tub.cups(1).ink == Wad(0) assert sai.tub.cups(1).lad == sai.our_address
def test_withdraw(self): # given self.dsethtoken.deposit(Wad(100000)) # when self.dsethtoken.withdraw(Wad(40000)) # then assert self.dsethtoken.balance_of(self.our_address) == Wad(60000)
def test_burn(self): # given self.dstoken.mint(Wad(100000)) # when self.dstoken.burn(Wad(40000)) # then assert self.dstoken.balance_of(self.our_address) == Wad(60000)
def __init__(self): super().__init__() self.max_weth_amount = Wad.from_number(self.arguments.max_weth_amount) self.min_weth_amount = Wad.from_number(self.arguments.min_weth_amount) self.max_sai_amount = Wad.from_number(self.arguments.max_sai_amount) self.min_sai_amount = Wad.from_number(self.arguments.min_sai_amount) self.min_margin = self.arguments.min_margin self.avg_margin = self.arguments.avg_margin self.max_margin = self.arguments.max_margin
def test_cork_and_hat(self, sai: SaiDeployment): # given assert sai.tub.hat() == Wad(0) # when sai.tub.cork(Wad.from_number(150000)) # then assert sai.tub.hat() == Wad.from_number(150000)
def _net_value(self, transfer: Transfer, our_address: Address): if transfer.from_address == our_address and transfer.to_address == our_address: return Wad(0) elif transfer.from_address == our_address: return Wad(0) - transfer.value elif transfer.to_address == our_address: return transfer.value else: return Wad(0)
def withdraw_everything(self): eth_balance = self.etherdelta.balance_of(self.our_address) if eth_balance > Wad(0): self.etherdelta.withdraw(eth_balance) sai_balance = self.etherdelta.balance_of_token(self.sai.address, self.our_address) if sai_balance > Wad(0): self.etherdelta.withdraw_token(self.sai.address, sai_balance)
def test_jump_and_gap(self, sai: SaiDeployment): # given assert sai.tap.gap() == Wad.from_number(1) # when sai.tap.jump(Wad.from_number(1.05)) # then assert sai.tap.gap() == Wad.from_number(1.05)
def test_s2s_and_bid_and_ask(self, sai: SaiDeployment): # when DSValue(web3=sai.web3, address=sai.tub.pip()).poke_with_int( Wad.from_number(500).value) sai.tap.jump(Wad.from_number(1.05)) # then assert sai.tap.bid() == Wad.from_number(475) assert sai.tap.s2s() == Wad.from_number(500) assert sai.tap.ask() == Wad.from_number(525)
def test_should_format_two_different_tokens(token1, token2, some_address): # given transfer1 = Transfer(token1, some_address, some_address, Wad.from_number(105)) transfer2 = Transfer(token2, some_address, some_address, Wad.from_number(17)) # expect assert TransferFormatter().format([transfer1, transfer2]) \ == "105.000000000000000000 TK1 and 17.000000000000000000 TK2"
def test_should_calculate_tx_costs(self, token1): # expect the tx_costs to be non negative and to increase with the number of steps steps = [] prev_tx_costs = Wad.from_number(0) for i in range(10): steps.append(Conversion(token1, token1, Ray(0), Wad(0), 'met')) opportunity = Sequence(steps) tx_costs = opportunity.tx_costs() assert (tx_costs > prev_tx_costs) prev_tx_costs = tx_costs
def test_should_format_net_balances(token1, our_address, some_address): # given transfer1 = Transfer(token1, our_address, some_address, Wad.from_number(15)) transfer2 = Transfer(token1, some_address, our_address, Wad.from_number(17)) # expect assert TransferFormatter().format_net([transfer1, transfer2], our_address) \ == "2.000000000000000000 TK1"
def test_nicely_convert_to_string_with_amounts(token1, token2): # given conversion = Conversion(token1, token2, Ray.from_number(1.01), Wad.from_number(1000), 'met()') conversion.source_amount = Wad.from_number(50) conversion.target_amount = Wad.from_number(50.5) # expect assert str(conversion) == "[50.000000000000000000 TK1 -> 50.500000000000000000 TK2 @1.010000000000000000000000000" \ " by met() (max=1000.000000000000000000 TK1)]"
def deposit_for_sell_orders(self): order_total = self.total_amount(self.our_sell_orders()) currently_deposited = self.etherdelta.balance_of_token( self.sai.address, self.our_address) if order_total > currently_deposited: additional_deposit = Wad.min(order_total - currently_deposited, self.sai.balance_of(self.our_address)) if additional_deposit > Wad(0): self.etherdelta.deposit_token(self.sai.address, additional_deposit)
def deposit_for_buy_orders(self): order_total = self.total_amount(self.our_buy_orders()) currently_deposited = self.etherdelta.balance_of(self.our_address) if order_total > currently_deposited: depositable_eth = Wad.max( self.eth_balance(self.our_address) - self.eth_reserve, Wad(0)) additional_deposit = Wad.min(order_total - currently_deposited, depositable_eth) if additional_deposit > Wad(0): self.etherdelta.deposit(additional_deposit)
def prepare_balances(self): recipient = Address('0x002ca7F9b416B2304cDd20c26882d1EF5c53F611') if self.sai.balance_of(self.our_address) > Wad(0): self.sai.transfer(recipient, self.sai.balance_of(self.our_address)) if self.skr.balance_of(self.our_address) > Wad(0): self.skr.transfer(recipient, self.skr.balance_of(self.our_address)) if self.gem.balance_of(self.our_address) > Wad.from_number(0.5): self.gem.transfer( recipient, self.gem.balance_of(self.our_address) - Wad.from_number(0.5))
def test_should_format_net_balances_excluding_transfers_between_us( token1, our_address, some_address): # given transfer1 = Transfer(token1, some_address, our_address, Wad.from_number(4)) transfer2 = Transfer(token1, our_address, some_address, Wad.from_number(1.5)) transfer3 = Transfer(token1, our_address, our_address, Wad.from_number(50)) # expect assert TransferFormatter().format_net([transfer1, transfer2, transfer3], our_address) \ == "2.500000000000000000 TK1"
def bustable_amount_in_sai(self, tap: Tap): #TODO we always try to bust 10 SAI less than what the Tub reports #in order to discount the growth of `joy()` that might've have happened since the last drip #of course this is not the right solution and it won't even work properly if the last #drip happened enough time ago bustable_woe = tap.woe() - tap.joy() - Wad.from_number(10) # we deduct 0.000001 in order to avoid rounding errors bustable_fog = tap.fog() * tap.ask() - Wad.from_number(0.000001) return Wad.max(bustable_woe, bustable_fog, Wad.from_number(0))
def test_should_calculate_total_rate(self, token1, token2): # given step1 = Conversion(token1, token2, Ray.from_number(1.01), Wad.from_number(1000), 'met1') step2 = Conversion(token2, token1, Ray.from_number(1.02), Wad.from_number(1000), 'met2') # when sequence = Sequence([step1, step2]) # then assert sequence.total_rate() == Ray.from_number(1.0302)
def required_top_up(self, cup): pro = cup.ink * self.tub.tag() tab = self.tub.tab(cup.cup_id) if tab > Wad(0): current_ratio = Ray(pro / tab) if current_ratio < self.minimum_ratio: return tab * (Wad(self.target_ratio - current_ratio) / self.tub.tag()) else: return None else: return None