diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 2cdc07b..52207a6 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -20,15 +20,15 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: python - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/commitlint.yaml b/.github/workflows/commitlint.yaml index d63f5a2..f5ce092 100644 --- a/.github/workflows/commitlint.yaml +++ b/.github/workflows/commitlint.yaml @@ -9,12 +9,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" diff --git a/.github/workflows/prtitle.yaml b/.github/workflows/prtitle.yaml index a6cd3b0..6e8999f 100644 --- a/.github/workflows/prtitle.yaml +++ b/.github/workflows/prtitle.yaml @@ -12,10 +12,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 2c88013..081d138 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8883134..9179bf9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" @@ -42,10 +42,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" @@ -66,10 +66,10 @@ jobs: python-version: [3.8, 3.9, "3.10", "3.11"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -89,10 +89,10 @@ jobs: # fail-fast: true # # steps: -# - uses: actions/checkout@v3 +# - uses: actions/checkout@v4 # # - name: Setup Python -# uses: actions/setup-python@v4 +# uses: actions/setup-python@v5 # with: # python-version: "3.10" # diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0fb78ed..282879a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,18 +10,18 @@ repos: - id: isort - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 24.2.0 hooks: - id: black name: black - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + rev: v1.8.0 hooks: - id: mypy additional_dependencies: [types-requests, types-setuptools] diff --git a/ape_frame/accounts.py b/ape_frame/accounts.py index 0f9cc1a..db4048d 100644 --- a/ape_frame/accounts.py +++ b/ape_frame/accounts.py @@ -1,4 +1,4 @@ -from typing import Any, Iterator, Optional, Union +from typing import Any, Callable, Iterator, Optional, Union from ape.api.accounts import AccountAPI, AccountContainerAPI, TransactionAPI from ape.types import AddressType, MessageSignature, SignableMessage, TransactionSignature @@ -23,13 +23,24 @@ def accounts(self) -> Iterator[AccountAPI]: yield FrameAccount() +def wrap_sign(fn: Callable) -> Optional[bytes]: + try: + return fn() + + except ValueError as err: + if not err.args[0]["message"] == "User declined transaction": + raise # The ValueError + + return None + + class FrameAccount(AccountAPI): @property def web3(self) -> Web3: headers = { "Origin": "Ape", "User-Agent": "ape-frame/0.1.0", - "Content-Type": "application/json" + "Content-Type": "application/json", } return Web3(HTTPProvider("http://127.0.0.1:1248", request_kwargs={"headers": headers})) @@ -41,53 +52,31 @@ def alias(self) -> str: def address(self) -> AddressType: return self.web3.eth.accounts[0] - def sign_message(self, msg: Any) -> Optional[MessageSignature]: + def sign_message(self, msg: Any, **signer_options) -> Optional[MessageSignature]: raw_signature = None if isinstance(msg, str): - # encode string messages as Ethereum Signed Messages - try: - raw_signature = self.web3.eth.sign(self.address, text=msg) - except ValueError as e: - if not e.args[0]["message"] == "User declined transaction": - raise - return None - if isinstance(msg, int): - try: - raw_signature = self.web3.eth.sign(self.address, hexstr=HexBytes(msg).hex()) - except ValueError as e: - if not e.args[0]["message"] == "User declined transaction": - raise - return None - if isinstance(msg, bytes): - try: - raw_signature = self.web3.eth.sign(self.address, hexstr=msg.hex()) - except ValueError as e: - if not e.args[0]["message"] == "User declined transaction": - raise - return None - - if isinstance(msg, SignableMessage): - try: - raw_signature = self.web3.eth.sign(self.address, data=msg.body) - except ValueError as e: - if not e.args[0]["message"] == "User declined transaction": - raise - return None - - if isinstance(msg, EIP712Message): - try: - raw_signature = self.web3.eth.sign_typed_data(self.address, msg._body_) - except ValueError as e: - if not e.args[0]["message"] == "User declined transaction": - raise - return None + raw_signature = wrap_sign(lambda: self.web3.eth.sign(self.address, text=msg)) + elif isinstance(msg, int): + raw_signature = wrap_sign( + lambda: self.web3.eth.sign(self.address, hexstr=HexBytes(msg).hex()) + ) + elif isinstance(msg, bytes): + raw_signature = wrap_sign(lambda: self.web3.eth.sign(self.address, hexstr=msg.hex())) + elif isinstance(msg, SignableMessage): + raw_signature = wrap_sign(lambda: self.web3.eth.sign(self.address, data=msg.body)) + elif isinstance(msg, EIP712Message): + raw_signature = wrap_sign( + lambda: self.web3.eth.sign_typed_data( + self.address, msg._body_ # type: ignore[arg-type] + ) + ) return ( - MessageSignature( # type: ignore[call-arg] - v=raw_signature[64], - r=raw_signature[0:32], - s=raw_signature[32:64], + MessageSignature( + v=int(raw_signature[64]), + r=HexBytes(raw_signature[0:32]), + s=HexBytes(raw_signature[32:64]), ) if raw_signature else None @@ -95,27 +84,25 @@ def sign_message(self, msg: Any) -> Optional[MessageSignature]: def sign_transaction(self, txn: TransactionAPI, **signer_options) -> Optional[TransactionAPI]: # TODO: need a way to deserialized from raw bytes - # raw_signed_txn_bytes = self.web3.eth.sign_transaction(txn.dict()) - txn_data = txn.dict(exclude={"sender"}) + txn_data = txn.model_dump(by_alias=True, mode="json", exclude={"sender"}) unsigned_txn = serializable_unsigned_transaction_from_dict(txn_data) - try: - raw_signature = self.web3.eth.sign(self.address, hexstr=keccak(unsigned_txn).hex()) - except ValueError as e: - if not e.args[0]["message"] == "User declined transaction": - raise - - return None - - txn.signature = TransactionSignature( # type: ignore[call-arg] - v=raw_signature[64], # type: ignore[arg-type] - r=raw_signature[0:32], # type: ignore[arg-type] - s=raw_signature[32:64], # type: ignore[arg-type] + raw_signature = wrap_sign( + lambda: self.web3.eth.sign(self.address, hexstr=keccak(unsigned_txn).hex()) + ) + txn.signature = ( + TransactionSignature( + v=int(raw_signature[64]), + r=HexBytes(raw_signature[0:32]), + s=HexBytes(raw_signature[32:64]), + ) + if raw_signature + else None ) return txn def check_signature( self, - data: Union[SignableMessage, TransactionAPI, EIP712Message, str], + data: Union[SignableMessage, TransactionAPI, str, EIP712Message, int], signature: Optional[MessageSignature] = None, ) -> bool: if isinstance(data, str): @@ -124,4 +111,5 @@ def check_signature( data = encode_defunct(primitive=data) if isinstance(data, EIP712Message): data = data.signable_message + return super().check_signature(data, signature) diff --git a/ape_frame/providers.py b/ape_frame/providers.py index 32e0bc1..98f78e6 100644 --- a/ape_frame/providers.py +++ b/ape_frame/providers.py @@ -1,8 +1,8 @@ from typing import Any from ape.api import UpstreamProvider -from ape_ethereum.provider import Web3Provider from ape.exceptions import ProviderError +from ape_ethereum.provider import Web3Provider from eth_utils import to_hex from requests import HTTPError # type: ignore[import] from web3 import HTTPProvider, Web3 @@ -28,7 +28,7 @@ def connect(self): headers = { "Origin": "Ape", "User-Agent": "ape-frame/0.1.0", - "Content-Type": "application/json" + "Content-Type": "application/json", } self._web3 = Web3(HTTPProvider(self.uri, request_kwargs={"headers": headers})) diff --git a/pyproject.toml b/pyproject.toml index 354c306..029d103 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ write_to = "ape_frame/version.py" [tool.black] line-length = 100 -target-version = ['py38', 'py39', 'py310'] +target-version = ['py38', 'py39', 'py310', 'py311'] include = '\.pyi?$' [tool.pytest.ini_options] diff --git a/setup.py b/setup.py index b67b76f..02fdf02 100644 --- a/setup.py +++ b/setup.py @@ -10,16 +10,17 @@ "hypothesis>=6.2.0,<7.0", # Strategy-based fuzzer ], "lint": [ - "black>=23.11.0,<24", # Auto-formatter and linter - "mypy>=1.6.1,<2", # Static type analyzer + "black>=24.2.0,<25", # Auto-formatter and linter + "mypy>=1.8.0,<2", # Static type analyzer "types-requests", # Needed due to mypy typeshed "types-setuptools", # Needed due to mypy typeshed "types-PyYAML", # Needed due to mypy typeshed - "flake8>=6.1.0,<7", # Style linter + "flake8>=7.0.0,<8", # Style linter "isort>=5.10.1,<6", # Import sorting linter "mdformat>=0.7.17", # Auto-formatter for markdown "mdformat-gfm>=0.3.5", # Needed for formatting GitHub-flavored markdown "mdformat-frontmatter>=0.4.1", # Needed for frontmatters-style headers in issue templates + "mdformat-pyproject>=0.0.1", # Allows configuring in pyproject.toml ], "release": [ # `release` GitHub Action job uses this "setuptools", # Installation tool @@ -59,7 +60,7 @@ url="https://github.com/ApeWorX/ape-frame", include_package_data=True, install_requires=[ - "eth-ape>=0.6.0,<0.7.0", + "eth-ape>=0.7.10,<0.8", ], python_requires=">=3.8,<4", extras_require=extras_require,