def set_options(self, mock_networking, etherscan, json_ipc, verbose, quiet, no_logs, console_logs, file_logs, sentry_logs, log_level, debug): # Session Emitter for pre and post character control engagement. if verbose and quiet: raise click.BadOptionUsage( option_name="quiet", message="--verbose and --quiet are mutually exclusive " "and cannot be used at the same time.") if verbose: NucypherClickConfig.verbosity = 2 elif quiet: NucypherClickConfig.verbosity = 0 else: NucypherClickConfig.verbosity = 1 if json_ipc: emitter = JSONRPCStdoutEmitter(verbosity=NucypherClickConfig.verbosity) else: emitter = StdoutEmitter(verbosity=NucypherClickConfig.verbosity) self.attach_emitter(emitter) if verbose: self.emitter.message("Verbose mode is enabled", color='blue') # Logging if debug and no_logs: raise click.BadOptionUsage( option_name="no-logs", message="--debug and --no-logs cannot be used at the same time.") # Defaults if file_logs is None: file_logs = self.log_to_file if sentry_logs is None: sentry_logs = self.log_to_sentry if debug: console_logs = True file_logs = True sentry_logs = False log_level = 'debug' if no_logs: console_logs = False file_logs = False sentry_logs = False GlobalLoggerSettings.set_log_level(log_level_name=log_level) if console_logs: GlobalLoggerSettings.start_console_logging() if file_logs: GlobalLoggerSettings.start_text_file_logging() GlobalLoggerSettings.start_json_file_logging() if sentry_logs: GlobalLoggerSettings.start_sentry_logging(self.sentry_endpoint) # CLI Session Configuration self.mock_networking = mock_networking self.debug = debug self.json_ipc = json_ipc self.etherscan = etherscan # Only used for testing outputs; # Redirects outputs to in-memory python containers. if mock_networking: self.emitter.message("WARNING: Mock networking is enabled") self.middleware = MockRestMiddleware() else: self.middleware = RestMiddleware()
def sign_and_broadcast_transaction(self, transaction_dict, transaction_name: str = "", confirmations: int = 0) -> dict: # # Setup # # TODO # 1754 - Move this to singleton - I do not approve... nor does Bogdan? if GlobalLoggerSettings._json_ipc: emitter = JSONRPCStdoutEmitter() else: emitter = StdoutEmitter() if self.transacting_power is READ_ONLY_INTERFACE: raise self.InterfaceError(str(READ_ONLY_INTERFACE)) # # Sign # # TODO: Show the USD Price: https://api.coinmarketcap.com/v1/ticker/ethereum/ price = transaction_dict['gasPrice'] cost_wei = price * transaction_dict['gas'] cost = Web3.fromWei(cost_wei, 'gwei') if self.transacting_power.is_device: emitter.message( f'Confirm transaction {transaction_name} on hardware wallet... ({cost} gwei @ {price})', color='yellow') signed_raw_transaction = self.transacting_power.sign_transaction( transaction_dict) # # Broadcast # emitter.message( f'Broadcasting {transaction_name} Transaction ({cost} gwei @ {price})...', color='yellow') try: txhash = self.client.send_raw_transaction( signed_raw_transaction) # <--- BROADCAST except (TestTransactionFailed, ValueError) as error: raise # TODO: Unify with Transaction failed handling # # Receipt # try: # TODO: Handle block confirmation exceptions receipt = self.client.wait_for_receipt(txhash, timeout=self.TIMEOUT, confirmations=confirmations) except TimeExhausted: # TODO: #1504 - Handle transaction timeout raise else: self.log.debug( f"[RECEIPT-{transaction_name}] | txhash: {receipt['transactionHash'].hex()}" ) # # Confirmations # # Primary check transaction_status = receipt.get('status', UNKNOWN_TX_STATUS) if transaction_status == 0: failure = f"Transaction transmitted, but receipt returned status code 0. " \ f"Full receipt: \n {pprint.pformat(receipt, indent=2)}" raise self.InterfaceError(failure) if transaction_status is UNKNOWN_TX_STATUS: self.log.info( f"Unknown transaction status for {txhash} (receipt did not contain a status field)" ) # Secondary check tx = self.client.get_transaction(txhash) if tx["gas"] == receipt["gasUsed"]: raise self.InterfaceError( f"Transaction consumed 100% of transaction gas." f"Full receipt: \n {pprint.pformat(receipt, indent=2)}") return receipt
def __init__(self, json_ipc, verbose, quiet, no_logs, console_logs, file_logs, sentry_logs, log_level, debug): self.log = Logger(self.__class__.__name__) # Session Emitter for pre and post character control engagement. if verbose and quiet: raise click.BadOptionUsage( option_name="quiet", message="--verbose and --quiet are mutually exclusive " "and cannot be used at the same time.") if verbose: GroupGeneralConfig.verbosity = 2 elif quiet: GroupGeneralConfig.verbosity = 0 else: GroupGeneralConfig.verbosity = 1 if json_ipc: emitter = JSONRPCStdoutEmitter( verbosity=GroupGeneralConfig.verbosity) else: emitter = StdoutEmitter(verbosity=GroupGeneralConfig.verbosity) self.emitter = emitter if verbose: self.emitter.message("Verbose mode is enabled", color='blue') # Logging if debug and no_logs: raise click.BadOptionUsage( option_name="no-logs", message="--debug and --no-logs cannot be used at the same time." ) # Defaults if file_logs is None: file_logs = self.log_to_file if sentry_logs is None: sentry_logs = self.log_to_sentry if debug: console_logs = True file_logs = True sentry_logs = False log_level = 'debug' if no_logs: console_logs = False file_logs = False sentry_logs = False if json_ipc: console_logs = False GlobalLoggerSettings.set_log_level(log_level_name=log_level) if console_logs: GlobalLoggerSettings.start_console_logging() if file_logs: GlobalLoggerSettings.start_text_file_logging() GlobalLoggerSettings.start_json_file_logging() if sentry_logs: GlobalLoggerSettings.start_sentry_logging(self.sentry_endpoint) if json_ipc: GlobalLoggerSettings.stop_console_logging() # JSON-RPC Protection self.debug = debug self.json_ipc = json_ipc
def sign_and_broadcast_transaction( self, transaction_dict, transaction_name: str = "", confirmations: int = 0, fire_and_forget: bool = False) -> Union[TxReceipt, HexBytes]: """ Takes a transaction dictionary, signs it with the configured signer, then broadcasts the signed transaction using the ethereum provider's eth_sendRawTransaction RPC endpoint. Optionally blocks for receipt and confirmation with 'confirmations', and 'fire_and_forget' flags. If 'fire and forget' is True this method returns the transaction hash only, without waiting for a receipt - otherwise return the transaction receipt. """ # # Setup # # TODO # 1754 - Move this to singleton - I do not approve... nor does Bogdan? if GlobalLoggerSettings._json_ipc: emitter = JSONRPCStdoutEmitter() else: emitter = StdoutEmitter() if self.transacting_power is READ_ONLY_INTERFACE: raise self.InterfaceError(str(READ_ONLY_INTERFACE)) # # Sign # # TODO: Show the USD Price: https://api.coinmarketcap.com/v1/ticker/ethereum/ price = transaction_dict['gasPrice'] price_gwei = Web3.fromWei(price, 'gwei') cost_wei = price * transaction_dict['gas'] cost = Web3.fromWei(cost_wei, 'ether') if self.transacting_power.is_device: emitter.message( f'Confirm transaction {transaction_name} on hardware wallet... ' f'({cost} ETH @ {price_gwei} gwei)', color='yellow') signed_raw_transaction = self.transacting_power.sign_transaction( transaction_dict) # # Broadcast # emitter.message( f'Broadcasting {transaction_name} Transaction ({cost} ETH @ {price_gwei} gwei)...', color='yellow') try: txhash = self.client.send_raw_transaction( signed_raw_transaction) # <--- BROADCAST except (TestTransactionFailed, ValueError): raise # TODO: Unify with Transaction failed handling -- Entry point for _handle_failed_transaction else: if fire_and_forget: return txhash # # Receipt # try: # TODO: Handle block confirmation exceptions receipt = self.client.wait_for_receipt(txhash, timeout=self.TIMEOUT, confirmations=confirmations) except TimeExhausted: # TODO: #1504 - Handle transaction timeout raise else: self.log.debug( f"[RECEIPT-{transaction_name}] | txhash: {receipt['transactionHash'].hex()}" ) # # Confirmations # # Primary check transaction_status = receipt.get('status', UNKNOWN_TX_STATUS) if transaction_status == 0: failure = f"Transaction transmitted, but receipt returned status code 0. " \ f"Full receipt: \n {pprint.pformat(receipt, indent=2)}" raise self.InterfaceError(failure) if transaction_status is UNKNOWN_TX_STATUS: self.log.info( f"Unknown transaction status for {txhash} (receipt did not contain a status field)" ) # Secondary check tx = self.client.get_transaction(txhash) if tx["gas"] == receipt["gasUsed"]: raise self.InterfaceError( f"Transaction consumed 100% of transaction gas." f"Full receipt: \n {pprint.pformat(receipt, indent=2)}") return receipt
def sign_and_broadcast_transaction(self, unsigned_transaction, transaction_name: str = "", confirmations: int = 0) -> dict: # # Setup # # TODO # 1754 # TODO: Move this to singleton - I do not approve... nor does Bogdan? if GlobalLoggerSettings._json_ipc: emitter = JSONRPCStdoutEmitter() else: emitter = StdoutEmitter() if self.transacting_power is READ_ONLY_INTERFACE: raise self.InterfaceError(str(READ_ONLY_INTERFACE)) # # Sign # # TODO: Show the USD Price # Price Oracle # https://api.coinmarketcap.com/v1/ticker/ethereum/ price = unsigned_transaction['gasPrice'] cost_wei = price * unsigned_transaction['gas'] cost = Web3.fromWei(cost_wei, 'gwei') if self.transacting_power.device: emitter.message( f'Confirm transaction {transaction_name} on hardware wallet... ({cost} gwei @ {price})', color='yellow') signed_raw_transaction = self.transacting_power.sign_transaction( unsigned_transaction) # # Broadcast # emitter.message( f'Broadcasting {transaction_name} Transaction ({cost} gwei @ {price})...', color='yellow') txhash = self.client.send_raw_transaction(signed_raw_transaction) try: receipt = self.client.wait_for_receipt(txhash, timeout=self.TIMEOUT) except TimeExhausted: # TODO: #1504 - Handle transaction timeout raise else: self.log.debug( f"[RECEIPT-{transaction_name}] | txhash: {receipt['transactionHash'].hex()}" ) # # Confirm # # Primary check deployment_status = receipt.get('status', UNKNOWN_TX_STATUS) if deployment_status == 0: failure = f"Transaction transmitted, but receipt returned status code 0. " \ f"Full receipt: \n {pprint.pformat(receipt, indent=2)}" raise self.InterfaceError(failure) if deployment_status is UNKNOWN_TX_STATUS: self.log.info( f"Unknown transaction status for {txhash} (receipt did not contain a status field)" ) # Secondary check tx = self.client.get_transaction(txhash) if tx["gas"] == receipt["gasUsed"]: raise self.InterfaceError( f"Transaction consumed 100% of transaction gas." f"Full receipt: \n {pprint.pformat(receipt, indent=2)}") # Block confirmations if confirmations: start = maya.now() confirmations_so_far = self.get_confirmations(receipt) while confirmations_so_far < confirmations: self.log.info( f"So far, we've only got {confirmations_so_far} confirmations. " f"Waiting for {confirmations - confirmations_so_far} more." ) time.sleep(3) confirmations_so_far = self.get_confirmations(receipt) if (maya.now() - start).seconds > self.TIMEOUT: raise self.NotEnoughConfirmations return receipt