def __init__(self): if history: self.session = PromptSession( history=FileHistory(os.path.join(sys.path[0], '.history'))) else: self.session = PromptSession() self.safe_operator = SafeOperator(safe_address, node_url) self.prompt_parser = PromptParser(self.safe_operator)
class SafeCli: def __init__(self): if history: self.session = PromptSession( history=FileHistory(os.path.join(sys.path[0], '.history'))) else: self.session = PromptSession() self.safe_operator = SafeOperator(safe_address, node_url) self.prompt_parser = PromptParser(self.safe_operator) def print_startup_info(self): print_formatted_text( pyfiglet.figlet_format('Gnosis Safe CLI')) # Print fancy text print_formatted_text( HTML('<b><ansigreen>Loading Safe information...</ansigreen></b>')) self.safe_operator.print_info() def get_prompt_text(self): return HTML( f'<bold><ansiblue>{safe_address}</ansiblue><ansired> > </ansired></bold>' ) def get_bottom_toolbar(self): return HTML( f'<b><style fg="ansiyellow">network={self.safe_operator.network.name} ' f'{self.safe_operator.safe_cli_info}</style></b>') def loop(self): while True: try: command = self.session.prompt( self.get_prompt_text, auto_suggest=AutoSuggestFromHistory(), bottom_toolbar=self.get_bottom_toolbar, lexer=PygmentsLexer(SafeLexer), completer=SafeCompleter()) if not command.strip(): continue self.prompt_parser.process_command(command) except EOFError: break except KeyboardInterrupt: continue except (argparse.ArgumentError, argparse.ArgumentTypeError, SystemExit): pass
def loop(self): while True: try: command = self.session.prompt( self.get_prompt_text, auto_suggest=AutoSuggestFromHistory(), bottom_toolbar=self.get_bottom_toolbar, lexer=PygmentsLexer(SafeLexer), completer=SafeCompleter(), ) if not command.strip(): continue if new_operator := self.parse_operator_mode(command): self.prompt_parser = PromptParser(new_operator) new_operator.refresh_safe_cli_info( ) # ClI info needs to be initialized else: self.prompt_parser.process_command(command)
class SafeCli: def __init__(self): if history: self.session = PromptSession( history=FileHistory(os.path.join(sys.path[0], ".history"))) else: self.session = PromptSession() self.safe_operator = SafeOperator(safe_address, node_url) self.prompt_parser = PromptParser(self.safe_operator) def print_startup_info(self): print_formatted_text( pyfiglet.figlet_format("Gnosis Safe CLI")) # Print fancy text print_formatted_text( HTML("<b><ansigreen>Loading Safe information...</ansigreen></b>")) self.safe_operator.print_info() def get_prompt_text(self): if isinstance(self.prompt_parser.safe_operator, SafeRelayOperator): return HTML( f"<bold><ansiblue>relay-service > {safe_address}</ansiblue><ansired> > </ansired></bold>" ) elif isinstance(self.prompt_parser.safe_operator, SafeTxServiceOperator): return HTML( f"<bold><ansiblue>tx-service > {safe_address}</ansiblue><ansired> > </ansired></bold>" ) elif isinstance(self.prompt_parser.safe_operator, SafeOperator): return HTML( f"<bold><ansiblue>blockchain > {safe_address}</ansiblue><ansired> > </ansired></bold>" ) def get_bottom_toolbar(self): return HTML( f'<b><style fg="ansiyellow">network={self.safe_operator.network.name} ' f"{self.safe_operator.safe_cli_info}</style></b>") def parse_operator_mode(self, command: str) -> Optional[SafeOperator]: """ Parse operator mode to switch between blockchain (default), relay-service, and tx-service :param command: :return: SafeOperator if detected """ split_command = command.split() try: if (split_command[0]) == "tx-service": print_formatted_text( HTML( "<b><ansigreen>Sending txs to tx service</ansigreen></b>" )) return SafeTxServiceOperator(safe_address, node_url) elif split_command[0] == "relay-service": if len(split_command) == 2 and Web3.isChecksumAddress( split_command[1]): gas_token = split_command[1] else: gas_token = None print_formatted_text( HTML( f"<b><ansigreen>Sending txs trough relay service gas-token={gas_token}</ansigreen></b>" )) return SafeRelayOperator(safe_address, node_url, gas_token=gas_token) elif split_command[0] == "blockchain": print_formatted_text( HTML( "<b><ansigreen>Sending txs to blockchain</ansigreen></b>" )) return self.safe_operator except SafeServiceNotAvailable: print_formatted_text( HTML( "<b><ansired>Mode not supported on this network</ansired></b>" )) def loop(self): while True: try: command = self.session.prompt( self.get_prompt_text, auto_suggest=AutoSuggestFromHistory(), bottom_toolbar=self.get_bottom_toolbar, lexer=PygmentsLexer(SafeLexer), completer=SafeCompleter(), ) if not command.strip(): continue if new_operator := self.parse_operator_mode(command): self.prompt_parser = PromptParser(new_operator) new_operator.refresh_safe_cli_info( ) # ClI info needs to be initialized else: self.prompt_parser.process_command(command) except EOFError: break
def test_safe_cli_happy_path(self): accounts = [self.ethereum_test_account, Account.create()] account_addresses = [account.address for account in accounts] safe_address = self.deploy_test_safe(owners=account_addresses, threshold=2, initial_funding_wei=self.w3.toWei(1, 'ether')).safe_address safe = Safe(safe_address, self.ethereum_client) safe_operator = SafeOperator(safe_address, self.ethereum_node_url) prompt_parser = PromptParser(safe_operator) random_address = Account.create().address self.assertEqual(safe_operator.accounts, set()) prompt_parser.process_command(f'load_cli_owners {self.ethereum_test_account.key.hex()}') self.assertEqual(safe_operator.default_sender, self.ethereum_test_account) self.assertEqual(safe_operator.accounts, {self.ethereum_test_account}) prompt_parser.process_command(f'send_ether {random_address} 1') # No enough signatures self.assertEqual(self.ethereum_client.get_balance(random_address), 0) value = 123 prompt_parser.process_command(f'load_cli_owners {accounts[1].key.hex()}') prompt_parser.process_command(f'send_ether {random_address} {value}') self.assertEqual(self.ethereum_client.get_balance(random_address), value) # Change threshold self.assertEqual(safe_operator.safe_cli_info.threshold, 2) self.assertEqual(safe.retrieve_threshold(), 2) prompt_parser.process_command('change_threshold 1') self.assertEqual(safe_operator.safe_cli_info.threshold, 1) self.assertEqual(safe.retrieve_threshold(), 1) # Approve Hash safe_tx_hash = Web3.keccak(text='hola') self.assertFalse(safe_operator.safe.retrieve_is_hash_approved(accounts[0].address, safe_tx_hash)) prompt_parser.process_command(f'approve_hash {safe_tx_hash.hex()} {accounts[0].address}') self.assertTrue(safe_operator.safe.retrieve_is_hash_approved(accounts[0].address, safe_tx_hash)) # Remove owner self.assertEqual(len(safe_operator.safe_cli_info.owners), 2) self.assertEqual(len(safe.retrieve_owners()), 2) prompt_parser.process_command(f'remove_owner {accounts[1].address}') self.assertEqual(safe_operator.safe_cli_info.owners, [self.ethereum_test_account.address]) self.assertEqual(safe.retrieve_owners(), [self.ethereum_test_account.address])
parser.add_argument('node_url', help='Ethereum node url') args = parser.parse_args() safe_address = args.safe_address node_url = args.node_url session = PromptSession() if __name__ == '__main__': safe_operator = SafeOperator(safe_address, node_url) print_formatted_text( pyfiglet.figlet_format('Gnosis Safe CLI')) # Print fancy text print_formatted_text( HTML(f'<b><ansigreen>Loading Safe information...</ansigreen></b>')) safe_operator.print_info() prompt_parser = PromptParser(safe_operator) def get_prompt_text(): return HTML( f'<bold><ansiblue>{safe_address}</ansiblue><ansired> > </ansired></bold>' ) def bottom_toolbar(): return HTML( f'<b><style fg="ansiyellow">network={safe_operator.network_name} ' f'{safe_operator.safe_cli_info}</style></b>') while True: try: command = session.prompt(get_prompt_text(), auto_suggest=AutoSuggestFromHistory(),