def _convert_balance_to_wad(self, balance: dict, decimals: int) -> dict: wei_balance = float(balance['wei']) pending_balance = float(balance['pendingWei']) ## DyDx can have negative balances from native margin trading is_negative = False if wei_balance < 0: is_negative = True converted_balance = from_wei(abs(int(wei_balance)), 'ether') converted_pending_balance = from_wei(abs(int(pending_balance)), 'ether') if decimals == 6: converted_balance = from_wei(abs(int(wei_balance)), 'mwei') converted_pending_balance = from_wei(abs(int(pending_balance)), 'mwei') # reconvert Wad to negative value if balance is negative if is_negative == True: converted_balance = converted_balance * -1 # Handle the edge case where orders are filled but balance change is still pending if converted_balance > 0: balance['wad'] = Wad.from_number( converted_balance) - Wad.from_number(converted_pending_balance) else: balance['wad'] = Wad.from_number( converted_balance) + Wad.from_number(converted_pending_balance) return balance
def from_trade(pair, trade): return BinanceUsTrade(trade_id=str(trade['id']), timestamp=trade['time'], pair=pair, is_sell=not trade['isBuyerMaker'], price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['qty']))
def test_local_accounts_register_key(): # given # [that address is not recognized by ganache, this way we can be sure it's the local account being used for signing] web3 = Web3(HTTPProvider("http://localhost:8555")) web3.eth.defaultAccount = Address( '0x13314e21cd6d343ceb857073f3f6d9368919d1ef').address # and keyfile_path = pkg_resources.resource_filename( __name__, "accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json") passfile_path = pkg_resources.resource_filename(__name__, "accounts/pass") register_key(web3, f"key_file={keyfile_path},pass_file={passfile_path}") # and # [as ganache does not know this address, we need to send some ETH to it first] eth_transfer(web3, Address(web3.eth.defaultAccount), Wad.from_number(100)) \ .transact(from_address=Address(web3.eth.accounts[0])) # when # [we deploy some test contract and mint some tokens] token = DSToken.deploy(web3, 'XYZ', 'XYZ') token.mint(Wad.from_number(150000)).transact() # then # [these operations were successful] assert token.balance_of(Address( web3.eth.defaultAccount)) == Wad.from_number(150000)
def test_convert(self): # two chain_amount = Wad(20000) assert self.token.normalize_amount(chain_amount) == Wad.from_number(2) # three normalized_amount = Wad.from_number(3) assert self.token.unnormalize_amount(normalized_amount) == Wad(30000)
def from_all_list(pair, trade): return Trade(trade_id=trade['trade_id'], timestamp=int( dateutil.parser.parse(trade['time']).timestamp()), pair=pair, is_sell=True if trade['side'] == 'sell' else False, price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['size']))
def create(item): return BinanceUsOrder( order_id=str(item['orderId']), pair=item['symbol'], is_sell=True if item['side'] == 'SELL' else False, price=Wad.from_number(item['price']), amount=Wad.from_number(item['origQty']), timestamp=item['time'])
def test_get_orders(self, mocker): instrument_id = "WETH-DAI" mocker.patch("dydx.client.Client.get_my_orders", side_effect=DydxMockServer.handle_get_orders) response = self.dydx.get_orders(instrument_id) assert (len(response) > 0) for order in response: assert(isinstance(order.is_sell, bool)) assert(Wad(order.price) > Wad(0)) TestDydx.check_orders(response)
def test_get_orders(self, mocker): pair = "ETH-KRW" mocker.patch("requests.request", side_effect=self.binaceUsMockServer.handle_request) response = self.binance_us.get_orders(pair) assert (len(response) > 0) for order in response: assert (isinstance(order.is_sell, bool)) assert (Wad(order.price) > Wad(0)) TestBinanceUs.check_orders(response)
def test_order_placement_and_cancellation(self, mocker): pair = "ETH-KRW" side = "ask" mocker.patch("requests.request", side_effect=self.binaceUsMockServer.handle_request) order_id = self.binance_us.place_order(pair, True, Wad.from_number(241700), Wad.from_number(10)) assert (isinstance(order_id, str)) assert (order_id is not None) cancel_result = self.binance_us.cancel_order(order_id, pair) assert (cancel_result == True)
def get_balances(self, pair: Pair): assert(isinstance(pair, Pair)) token_buy = ERC20Token(web3=self.zrx_exchange.web3, address=Address(pair.buy_token_address)) token_sell = ERC20Token(web3=self.zrx_exchange.web3, address=Address(pair.sell_token_address)) our_address = Address(self.zrx_exchange.web3.eth.defaultAccount) return token_sell.balance_of(our_address) * Wad.from_number(10 ** (18 - pair.sell_token_decimals)), \ token_buy.balance_of(our_address) * Wad.from_number(10 ** (18 - pair.buy_token_decimals))
def test_direct_approval_should_not_approve_if_already_approved(): # given global web3, our_address, second_address, token token.approve(second_address, Wad(2**248 + 17)).transact() # when directly()(token, second_address, "some-name") # then assert token.allowance_of(our_address, second_address) == Wad(2**248 + 17)
def get_exchange_rate(self) -> Wad: token_a_reserve = self.get_exchange_balance(self.token_a, self.pair_address) token_b_reserve = self.get_exchange_balance(self.token_b, self.pair_address) if token_a_reserve == Wad.from_number( 0) or token_b_reserve == Wad.from_number(0): return Wad.from_number(0) else: return token_b_reserve / token_a_reserve
def _blockchain_to_wad(pair: Pair, amount: Wad, token_address: Address): assert(isinstance(pair, Pair)) assert(isinstance(amount, Wad)) assert(isinstance(token_address, Address)) assert(token_address in [pair.buy_token_address, pair.sell_token_address]) if token_address == pair.buy_token_address: return amount * Wad.from_number(10 ** (18 - pair.buy_token_decimals)) elif token_address == pair.sell_token_address: return amount * Wad.from_number(10 ** (18 - pair.sell_token_decimals))
def add_liquidity(self, amount: Wad) -> Transact: assert (isinstance(amount, Wad)) min_liquidity = Wad.from_number(0.5) * amount max_token = amount * self.get_exchange_rate() * Wad.from_number( 1.00000001) return Transact( self, self.web3, self.abi, self.exchange, self._contract, 'addLiquidity', [min_liquidity.value, max_token.value, self._deadline()], {'value': amount.value})
def test_order(self): price = Wad.from_number(4.8765) amount = Wad.from_number(0.222) order = Order( order_id="153153", timestamp=int(time.time()), pair="ETH-KRW", is_sell=False, price=price, amount=amount ) assert (order.price == order.sell_to_buy_price) assert (order.price == order.buy_to_sell_price)
def test_via_tx_manager_approval_should_not_approve_if_already_approved(): # given global web3, our_address, second_address, token tx = TxManager.deploy(web3) tx.execute([], [token.approve(second_address, Wad(2**248 + 19)).invocation() ]).transact() # when via_tx_manager(tx)(token, second_address, "some-name") # then assert token.allowance_of(tx.address, second_address) == Wad(2**248 + 19)
def from_message(trade, pair: str, market_info: dict) -> Trade: decimal_exponent = 18 - int(market_info['quoteCurrency']['decimals']) price = Wad.from_number(float(trade['price']) * 10**decimal_exponent) return Trade(trade_id=trade['uuid'], timestamp=int( dateutil.parser.parse( trade['createdAt']).timestamp()), pair=trade["market"], is_sell=True if trade['side'] == 'SELL' else False, price=price, amount=Wad.from_number( from_wei(abs(int(float(trade['amount']))), 'ether')))
def from_message(item: list, pair: str, market_info: dict) -> Order: decimal_exponent = 18 - int(market_info['quoteCurrency']['decimals']) price = Wad.from_number(float(item['price']) * 10**decimal_exponent) return Order(order_id=item['id'], timestamp=int( dateutil.parser.parse(item['createdAt']).timestamp()), pair=pair, is_sell=True if item['side'] == 'SELL' else False, price=price, amount=Wad.from_number( from_wei(abs(int(float(item['baseAmount']))), 'ether')))
def get_amounts_out(self, amount_in: Wad, tokens: List[Token]) -> List[Wad]: """ Calculate maximum output amount of a given input. Desired amount_in must be less than available liquidity or call will fail. Args: amounts_in: Desired amount of tokens out. tokens: List of tokens used to form a path for swap and normalize amounts for token decimals Returns: A list of uint256 reserve amounts required. """ assert (isinstance(amount_in, Wad)) assert (isinstance(tokens, List)) token_addresses = list(map(lambda token: token.address.address, tokens)) amounts = self._router_contract.functions.getAmountsOut( amount_in.value, token_addresses).call() wad_amounts = list( map(lambda amount: Wad.from_number(Web3.fromWei(amount, 'ether')), amounts)) for index, token in enumerate(tokens): wad_amounts[index] = token.normalize_amount(wad_amounts[index]) return wad_amounts
def get_our_exchange_balance(self, token: Token, pair_address: Address) -> Wad: assert (isinstance(token, Token)) assert (isinstance(pair_address, Address)) if self.is_new_pool: return Wad.from_number(0) current_liquidity = self.get_current_liquidity() if current_liquidity == Wad.from_number(0): return Wad.from_number(0) total_liquidity = self.get_total_liquidity() exchange_balance = self.get_exchange_balance(token, pair_address) return current_liquidity * exchange_balance / total_liquidity
def main(self): self.startup() pending_txes = get_pending_transactions(web3) pprint( list( map(lambda t: f"{t.name()} with gas {t.current_gas}", pending_txes))) if len(pending_txes) > 0: while len(pending_txes) > 0: pending_txes[0].cancel(gas_price=increasing_gas) # After the synchronous cancel, wait to see if subsequent transactions get mined time.sleep(15) pending_txes = get_pending_transactions(web3) else: logging.info( f"No pending transactions were found; submitting {stuck_txes_to_submit}" ) for i in range(1, stuck_txes_to_submit + 1): self._run_future( weth.deposit(Wad(i)).transact_async( gas_price=FixedGasPrice(int(0.4 * i * GWEI)))) time.sleep(2) self.shutdown()
def get_orders(self, pair: Pair, zrx_orders: list) -> List[Order]: assert (isinstance(pair, Pair)) assert (isinstance(zrx_orders, list)) result = [] for zrx_order in zrx_orders: is_sell = zrx_order.buy_token == pair.buy_token_address and zrx_order.pay_token == pair.sell_token_address is_buy = zrx_order.buy_token == pair.sell_token_address and zrx_order.pay_token == pair.buy_token_address if is_sell or is_buy: amount = zrx_order.remaining_sell_amount if is_sell else zrx_order.remaining_buy_amount price = zrx_order.buy_to_sell_price if is_sell else zrx_order.sell_to_buy_price result.append( Order(order_id=zrx_order.order_id, is_sell=is_sell, price=price / Wad.from_number(10**(pair.buy_token_decimals - pair.sell_token_decimals)), amount=self._blockchain_to_wad( pair, amount, pair.sell_token_address), zrx_order=zrx_order)) return result
def check_sync_transaction_still_works(self): balance_before = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) amount = Wad.from_number(0.01) assert self.collateral.adapter.join(self.keeper_address, amount).transact() balance_after = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) assert balance_before + amount == balance_after
def test_direct_approval(): # given global web3, our_address, second_address, token # when directly()(token, second_address, "some-name") # then assert token.allowance_of(our_address, second_address) == Wad(2**256 - 1)
def test_via_tx_manager_approval(): # given global web3, our_address, second_address, token tx = TxManager.deploy(web3) # when via_tx_manager(tx)(token, second_address, "some-name") # then assert token.allowance_of(tx.address, second_address) == Wad(2**256 - 1)
def check_async_transaction_still_works(self): balance_before = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) amount = Wad.from_number(0.01) AuctionKeeper._run_future( self.collateral.adapter.exit(self.keeper_address, amount).transact_async()) wait_for_other_threads() balance_after = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) assert balance_before - amount == balance_after
def test_replace_async_transaction(self): balance_before = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) self.start_ignoring_transactions() amount1 = Wad.from_number(0.11) tx1 = self.collateral.adapter.join(self.keeper_address, amount1) AuctionKeeper._run_future(tx1.transact_async(gas_price=self.gas)) self.end_ignoring_transactions() amount2 = Wad.from_number(0.14) tx2 = self.collateral.adapter.join(self.keeper_address, amount2) AuctionKeeper._run_future(tx2.transact_async(replace=tx1)) # Wait for async tx threads to exit normally (should consider doing this after every async test) wait_for_other_threads() balance_after = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) assert balance_before + amount2 == balance_after self.check_sync_transaction_still_works() self.check_async_transaction_still_works()
def test_direct_approval_should_obey_from_address(): # given global web3, our_address, second_address, third_address, token # and # [there is already approval from the `defaultAccount`] # [so that we make sure we check for the existing approval properly] directly()(token, second_address, "some-name") # when directly(from_address=third_address)(token, second_address, "some-name") # then assert token.allowance_of(third_address, second_address) == Wad(2**256 - 1)
def test_min_amount(self): assert self.token.min_amount == Wad.from_number(0.0001) assert float(self.token.min_amount) == 0.0001 assert self.token.unnormalize_amount(self.token.min_amount) == Wad(1) assert Wad.from_number(0.0004) > self.token.min_amount assert Wad.from_number(0.00005) < self.token.min_amount assert self.token.unnormalize_amount( Wad.from_number(0.0006)) > self.token.unnormalize_amount( self.token.min_amount) assert self.token.unnormalize_amount( Wad.from_number(0.00007)) < self.token.unnormalize_amount( self.token.min_amount) assert self.token.unnormalize_amount( Wad.from_number(0.00008)) == Wad(0)
def test_ignore_sync_transaction(self): balance_before = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) self.start_ignoring_sync_transactions() assert self.collateral.adapter.join(self.keeper_address, Wad.from_number(0.2)).transact() self.end_ignoring_sync_transactions() balance_after = self.geb.safe_engine.token_collateral( self.collateral_type, self.keeper_address) assert balance_before == balance_after self.check_sync_transaction_still_works() self.check_async_transaction_still_works()