Example #1
0
 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)
Example #2
0
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 setup_operator(self, number_owners: int = 1) -> SafeOperator:
     assert number_owners >= 1, 'Number of owners cannot be less than 1!'
     safe_address = self.deploy_test_safe(owners=[self.ethereum_test_account.address]).safe_address
     safe_operator = SafeOperator(safe_address, self.ethereum_node_url)
     safe_operator.load_cli_owners([self.ethereum_test_account.key.hex()])
     for _ in range(number_owners - 1):
         account = Account.create()
         safe_operator.add_owner(account.address)
         safe_operator.load_cli_owners([account.key.hex()])
     return safe_operator
Example #4
0
    def test_add_owner(self):
        safe_address = self.deploy_test_safe(
            owners=[self.ethereum_test_account.address]).safe_address
        safe_operator = SafeOperator(safe_address, self.ethereum_node_url)
        with self.assertRaises(ExistingOwnerException):
            safe_operator.add_owner(self.ethereum_test_account.address)

        new_owner = Account.create().address
        with self.assertRaises(SenderRequiredException):
            safe_operator.add_owner(new_owner)

        safe_operator.default_sender = self.ethereum_test_account
        with self.assertRaises(NotEnoughSignatures):
            safe_operator.add_owner(new_owner)

        safe_operator.accounts.add(self.ethereum_test_account)
        safe = Safe(safe_address, self.ethereum_client)
        self.assertTrue(safe_operator.add_owner(new_owner))
        self.assertIn(self.ethereum_test_account, safe_operator.accounts)
        self.assertIn(new_owner, safe.retrieve_owners())
    def test_approve_hash(self):
        safe_address = self.deploy_test_safe(
            owners=[self.ethereum_test_account.address]).safe_address
        safe_operator = SafeOperator(safe_address, self.ethereum_node_url)
        safe_tx_hash = Web3.keccak(text='random-test')
        random_account = Account.create()
        with self.assertRaises(AccountNotLoadedException):
            safe_operator.approve_hash(safe_tx_hash, random_account.address)

        with self.assertRaises(NonExistingOwnerException):
            safe_operator.accounts.add(random_account)
            safe_operator.approve_hash(safe_tx_hash, random_account.address)

        safe_operator.accounts.add(self.ethereum_test_account)
        safe_operator.default_sender = self.ethereum_test_account
        self.assertTrue(
            safe_operator.approve_hash(safe_tx_hash,
                                       self.ethereum_test_account.address))
        with self.assertRaises(HashAlreadyApproved):
            safe_operator.approve_hash(safe_tx_hash,
                                       self.ethereum_test_account.address)
Example #6
0
    def test_remove_owner(self):
        safe_address = self.deploy_test_safe(
            owners=[self.ethereum_test_account.address]).safe_address
        safe_operator = SafeOperator(safe_address, self.ethereum_node_url)
        random_address = Account.create().address
        with self.assertRaises(NonExistingOwnerException):
            safe_operator.remove_owner(random_address)

        safe_operator.load_cli_owners([self.ethereum_test_account.key.hex()])
        new_owner = Account.create().address
        safe = Safe(safe_address, self.ethereum_client)
        self.assertTrue(safe_operator.add_owner(new_owner))
        self.assertIn(new_owner, safe.retrieve_owners())

        self.assertTrue(safe_operator.remove_owner(new_owner))
        self.assertNotIn(new_owner, safe_operator.accounts)
        self.assertNotIn(new_owner, safe.retrieve_owners())
Example #7
0
    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])
Example #8
0
from safe_cli.safe_completer import SafeCompleter
from safe_cli.safe_lexer import SafeLexer
from safe_cli.safe_operator import SafeOperator

parser = argparse.ArgumentParser()
parser.add_argument('safe_address', help='Address of Safe to use')
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} '
Example #9
0
    def test_load_cli_owner(self):
        random_address = Account.create().address
        safe_operator = SafeOperator(random_address, self.ethereum_node_url)
        random_accounts = [Account.create() for _ in range(3)]
        random_accounts_keys = [
            account.key.hex() for account in random_accounts
        ]
        self.assertFalse(safe_operator.accounts)
        safe_operator.load_cli_owners(random_accounts_keys)
        self.assertEqual(len(safe_operator.accounts),
                         len(random_accounts_keys))
        # Test accounts are not duplicated
        safe_operator.load_cli_owners(random_accounts_keys)
        self.assertEqual(len(safe_operator.accounts),
                         len(random_accounts_keys))
        # Test invalid accounts don't make the method break
        safe_operator.load_cli_owners(
            ['aloha', Account.create().key.hex(), 'bye'])
        self.assertEqual(len(safe_operator.accounts),
                         len(random_accounts_keys) + 1)
        # TODO Test setting default sender, mock getBalance

        # Test unload cli owner
        safe_operator.default_sender = random_accounts[0]
        number_of_accounts = len(safe_operator.accounts)
        safe_operator.unload_cli_owners(
            ['aloha', random_accounts[0].address, 'bye'])
        self.assertEqual(len(safe_operator.accounts), number_of_accounts - 1)
        self.assertFalse(safe_operator.default_sender)