async def _get_logs(self, event_filter_params: Dict[str, any], max_tries: Optional[int] = 30) -> List[Dict[str, any]]: async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance( ) count: int = 0 logs = [] while True: try: count += 1 if count > max_tries: self.logger().debug( f"Error fetching logs from block with filters: '{event_filter_params}'." ) break logs = await async_scheduler.call_async( functools.partial(self._w3.eth.getLogs, event_filter_params)) break except asyncio.CancelledError: raise except Exception: self.logger().debug( f"Block not found with filters: '{event_filter_params}'. Retrying..." ) await asyncio.sleep(0.5) return logs
async def check_and_fix_approval_amounts(self, spender: str) -> List[str]: """ Maintain the approve amounts for a token. This function will be used to ensure trade execution using exchange protocols such as 0x, but should be defined in child classes Allowance amounts to manage: 1. Allow external contract to pull tokens from local wallet """ min_approve_amount: int = int(Decimal("1e35")) target_approve_amount: int = int(Decimal("1e36")) async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance() # Get currently approved amounts get_approved_amounts_tasks: List[Coroutine] = [ async_scheduler.call_async(erc20_token.contract.functions.allowance(self.address, spender).call) for erc20_token in self._erc20_token_list ] approved_amounts: List[int] = await safe_gather(*get_approved_amounts_tasks) # Check and fix the approved amounts tx_hashes: List[str] = [] for approved_amount, erc20_token in zip(approved_amounts, self._erc20_token_list): token_name: str = await erc20_token.get_name() token_contract: Contract = erc20_token.contract if approved_amount >= min_approve_amount: self.logger().info(f"Approval already exists for {token_name} from wallet address {self.address}.") continue self.logger().info(f"Approving spender for drawing {token_name} from wallet address {self.address}.") tx_hash: str = self.execute_transaction(token_contract.functions.approve( spender, target_approve_amount )) tx_hashes.append(tx_hash) return tx_hashes
async def handler_loop(self, bot: Bot, update: Update) -> None: async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance( ) try: input_text = update.message.text.strip() output = f"\n[Telegram Input] {input_text}" self._hb.app.log(output) # if the command does starts with any disabled commands if any([ input_text.lower().startswith(dc) for dc in DISABLED_COMMANDS ]): self.add_msg_to_queue( f"Command {input_text} is disabled from telegram") else: # Set display options to max, so that telegram does not display truncated data pd.set_option('display.max_rows', 500) pd.set_option('display.max_columns', 500) pd.set_option('display.width', 1000) await async_scheduler.call_async(self._hb._handle_command, input_text) # Reset to normal, so that pandas's default autodetect width still works pd.set_option('display.max_rows', 0) pd.set_option('display.max_columns', 0) pd.set_option('display.width', 0) except Exception as e: self.add_msg_to_queue(str(e))
async def _get_contract_info(self): if self._name is not None and self._symbol is not None and self._decimals is not None: return tasks: List[Coroutine] = [ AsyncCallScheduler.shared_instance().call_async(func, *args) for func, args in [(self.get_name_from_contract, [self._contract]), (self.get_symbol_from_contract, [self._contract]), (self._contract.functions.decimals().call, [])] ] try: name, symbol, decimals = await safe_gather(*tasks) self._name = name self._symbol = symbol self._decimals = decimals except asyncio.CancelledError: raise except Exception: self.logger().network( f"Error fetching token info for {self._contract.address}.", exc_info=True, app_warning_msg= f"Error fetching token info for {self._contract.address}. " f"Check wallet network connection")
def login(cls, secrets_manager: BaseSecretsManager) -> bool: if not validate_password(secrets_manager): return False cls.secrets_manager = secrets_manager coro = AsyncCallScheduler.shared_instance().call_async( cls.decrypt_all, timeout_seconds=30) safe_ensure_future(coro) return True
async def check_incoming_eth(self, new_blocks: List[AttributeDict]): async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance( ) watch_addresses: Set[str] = self._watch_addresses filtered_blocks: List[AttributeDict] = [ block for block in new_blocks if block is not None ] block_to_timestamp: Dict[str, float] = dict( (block.hash, float(block.timestamp)) for block in filtered_blocks) transactions: List[AttributeDict] = list( cytoolz.concat(b.transactions for b in filtered_blocks)) incoming_eth_transactions: List[AttributeDict] = [ t for t in transactions if ((t.get("to") in watch_addresses) and (t.get("value", 0) > 0)) ] get_receipt_tasks: List[Coroutine] = [ async_scheduler.call_async(self._w3.eth.getTransactionReceipt, t.hash) for t in incoming_eth_transactions ] try: transaction_receipts: List[AttributeDict] = await asyncio.gather( *get_receipt_tasks) except asyncio.CancelledError: raise except Exception: self.logger().network( "Error fetching Ethereum block receipts.", app_warning_msg="Error fetching Ethereum block receipts. " "Please check Ethereum node connection.", exc_info=True) return for incoming_transaction, receipt in zip(incoming_eth_transactions, transaction_receipts): # Filter out failed transactions. if receipt.status != 1: continue # Emit event. raw_eth_value: int = incoming_transaction.get("value") eth_value: float = raw_eth_value * 1e-18 from_address: str = incoming_transaction.get("from") to_address: str = incoming_transaction.get("to") timestamp: float = block_to_timestamp[incoming_transaction.get( "blockHash")] self.trigger_event( IncomingEthWatcherEvent.ReceivedEther, WalletReceivedAssetEvent(timestamp, incoming_transaction.hash.hex(), from_address, to_address, "ETH", eth_value, raw_eth_value))
def __init__(self, token: str, chat_id: str, hb: "HummingbotApplication") -> None: super().__init__() self._token = token or global_config_map.get("telegram_token").value self._chat_id = chat_id or global_config_map.get("telegram_chat_id").value self._updater = Updater(token=token, workers=0) self._hb = hb self._ev_loop = asyncio.get_event_loop() self._async_call_scheduler = AsyncCallScheduler.shared_instance() # Register command handler and start telegram message polling handles = [MessageHandler(Filters.text, self.handler)] for handle in handles: self._updater.dispatcher.add_handler(handle)
async def check_transaction_receipts(self): """ Look for failed transactions, and emit transaction fail event if any are found. """ async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance( ) tasks = [ async_scheduler.call_async(self._w3.eth.getTransactionReceipt, tx_hash) for tx_hash in self._pending_tx_dict.keys() ] transaction_receipts: List[AttributeDict] = [ tr for tr in await safe_gather(*tasks) if (tr is not None and tr.get("blockHash") is not None) ] block_hash_set: Set[HexBytes] = set(tr.blockHash for tr in transaction_receipts) fetch_block_tasks = [ async_scheduler.call_async(self._w3.eth.getBlock, block_hash) for block_hash in block_hash_set ] blocks: Dict[HexBytes, AttributeDict] = dict( (block.hash, block) for block in await safe_gather(*fetch_block_tasks) if block is not None) for receipt in transaction_receipts: # Emit gas used event. tx_hash: str = receipt.transactionHash.hex() gas_price_wei: int = self._pending_tx_dict[tx_hash] gas_used: int = receipt.gasUsed gas_eth_amount_raw: int = gas_price_wei * gas_used if receipt.blockHash in blocks: block: AttributeDict = blocks[receipt.blockHash] if receipt.status == 0: self.logger().warning( f"The transaction {tx_hash} has failed.") self.trigger_event(WalletEvent.TransactionFailure, tx_hash) self.trigger_event( WalletEvent.GasUsed, EthereumGasUsedEvent(float(block.timestamp), tx_hash, float(gas_price_wei * 1e-9), gas_price_wei, gas_used, float(gas_eth_amount_raw * 1e-18), gas_eth_amount_raw)) # Stop tracking the transaction. self._stop_tx_tracking(tx_hash)
async def _check_transaction_receipt(self, tx_hash: str, timestamp: int): """ Look for transaction receipt, only raise not found error if they are missing for longer than two minutes. """ async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance() try: return await async_scheduler.call_async(self._w3.eth.getTransactionReceipt, tx_hash) except TransactionNotFound as e: now: float = time.time() if now - timestamp > 120: stop_tx_hash = e.args[0].split(" ")[3] self._stop_tx_tracking(stop_tx_hash) self.logger().info(f"Stopped tracking transaction with hash: {stop_tx_hash}.") return None
async def outgoing_eth_transactions_loop(self): async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance() while True: signed_transaction: AttributeDict = await self._outgoing_transactions_queue.get() tx_hash: str = signed_transaction.hash.hex() try: await async_scheduler.call_async(self._w3.eth.sendRawTransaction, signed_transaction.rawTransaction) except asyncio.CancelledError: raise except Exception: self.logger().network( f"Error sending transaction {tx_hash}.", exc_info=True, app_warning_msg=f"Error sending transaction {tx_hash}. Check wallet network connection") self.trigger_event(WalletEvent.TransactionFailure, tx_hash) self._local_nonce -= 1
def __init__( self, token: str, chat_id: str, hb: "hummingbot.client.hummingbot_application.HummingbotApplication" ) -> None: super().__init__() self._token = token self._chat_id = chat_id self._updater = Updater(token=token, workers=0) self._hb = hb self._ev_loop = asyncio.get_event_loop() self._async_call_scheduler = AsyncCallScheduler.shared_instance() self._msg_queue: asyncio.Queue = asyncio.Queue() self._send_msg_task: Optional[asyncio.Task] = None # Register command handler and start telegram message polling handles = [MessageHandler(Filters.text, self.handler)] for handle in handles: self._updater.dispatcher.add_handler(handle)
async def list_encrypted( self, # type: HummingbotApplication ): encrypted_files = list_encrypted_file_paths() if len(encrypted_files) == 0: self._notify("There is no encrypted file in your conf folder.") return self.placeholder_mode = True self.app.toggle_hide_input() in_memory_config_map.get( "password").value = await self._one_password_config() password = in_memory_config_map.get("password").value coro = AsyncCallScheduler.shared_instance().call_async( partial(self._list_all_encrypted, encrypted_files, password), timeout_seconds=30) safe_ensure_future(coro) self.app.change_prompt(prompt=">>> ") self.app.toggle_hide_input() self.placeholder_mode = False
def login(cls, password): encrypted_files = list_encrypted_file_paths() wallets = list_wallets() if encrypted_files: try: decrypt_file(encrypted_files[0], password) except ValueError as err: if str(err) == "MAC mismatch": return False raise err elif wallets: try: unlock_wallet(wallets[0], password) except ValueError as err: if str(err) == "MAC mismatch": return False raise err Security.password = password coro = AsyncCallScheduler.shared_instance().call_async(cls.decrypt_all, timeout_seconds=30) safe_ensure_future(coro) return True
async def _update_gas_price(self): async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance( ) new_gas_price: int = await async_scheduler.call_async( getattr, self._w3.eth, "gasPrice") self._gas_price = new_gas_price
async def start_network(self): if self._outgoing_transactions_task is not None: await self.stop_network() async_scheduler: AsyncCallScheduler = AsyncCallScheduler.shared_instance( ) if len(self._erc20_tokens) < len(self._erc20_token_list): # Fetch token data. fetch_symbols_tasks: List[Coroutine] = [ token.get_symbol() for token in self._erc20_token_list ] fetch_decimals_tasks: List[Coroutine] = [ token.get_decimals() for token in self._erc20_token_list ] token_symbols: List[str] = await safe_gather(*fetch_symbols_tasks) token_decimals: List[int] = await safe_gather(*fetch_decimals_tasks ) for token, symbol, decimals in zip(self._erc20_token_list, token_symbols, token_decimals): self._erc20_tokens[symbol] = token self._asset_decimals[symbol] = decimals self._weth_token = self._erc20_tokens.get("WETH") # Fetch blockchain data. self._local_nonce = await async_scheduler.call_async( lambda: self.get_remote_nonce()) # Create event watchers. self._new_blocks_watcher = NewBlocksWatcher(self._w3) self._account_balance_watcher = AccountBalanceWatcher( self._w3, self._new_blocks_watcher, self._account.address, [ erc20_token.address for erc20_token in self._erc20_tokens.values() ], [token.abi for token in self._erc20_tokens.values()]) self._erc20_events_watcher = ERC20EventsWatcher( self._w3, self._new_blocks_watcher, [token.address for token in self._erc20_tokens.values()], [token.abi for token in self._erc20_tokens.values()], [self._account.address]) self._incoming_eth_watcher = IncomingEthWatcher( self._w3, self._new_blocks_watcher, [self._account.address]) if self._weth_token is not None: self._weth_watcher = WethWatcher(self._w3, self._weth_token, self._new_blocks_watcher, [self._account.address]) self._zeroex_fill_watcher = ZeroExFillWatcher( self._w3, self._new_blocks_watcher) # Connect the event forwarders. self._new_blocks_watcher.add_listener(NewBlocksWatcherEvent.NewBlocks, self._event_forwarder) self._erc20_events_watcher.add_listener( ERC20WatcherEvent.ReceivedToken, self._received_asset_event_forwarder) self._erc20_events_watcher.add_listener( ERC20WatcherEvent.ApprovedToken, self._approved_token_event_forwarder) self._incoming_eth_watcher.add_listener( IncomingEthWatcherEvent.ReceivedEther, self._received_asset_event_forwarder) self._zeroex_fill_watcher.add_listener( ZeroExEvent.Fill, self._zeroex_fill_event_forwarder) if self._weth_watcher is not None: self._weth_watcher.add_listener(WalletEvent.WrappedEth, self._wrapped_eth_event_forwarder) self._weth_watcher.add_listener( WalletEvent.UnwrappedEth, self._unwrapped_eth_event_forwarder) # Start the transaction processing tasks. self._outgoing_transactions_task = safe_ensure_future( self.outgoing_eth_transactions_loop()) self._check_transaction_receipts_task = safe_ensure_future( self.check_transaction_receipts_loop()) # Start the event watchers. await self._new_blocks_watcher.start_network() await self._account_balance_watcher.start_network() await self._erc20_events_watcher.start_network() await self._incoming_eth_watcher.start_network() if self._weth_watcher is not None: await self._weth_watcher.start_network()