Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make cli more inclusive #413

Merged
merged 6 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions src/safe_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from safe_cli.argparse_validators import check_ethereum_address
from safe_cli.operators import (
SafeCliTerminationException,
SafeOperator,
SafeServiceNotAvailable,
SafeTxServiceOperator,
Expand Down Expand Up @@ -45,12 +46,21 @@ def __init__(self, safe_address: ChecksumAddress, node_url: str, history: bool):

def print_startup_info(self):
print_formatted_text(text2art("Safe CLI")) # Print fancy text
print_formatted_text(HTML(f"<b><ansigreen>Version {VERSION}</ansigreen></b>"))
print_formatted_text(HTML(f"<b>Version: {VERSION}</b>"))
print_formatted_text(
HTML("<b><ansigreen>Loading Safe information...</ansigreen></b>")
)
self.safe_operator.print_info()

print_formatted_text(
HTML("\nUse the <b>tab key</b> to show options in interactive mode.")
)
print_formatted_text(
HTML(
"The <b>help</b> command displays all available options and the <b>exit</b> command terminates the safe-cli."
)
)

def get_prompt_text(self):
mode: Optional[str] = "blockchain"
if isinstance(self.prompt_parser.safe_operator, SafeTxServiceOperator):
Expand Down Expand Up @@ -111,6 +121,8 @@ def loop(self):
new_operator.refresh_safe_cli_info() # ClI info needs to be initialized
else:
self.prompt_parser.process_command(command)
except SafeCliTerminationException:
break
except EOFError:
break
except KeyboardInterrupt:
Expand All @@ -119,8 +131,29 @@ def loop(self):
pass


def get_usage_msg():
return """
safe-cli [-h] [--history] [--get-safes-from-owner] address node_url

Examples:
safe-cli 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
safe-cli --get-safes-from-owner 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org

safe-cli --history 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
safe-cli --history --get-safes-from-owner 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
"""


def build_safe_cli() -> Optional[SafeCli]:
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(usage=get_usage_msg())
parser.add_argument(
"-v",
"--version",
action="version",
version=f"Safe CLI v{VERSION}",
help="Show program's version number and exit.",
)

parser.add_argument(
"address",
help="The address of the Safe, or an owner address if --get-safes-from-owner is specified.",
Expand Down
3 changes: 2 additions & 1 deletion src/safe_cli/operators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .enums import SafeOperatorMode
from .exceptions import SafeServiceNotAvailable
from .exceptions import SafeCliTerminationException, SafeServiceNotAvailable
from .safe_operator import SafeOperator
from .safe_tx_service_operator import SafeTxServiceOperator

Expand All @@ -8,4 +8,5 @@
"SafeOperatorMode",
"SafeServiceNotAvailable",
"SafeTxServiceOperator",
"SafeCliTerminationException",
]
4 changes: 4 additions & 0 deletions src/safe_cli/operators/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,7 @@ class SafeServiceNotAvailable(SafeOperatorException):

class HardwareWalletException(SafeOperatorException):
pass


class SafeCliTerminationException(SafeOperatorException):
pass
29 changes: 29 additions & 0 deletions src/safe_cli/prompt_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import functools

from prompt_toolkit import HTML, print_formatted_text
from prompt_toolkit.formatted_text import html

from gnosis.safe.api import SafeAPIException

Expand All @@ -25,6 +26,7 @@
NotEnoughSignatures,
NotEnoughTokenToSend,
SafeAlreadyUpdatedException,
SafeCliTerminationException,
SafeOperatorException,
SafeVersionNotSupportedException,
SameFallbackHandlerException,
Expand All @@ -33,6 +35,7 @@
ThresholdLimitException,
)
from .operators.safe_operator import SafeOperator
from .safe_completer_constants import meta, safe_commands_arguments


def safe_exception(function):
Expand Down Expand Up @@ -319,6 +322,24 @@ def remove_delegate(args):
def remove_proposed_transaction(args):
safe_operator.remove_proposed_transaction(args.safe_tx_hash)

