Skip to content

Commit

Permalink
feat!: Ape 0.8 support (#44)
Browse files Browse the repository at this point in the history
Co-authored-by: Dalena <mimidalena@gmail.com>
  • Loading branch information
antazoey and dtdang committed May 31, 2024
1 parent 4615d7c commit 415f87a
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
# TODO: Replace with macos-latest when works again.
# https://github.com/actions/setup-python/issues/808
os: [ubuntu-latest, macos-12] # eventually add `windows-latest`
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
python-version: [3.9, "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ repos:
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter]
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]

default_language_version:
python: python3
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ to load and create accounts, sign messages, and sign transactions.

## Dependencies

- [python3](https://www.python.org/downloads) version 3.8 up to 3.12.
- [python3](https://www.python.org/downloads) version 3.9 up to 3.12.

## Installation

Expand Down
6 changes: 3 additions & 3 deletions ape_ledger/_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Tuple, Union
from typing import Union

import click
from ape import accounts
Expand All @@ -18,7 +18,7 @@
from ape_ledger.hdpath import HDAccountPath, HDBasePath


def _select_account(hd_path: Union[HDBasePath, str]) -> Tuple[str, HDAccountPath]:
def _select_account(hd_path: Union[HDBasePath, str]) -> tuple[str, HDAccountPath]:
choices = AddressPromptChoice(hd_path)
return choices.get_user_selected_account()

Expand Down Expand Up @@ -52,7 +52,7 @@ def _list(cli_ctx):
click.echo(f" {account.address}{alias_display}{hd_path_display}")


def _get_ledger_accounts() -> List[LedgerAccount]:
def _get_ledger_accounts() -> list[LedgerAccount]:
return [a for a in accounts if isinstance(a, LedgerAccount)]


Expand Down
7 changes: 5 additions & 2 deletions ape_ledger/accounts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from collections.abc import Iterator
from pathlib import Path
from typing import Any, Dict, Iterator, Optional
from typing import Any, Optional

import rich
from ape.api import AccountAPI, AccountContainerAPI, TransactionAPI
Expand Down Expand Up @@ -31,6 +32,8 @@ def _to_bytes(val) -> bytes:


class AccountContainer(AccountContainerAPI):
name: str = "ledger"

@property
def accounts(self) -> Iterator[AccountAPI]:
for account_file in self._account_files:
Expand Down Expand Up @@ -172,7 +175,7 @@ def sign_message(self, msg: Any, **signer_options) -> Optional[MessageSignature]

def sign_transaction(self, txn: TransactionAPI, **kwargs) -> Optional[TransactionAPI]:
txn.chain_id = 1
txn_dict: Dict = {
txn_dict: dict = {
"nonce": txn.nonce,
"gas": txn.gas_limit,
"amount": txn.value,
Expand Down
4 changes: 2 additions & 2 deletions ape_ledger/choices.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Optional, Tuple, Union
from typing import Any, Optional, Union

import click
from ape.cli import PromptChoice
Expand Down Expand Up @@ -58,7 +58,7 @@ def convert(
self._choice_index = self._choice_index if address_index is None else address_index
return address

def get_user_selected_account(self) -> Tuple[str, HDAccountPath]:
def get_user_selected_account(self) -> tuple[str, HDAccountPath]:
"""Returns the selected address from the user along with the HD path.
The user is able to page using special characters ``n`` and ``p``.
"""
Expand Down
18 changes: 9 additions & 9 deletions ape_ledger/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import atexit
from functools import cached_property
from typing import Dict, Tuple

import hid # type: ignore
from ape.logging import LogLevel, logger
Expand All @@ -13,7 +12,7 @@


class DeviceFactory:
device_map: Dict[str, "LedgerDeviceClient"] = {}
device_map: dict[str, "LedgerDeviceClient"] = {}

def create_device(self, account: HDAccountPath):
if account.path in self.device_map:
Expand Down Expand Up @@ -56,23 +55,24 @@ def close():
def get_address(self) -> str:
return get_account_by_path(self._account, dongle=self.dongle).address

def sign_message(self, text: bytes) -> Tuple[int, int, int]:
def sign_message(self, text: bytes) -> tuple[int, int, int]:
signed_msg = sign_message(text, sender_path=self._account, dongle=self.dongle)
return signed_msg.v, signed_msg.r, signed_msg.s

def sign_typed_data(self, domain_hash: bytes, message_hash: bytes) -> Tuple[int, int, int]:
def sign_typed_data(self, domain_hash: bytes, message_hash: bytes) -> tuple[int, int, int]:
signed_msg = sign_typed_data_draft(
domain_hash, message_hash, sender_path=self._account, dongle=self.dongle
)
return signed_msg.v, signed_msg.r, signed_msg.s

def sign_transaction(self, txn: Dict) -> Tuple[int, int, int]:
def sign_transaction(self, txn: dict) -> tuple[int, int, int]:
kwargs = {**txn, "sender_path": self._account, "dongle": self.dongle}
signed_tx = create_transaction(**kwargs)
if isinstance(signed_tx, SignedType2Transaction):
return signed_tx.y_parity, signed_tx.sender_r, signed_tx.sender_s
else:
return signed_tx.v, signed_tx.r, signed_tx.s
return (
(signed_tx.y_parity, signed_tx.sender_r, signed_tx.sender_s)
if isinstance(signed_tx, SignedType2Transaction)
else (signed_tx.v, signed_tx.r, signed_tx.s)
)


_device_factory = DeviceFactory()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ write_to = "ape_ledger/version.py"

[tool.black]
line-length = 100
target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
target-version = ['py39', 'py310', 'py311', 'py312']
include = '\.pyi?$'

[tool.pytest.ini_options]
Expand Down
6 changes: 2 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import find_packages, setup

extras_require = {
Expand Down Expand Up @@ -61,7 +60,7 @@
url="https://github.com/ApeWorX/ape-ledger",
include_package_data=True,
install_requires=[
"eth-ape>=0.7.0,<0.8",
"eth-ape>=0.8.1,<0.9",
"ledgereth>=0.9.1,<0.10",
"click", # Use same version as eth-ape
"rich", # Use same version as eth-ape
Expand All @@ -77,7 +76,7 @@
"ape_ledger=ape_ledger._cli:cli",
],
},
python_requires=">=3.8,<4",
python_requires=">=3.9,<4",
extras_require=extras_require,
py_modules=["ape_ledger"],
license="Apache-2.0",
Expand All @@ -93,7 +92,6 @@
"Operating System :: MacOS",
"Operating System :: POSIX",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down
30 changes: 22 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import json
import shutil
from pathlib import Path
from tempfile import mkdtemp

import ape
import pytest
from ape import accounts, networks
from click.testing import CliRunner
from eth_account.messages import encode_defunct
from eth_pydantic_types import HexBytes
Expand All @@ -10,6 +13,17 @@
TEST_HD_PATH = "m/44'/60'/{x}'/0/0"


# NOTE: Ensure that we don't use local paths for these
DATA_FOLDER = Path(mkdtemp()).resolve()
ape.config.DATA_FOLDER = DATA_FOLDER


@pytest.fixture(autouse=True, scope="session")
def clean_data_folder():
yield # Run all tests
shutil.rmtree(DATA_FOLDER, ignore_errors=True)


@pytest.fixture
def hd_path():
return TEST_HD_PATH
Expand All @@ -22,7 +36,7 @@ def alias():

@pytest.fixture
def test_accounts():
return accounts.test_accounts
return ape.accounts.test_accounts


@pytest.fixture
Expand All @@ -47,7 +61,7 @@ def address(account_addresses):

@pytest.fixture(autouse=True)
def connection():
with networks.ethereum.local.use_provider("test") as provider:
with ape.networks.ethereum.local.use_provider("test") as provider:
yield provider


Expand Down Expand Up @@ -101,12 +115,12 @@ def runner():

@pytest.fixture
def assert_account(address):
def fn(account_path, expected_address=None, expected_hdpath="m/44'/60'/0'/0/0"):
def fn(account_path: Path, expected_address=None, expected_hdpath="m/44'/60'/0'/0/0"):
expected_address = expected_address or address
with open(account_path) as account_file:
account_data = json.load(account_file)
assert account_data["address"] == expected_address
assert account_data["hdpath"] == expected_hdpath
assert account_path.is_file(), "Account file missing."
account_data = json.loads(account_path.read_text())
assert account_data["address"] == expected_address
assert account_data["hdpath"] == expected_hdpath

return fn

Expand Down
20 changes: 9 additions & 11 deletions tests/test_accounts.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import json
import tempfile
from pathlib import Path
from typing import Optional, cast

import pytest
from ape import networks
from ape.api import TransactionAPI
from ape.types import AddressType
from ape.utils import create_tempdir
from ape_ethereum.ecosystem import DynamicFeeTransaction, StaticFeeTransaction
from eip712.messages import EIP712Message, EIP712Type
from eth_account.messages import SignableMessage
Expand Down Expand Up @@ -105,21 +104,20 @@ def isolated_file_system(runner):

@pytest.fixture
def account(mock_container, create_account, hd_path):
with tempfile.TemporaryDirectory() as temp_dir:
path = Path(temp_dir) / "account.json"
with create_tempdir() as temp_dir:
path = temp_dir / "account.json"
create_account(path, hd_path)
with networks.ethereum.local.use_provider("test"):
yield LedgerAccount(container=mock_container, account_file_path=path)
yield LedgerAccount(name=mock_container, account_file_path=path)


class TestAccountContainer:
def test_save_account(self, mock_container, alias, address, hd_path, assert_account):
with tempfile.TemporaryDirectory() as temp_dir:
data_path = Path(temp_dir)
container = AccountContainer(data_folder=data_path, account_type=LedgerAccount)
container.save_account(alias, address, hd_path)
account_path = data_path / f"{alias}.json"
assert_account(account_path, expected_hdpath=hd_path)
container = AccountContainer(account_type=LedgerAccount)
container.save_account(alias, address, hd_path)
temp_dir = container.config_manager.DATA_FOLDER
account_path = temp_dir / "ledger" / f"{alias}.json"
assert_account(account_path, expected_hdpath=hd_path)


class TestLedgerAccount:
Expand Down

0 comments on commit 415f87a

Please sign in to comment.