diff --git a/requirements.txt b/requirements.txt index f6f100fe..7251c4bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ colorama==0.4.6 +ledgereth==0.9.0 packaging>=23.1 prompt_toolkit==3.0.39 pyfiglet==1.0.0 pygments==2.16.1 requests==2.31.0 -safe-eth-py==6.0.0b2 -ledgereth==0.7.3 +safe-eth-py==6.0.0b3 tabulate==0.9.0 web3==6.10.0 diff --git a/safe_cli/operators/hw_accounts/ledger_account.py b/safe_cli/operators/hw_accounts/ledger_account.py index 36a97101..a73a7329 100644 --- a/safe_cli/operators/hw_accounts/ledger_account.py +++ b/safe_cli/operators/hw_accounts/ledger_account.py @@ -1,8 +1,11 @@ import warnings +from eth_account.datastructures import SignedTransaction from eth_account.signers.base import BaseAccount +from hexbytes import HexBytes from ledgerblue import Dongle from ledgereth import create_transaction, sign_typed_data_draft +from web3 import Web3 from web3.types import TxParams @@ -73,7 +76,7 @@ def signTransaction(self, transaction_dict): ) pass - def sign_transaction(self, tx: TxParams): + def sign_transaction(self, tx: TxParams) -> SignedTransaction: signed = create_transaction( destination=tx["to"], amount=tx["value"], @@ -86,7 +89,14 @@ def sign_transaction(self, tx: TxParams): sender_path=self.path, dongle=self.dongle, ) - return signed + raw_transaction = signed.raw_transaction() + return SignedTransaction( + HexBytes(raw_transaction), + Web3.keccak(HexBytes(raw_transaction)), + signed.sender_r, + signed.sender_s, + signed.y_parity, + ) def __bytes__(self): return self.key diff --git a/safe_cli/operators/safe_operator.py b/safe_cli/operators/safe_operator.py index 1976715f..47833131 100644 --- a/safe_cli/operators/safe_operator.py +++ b/safe_cli/operators/safe_operator.py @@ -11,7 +11,7 @@ from hexbytes import HexBytes from ledgereth import get_account_by_path from ledgereth.comms import init_dongle -from ledgereth.exceptions import LedgerNotFound +from ledgereth.exceptions import LedgerAppNotOpened, LedgerLocked, LedgerNotFound from packaging import version as semantic_version from prompt_toolkit import HTML, print_formatted_text from web3 import Web3 @@ -33,12 +33,12 @@ from safe_cli.api.transaction_service_api import TransactionServiceApi from safe_cli.ethereum_hd_wallet import get_account_from_words +from safe_cli.operators.hw_accounts.ledger_account import LedgerAccount from safe_cli.safe_addresses import ( get_default_fallback_handler_address, get_safe_contract_address, get_safe_l2_contract_address, ) -from safe_cli.operators.hw_accounts.ledger_account import LedgerAccount from safe_cli.utils import get_erc_20_list, yes_or_no_question @@ -307,28 +307,38 @@ def load_cli_owners(self, keys: List[str]): def load_ledger_cli_owners(self): try: dongle = init_dongle() + # Search between 10 first accounts + for index in range(10): + path = f"44'/60'/{index}'/0/0" + account = get_account_by_path(path, dongle=dongle) + if account.address in self.safe_cli_info.owners: + sender = LedgerAccount(account.path, account.address, dongle) + self.accounts.add(sender) + balance = self.ethereum_client.get_balance(account.address) + print_formatted_text( + HTML( + f"Loaded account {account.address} " + f'with balance={Web3.from_wei(balance, "ether")} ether' + f"Ledger account cannot be defined as sender" + ) + ) + # TODO add ledger as sender + break except LedgerNotFound: print_formatted_text( HTML("Unable to find Ledger device") ) return - # Search between 10 first accounts - for index in range(10): - path = f"44'/60'/{index}'/0/0" - account = get_account_by_path(path, dongle=dongle) - if account.address in self.safe_cli_info.owners: - sender = LedgerAccount(account.path, account.address, dongle) - self.accounts.add(sender) - balance = self.ethereum_client.get_balance(account.address) - print_formatted_text( - HTML( - f"Loaded account {account.address} " - f'with balance={Web3.fromWei(balance, "ether")} ether' - f"Ledger account cannot be defined as sender" - ) - ) - # TODO add ledger as sender - break + except LedgerAppNotOpened: + print_formatted_text( + HTML("Ensure open ethereum app on your ledger") + ) + return + except LedgerLocked: + print_formatted_text( + HTML("Ensure open ethereum app on your ledger") + ) + return def unload_cli_owners(self, owners: List[str]): accounts_to_remove: Set[Account] = set() diff --git a/setup.py b/setup.py index 2f0e4b28..afa425a8 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ "pyfiglet>=0.8", "pygments>=2", "requests>=2", - "safe-eth-py>=6", + "safe-eth-py==6.0.0b3", "tabulate>=0.8", ], packages=setuptools.find_packages(), diff --git a/tests/test_safe_operator.py b/tests/test_safe_operator.py index 6b220fbb..39b403b1 100644 --- a/tests/test_safe_operator.py +++ b/tests/test_safe_operator.py @@ -75,7 +75,7 @@ def test_load_cli_owner(self, get_contract_mock: MagicMock): @mock.patch("safe_cli.operators.safe_operator.get_account_by_path") def test_load_ledger_cli_owner(self, mock_get_account_by_path: MagicMock): owner_address = Account.create().address - safe_address = self.deploy_test_safe(owners=[owner_address]).safe_address + safe_address = self.deploy_test_safe(owners=[owner_address]).address safe_operator = SafeOperator(safe_address, self.ethereum_node_url) safe_operator.load_ledger_cli_owners() self.assertEqual(len(safe_operator.accounts), 0)