def list_commands(args):
print_formatted_text(
HTML("<b>The following commands can be used:</b>"), end="\n\n"
)
for key in sorted(safe_commands_arguments.keys()):
print_formatted_text(
HTML(
f"<b>{key} {html.html_escape(safe_commands_arguments.get(key))}</b>"
)
)
print_formatted_text(HTML("&#9;"), meta.get(key, ""))
print_formatted_text(
HTML("\nUse the <b>tab key</b> to show options in interactive mode.")
)

def terminate_cli(args):
raise SafeCliTerminationException()

# Cli owners
parser_show_cli_owners = subparsers.add_parser("show_cli_owners")
parser_show_cli_owners.set_defaults(func=show_cli_owners)
Expand Down Expand Up @@ -542,4 +563,12 @@ def remove_proposed_transaction(args):
"safe_tx_hash", type=check_keccak256_hash
)

# List all command options
parser_help = subparsers.add_parser("help")
parser_help.set_defaults(func=list_commands)

# Terminate safe cli
parser_exit = subparsers.add_parser("exit")
parser_exit.set_defaults(func=terminate_cli)

return prompt_parser
6 changes: 6 additions & 0 deletions src/safe_cli/safe_completer_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"blockchain": "",
"tx-service": "",
"drain": "<address>",
"help": "",
"exit": "",
}

safe_commands = list(safe_commands_arguments.keys())
Expand Down Expand Up @@ -215,4 +217,8 @@
"drain": HTML(
"Command <b>drain</b> will try to send all assets ether and ERC20 to a check-summed account"
),
"help": HTML(
"Command <b>help</b> will display the command options available for use in safe-cli"
),
"exit": HTML("Command <b>exit</b> will terminate the safe-cli process"),
}
21 changes: 19 additions & 2 deletions src/safe_cli/safe_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,32 @@
)
from safe_cli.utils import yes_or_no_question

from . import VERSION
from .argparse_validators import (
check_ethereum_address,
check_positive_integer,
check_private_key,
)


def get_usage_msg():
return """
safe-creator [-h] [-v] [--threshold THRESHOLD] [--owners OWNERS [OWNERS ...]] [--safe-contract SAFE_CONTRACT] [--proxy-factory PROXY_FACTORY] [--callback-handler CALLBACK_HANDLER] [--salt-nonce SALT_NONCE] [--without-events] node_url private_key

Example:
safe-creator https://sepolia.drpc.org 0000000000000000000000000000000000000000000000000000000000000000
"""


def setup_argument_parser():
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(usage=get_usage_msg())
parser.add_argument(
"-v",
"--version",
action="version",
version=f"Safe Creator v{VERSION}",
help="Show program's version number and exit.",
)
parser.add_argument("node_url", help="Ethereum node url")
parser.add_argument(
"private_key", help="Deployer private_key", type=check_private_key
Expand Down Expand Up @@ -88,8 +105,8 @@ def setup_argument_parser():

def main(*args, **kwargs) -> EthereumTxSent:
parser = setup_argument_parser()
print_formatted_text(text2art("Safe Creator")) # Print fancy text
args = parser.parse_args()
print_formatted_text(text2art("Safe Creator")) # Print fancy text
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moved so that the logo is not displayed when the --help and --version commands are executed.

node_url: URI = args.node_url
account: LocalAccount = Account.from_key(args.private_key)
owners: List[str] = args.owners if args.owners else [account.address]
Expand Down
5 changes: 5 additions & 0 deletions tests/test_safe_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from gnosis.safe import Safe

from safe_cli.operators import SafeCliTerminationException
from safe_cli.operators.safe_operator import SafeOperator
from safe_cli.prompt_parser import PromptParser

Expand Down Expand Up @@ -74,6 +75,10 @@ def test_safe_cli_happy_path(self):
)
self.assertEqual(safe.retrieve_owners(), [self.ethereum_test_account.address])

# Terminate cli
with self.assertRaises(SafeCliTerminationException):
prompt_parser.process_command("exit")


if __name__ == "__main__":
unittest.main()
Loading