Skip to content

Commit

Permalink
feat!: upgrade to Ape 0.8 (#42)
Browse files Browse the repository at this point in the history
Co-authored-by: Dalena <[email protected]>
  • Loading branch information
antazoey and dtdang authored Jun 1, 2024
1 parent d190874 commit b491134
Show file tree
Hide file tree
Showing 17 changed files with 68 additions and 58 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/draft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ on:
jobs:
update-draft:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# Drafts your next Release notes as Pull Requests are merged into "main"
- uses: release-drafter/release-drafter@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,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"]
safe-version: ["1.3.0"]

env:
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repos:

- repo: https://github.com/psf/black
rev: 24.4.2

hooks:
- id: black
name: black
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Account plugin for the [Safe](https://safe.global//) Multisig wallet (previously

## 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
4 changes: 0 additions & 4 deletions ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ dependencies:
import_remapping:
- "@openzeppelin/contracts=openzeppelin/v3.4.0"

solidity:
import_remapping:
- "@gnosis=safe-contracts/v1.3.0"

foundry:
fork:
ethereum:
Expand Down
3 changes: 2 additions & 1 deletion ape_safe/_cli/click_ext.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import NoReturn, Optional, Sequence, Union, cast
from collections.abc import Sequence
from typing import NoReturn, Optional, Union, cast

import click
from ape.api import AccountAPI
Expand Down
7 changes: 4 additions & 3 deletions ape_safe/_cli/pending.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Dict, List, Optional, Sequence, Union, cast
from collections.abc import Sequence
from typing import Optional, Union, cast

import click
import rich
Expand Down Expand Up @@ -46,7 +47,7 @@ def _list(cli_ctx, safe, verbose) -> None:
rich.print("There are no pending transactions.")
return

txns_by_nonce: Dict[int, List[UnexecutedTxData]] = {}
txns_by_nonce: dict[int, list[UnexecutedTxData]] = {}
for txn in txns:
if txn.nonce in txns_by_nonce:
txns_by_nonce[txn.nonce].append(txn)
Expand Down Expand Up @@ -259,7 +260,7 @@ def execute(cli_ctx, safe, txn_ids, submitter, nonce):

def _execute(safe: SafeAccount, txn: UnexecutedTxData, submitter: AccountAPI, **tx_kwargs):
safe_tx = safe.create_safe_tx(**txn.model_dump(mode="json", by_alias=True))
signatures: Dict[AddressType, MessageSignature] = {
signatures: dict[AddressType, MessageSignature] = {
c.owner: MessageSignature.from_rsv(c.signature) for c in txn.confirmations
}
exc_tx = safe.create_execute_transaction(safe_tx, signatures, **tx_kwargs)
Expand Down
19 changes: 10 additions & 9 deletions ape_safe/accounts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
import os
from collections.abc import Iterable, Iterator, Mapping
from pathlib import Path
from typing import Any, Dict, Iterable, Iterator, List, Mapping, Optional, Tuple, Type, Union, cast
from typing import Any, Dict, Optional, Union, cast

from ape.api import AccountAPI, AccountContainerAPI, ReceiptAPI, TransactionAPI
from ape.api.address import BaseAddress
Expand Down Expand Up @@ -192,7 +193,7 @@ def get_signatures(
return signatures


def _safe_tx_exec_args(safe_tx: SafeTx) -> List:
def _safe_tx_exec_args(safe_tx: SafeTx) -> list:
return list(safe_tx._body_["message"].values())


Expand Down Expand Up @@ -283,7 +284,7 @@ def version(self) -> str:
return self.contract.VERSION()

@property
def signers(self) -> List[AddressType]:
def signers(self) -> list[AddressType]:
# NOTE: Signers are in order because of `Set`
try:
return self.client.safe_details.owners
Expand Down Expand Up @@ -329,7 +330,7 @@ def sign_message(self, msg: Any, **signer_options) -> Optional[MessageSignature]
raise NotImplementedError("Safe accounts do not support message signing!")

@property
def safe_tx_def(self) -> Type[SafeTx]:
def safe_tx_def(self) -> type[SafeTx]:
return create_safe_tx_def(
version=self.version,
contract_address=self.address,
Expand Down Expand Up @@ -372,14 +373,14 @@ def create_safe_tx(self, txn: Optional[TransactionAPI] = None, **safe_tx_kwargs)
}
return self.safe_tx_def(**safe_tx)

def pending_transactions(self) -> Iterator[Tuple[SafeTx, List[SafeTxConfirmation]]]:
def pending_transactions(self) -> Iterator[tuple[SafeTx, list[SafeTxConfirmation]]]:
for executed_tx in self.client.get_transactions(confirmed=False):
yield self.create_safe_tx(
**executed_tx.model_dump(mode="json", by_alias=True)
), executed_tx.confirmations

@property
def local_signers(self) -> List[AccountAPI]:
def local_signers(self) -> list[AccountAPI]:
# NOTE: Is not ordered by signing order
# TODO: Skip per user config
# TODO: Order per user config
Expand Down Expand Up @@ -605,7 +606,7 @@ def sign_transaction(
txn: TransactionAPI,
submit: bool = True,
submitter: Union[AccountAPI, AddressType, str, None] = None,
skip: Optional[List[Union[AccountAPI, AddressType, str]]] = None,
skip: Optional[list[Union[AccountAPI, AddressType, str]]] = None,
signatures_required: Optional[int] = None, # NOTE: Required if increasing threshold
**signer_options,
) -> Optional[TransactionAPI]:
Expand Down Expand Up @@ -731,7 +732,7 @@ def skip_signer(signer: AccountAPI):
return None

def add_signatures(
self, safe_tx: SafeTx, confirmations: Optional[List[SafeTxConfirmation]] = None
self, safe_tx: SafeTx, confirmations: Optional[list[SafeTxConfirmation]] = None
) -> Dict[AddressType, MessageSignature]:
confirmations = confirmations or []
if not self.local_signers:
Expand All @@ -753,7 +754,7 @@ def select_signer(self, for_: str = "submitter") -> AccountAPI:
return select_account(prompt_message=f"Select a {for_}", key=self.local_signers)


def _get_safe_tx_id(safe_tx: SafeTx, confirmations: List[SafeTxConfirmation]) -> SafeTxID:
def _get_safe_tx_id(safe_tx: SafeTx, confirmations: list[SafeTxConfirmation]) -> SafeTxID:
if tx_hash_result := next((c.transaction_hash for c in confirmations), None):
return cast(SafeTxID, tx_hash_result)

Expand Down
15 changes: 8 additions & 7 deletions ape_safe/client/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod
from collections.abc import Iterator
from functools import cached_property
from typing import Dict, Iterator, Optional, Set, Union
from typing import Optional, Union

import certifi
import requests
Expand Down Expand Up @@ -47,14 +48,14 @@ def get_confirmations(self, safe_tx_hash: SafeTxID) -> Iterator[SafeTxConfirmati

@abstractmethod
def post_transaction(
self, safe_tx: SafeTx, signatures: Dict[AddressType, MessageSignature], **kwargs
self, safe_tx: SafeTx, signatures: dict[AddressType, MessageSignature], **kwargs
): ...

@abstractmethod
def post_signatures(
self,
safe_tx_or_hash: Union[SafeTx, SafeTxID],
signatures: Dict[AddressType, MessageSignature],
signatures: dict[AddressType, MessageSignature],
): ...

@abstractmethod
Expand All @@ -69,8 +70,8 @@ def get_transactions(
confirmed: Optional[bool] = None,
starting_nonce: int = 0,
ending_nonce: Optional[int] = None,
filter_by_ids: Optional[Set[SafeTxID]] = None,
filter_by_missing_signers: Optional[Set[AddressType]] = None,
filter_by_ids: Optional[set[SafeTxID]] = None,
filter_by_missing_signers: Optional[set[AddressType]] = None,
) -> Iterator[SafeApiTxData]:
"""
confirmed: Confirmed if True, not confirmed if False, both if None
Expand Down Expand Up @@ -127,14 +128,14 @@ def session(self) -> requests.Session:
def _get(self, url: str) -> Response:
return self._request("GET", url)

def _post(self, url: str, json: Optional[Dict] = None, **kwargs) -> Response:
def _post(self, url: str, json: Optional[dict] = None, **kwargs) -> Response:
return self._request("POST", url, json=json, **kwargs)

@cached_property
def _http(self):
return urllib3.PoolManager(ca_certs=certifi.where())

def _request(self, method: str, url: str, json: Optional[Dict] = None, **kwargs) -> Response:
def _request(self, method: str, url: str, json: Optional[dict] = None, **kwargs) -> Response:
# **WARNING**: The trailing slash in the URL is CRITICAL!
# If you remove it, things will not work as expected.

Expand Down
13 changes: 7 additions & 6 deletions ape_safe/client/mock.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Iterator
from datetime import datetime, timezone
from typing import Dict, Iterator, List, Optional, Union, cast
from typing import Optional, Union, cast

from ape.contracts import ContractInstance
from ape.types import AddressType, MessageSignature
Expand All @@ -23,8 +24,8 @@
class MockSafeClient(BaseSafeClient, ManagerAccessMixin):
def __init__(self, contract: ContractInstance):
self.contract = contract
self.transactions: Dict[SafeTxID, SafeApiTxData] = {}
self.transactions_by_nonce: Dict[int, List[SafeTxID]] = {}
self.transactions: dict[SafeTxID, SafeApiTxData] = {}
self.transactions_by_nonce: dict[int, list[SafeTxID]] = {}

@property
def safe_details(self) -> SafeDetails:
Expand Down Expand Up @@ -52,7 +53,7 @@ def guard(self) -> AddressType:
)

@property
def modules(self) -> List[AddressType]:
def modules(self) -> list[AddressType]:
return self.contract.getModules() if "getModules" in self.contract._view_methods_ else []

def get_next_nonce(self) -> int:
Expand All @@ -72,7 +73,7 @@ def get_confirmations(self, safe_tx_hash: SafeTxID) -> Iterator[SafeTxConfirmati
yield from safe_tx_data.confirmations

def post_transaction(
self, safe_tx: SafeTx, signatures: Dict[AddressType, MessageSignature], **kwargs
self, safe_tx: SafeTx, signatures: dict[AddressType, MessageSignature], **kwargs
):
safe_tx_data = UnexecutedTxData.from_safe_tx(safe_tx, self.safe_details.threshold)
safe_tx_data.confirmations.extend(
Expand All @@ -94,7 +95,7 @@ def post_transaction(
def post_signatures(
self,
safe_tx_or_hash: Union[SafeTx, SafeTxID],
signatures: Dict[AddressType, MessageSignature],
signatures: dict[AddressType, MessageSignature],
):
for signer, signature in signatures.items():
safe_tx_id = (
Expand Down
10 changes: 5 additions & 5 deletions ape_safe/client/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from datetime import datetime, timezone
from enum import Enum
from typing import Dict, List, NewType, Optional, Union, cast
from typing import NewType, Optional, Union, cast

from ape.types import AddressType, HexBytes
from eip712.common import SafeTxV1, SafeTxV2
Expand All @@ -18,9 +18,9 @@ class SafeDetails(BaseModel):
address: AddressType
nonce: int
threshold: int
owners: List[AddressType]
owners: list[AddressType]
master_copy: AddressType = Field(alias="masterCopy")
modules: List[AddressType]
modules: list[AddressType]
fallback_handler: AddressType = Field(alias="fallbackHandler")
guard: AddressType
version: str
Expand Down Expand Up @@ -61,7 +61,7 @@ class UnexecutedTxData(BaseModel):
modified: datetime
safe_tx_hash: SafeTxID = Field(alias="safeTxHash")
confirmations_required: int = Field(alias="confirmationsRequired")
confirmations: List[SafeTxConfirmation] = []
confirmations: list[SafeTxConfirmation] = []
trusted: bool = True
signatures: Optional[HexBytes] = None

Expand All @@ -77,7 +77,7 @@ def from_safe_tx(cls, safe_tx: SafeTx, confirmations_required: int) -> "Unexecut
)

@property
def base_tx_dict(self) -> Dict:
def base_tx_dict(self) -> dict:
return {
"to": self.to,
"value": self.value,
Expand Down
4 changes: 2 additions & 2 deletions ape_safe/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from contextlib import ContextDecorator
from typing import Optional, Type
from typing import Optional

from ape.exceptions import AccountsError, ApeException, ContractLogicError, SignatureError
from ape.types import AddressType
Expand Down Expand Up @@ -81,7 +81,7 @@ class handle_safe_logic_error(ContextDecorator):
def __enter__(self):
pass

def __exit__(self, exc_type: Type[BaseException], exc: BaseException, tb):
def __exit__(self, exc_type: type[BaseException], exc: BaseException, tb):
if (
isinstance(exc, ContractLogicError) # NOTE: Just for mypy
and exc_type == ContractLogicError
Expand Down
4 changes: 1 addition & 3 deletions ape_safe/multisend.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

from ape.api import ReceiptAPI, TransactionAPI
from ape.contracts.base import ContractInstance, ContractTransactionHandler
from ape.types import ContractType, HexBytes
Expand Down Expand Up @@ -116,7 +114,7 @@ def __init__(self) -> None:
"""
Initialize a new Multicall session object. By default, there are no calls to make.
"""
self.calls: List[dict] = []
self.calls: list[dict] = []

@classmethod
def inject(cls):
Expand Down
5 changes: 3 additions & 2 deletions ape_safe/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import TYPE_CHECKING, List, Mapping, cast
from collections.abc import Mapping
from typing import TYPE_CHECKING, cast

from ape.types import AddressType, MessageSignature
from eip712.messages import calculate_hash
Expand All @@ -8,7 +9,7 @@
from ape_safe.client.types import SafeTxID


def order_by_signer(signatures: Mapping[AddressType, MessageSignature]) -> List[MessageSignature]:
def order_by_signer(signatures: Mapping[AddressType, MessageSignature]) -> list[MessageSignature]:
# NOTE: Must order signatures in ascending order of signer address (converted to int)
return list(signatures[signer] for signer in sorted(signatures, key=lambda a: to_int(hexstr=a)))

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_safe/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 @@ -59,7 +58,7 @@
url="https://github.com/ApeWorX/ape-safe",
include_package_data=True,
install_requires=[
"eth-ape>=0.7.7,<0.8",
"eth-ape>=0.8.1,<0.9",
"requests>=2.31.0,<3",
"eip712", # Use same version as eth-ape
"click", # Use same version as eth-ape
Expand All @@ -71,7 +70,7 @@
"ape_safe=ape_safe._cli:cli",
],
},
python_requires=">=3.8,<4",
python_requires=">=3.9,<4",
extras_require=extras_require,
py_modules=["ape_safe"],
license="Apache-2.0",
Expand All @@ -87,7 +86,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
Loading

0 comments on commit b491134

Please sign in to comment.