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)