Skip to content

Commit

Permalink
feat!: update to 0.8 (#133)
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 f14995b commit 094b76d
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 203 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 @@ -15,7 +15,7 @@ The following blockchain explorers are supported in this plugin:

## 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
7 changes: 7 additions & 0 deletions ape-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: ape-etherscan-tests

contracts_folder: tests/contracts

dependencies:
- name: bar
local: ./tests/dependency
29 changes: 15 additions & 14 deletions ape_etherscan/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import os
import random
import time
from collections.abc import Iterator
from io import StringIO
from typing import Dict, Iterator, List, Optional
from typing import Optional

from ape.api import PluginConfig
from ape.logging import logger
Expand Down Expand Up @@ -201,7 +202,7 @@ def base_uri(self) -> str:
return self._instance.api_uri

@property
def base_params(self) -> Dict:
def base_params(self) -> dict:
return {"module": self._module_name}

@property
Expand All @@ -225,8 +226,8 @@ def _clean_uri(self) -> str:

def _get(
self,
params: Optional[Dict] = None,
headers: Optional[Dict[str, str]] = None,
params: Optional[dict] = None,
headers: Optional[dict[str, str]] = None,
raise_on_exceptions: bool = True,
) -> EtherscanResponse:
params = self.__authorize(params)
Expand All @@ -248,7 +249,7 @@ def _get(
)

def _post(
self, json_dict: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None
self, json_dict: Optional[dict] = None, headers: Optional[dict[str, str]] = None
) -> EtherscanResponse:
data = self.__authorize(json_dict)
return self._request("POST", data=data, headers=headers)
Expand All @@ -257,9 +258,9 @@ def _request(
self,
method: str,
raise_on_exceptions: bool = True,
headers: Optional[Dict] = None,
params: Optional[Dict] = None,
data: Optional[Dict] = None,
headers: Optional[dict] = None,
params: Optional[dict] = None,
data: Optional[dict] = None,
) -> EtherscanResponse:
headers = headers or self.DEFAULT_HEADERS
for i in range(self._retries):
Expand Down Expand Up @@ -288,7 +289,7 @@ def _request(

return EtherscanResponse(response, self._instance.ecosystem_name, raise_on_exceptions)

def __authorize(self, params_or_data: Optional[Dict] = None) -> Optional[Dict]:
def __authorize(self, params_or_data: Optional[dict] = None) -> Optional[dict]:
env_var_key = API_KEY_ENV_KEY_MAP.get(self._instance.ecosystem_name)
if not env_var_key:
return params_or_data
Expand Down Expand Up @@ -332,15 +333,15 @@ def get_source_code(self) -> SourceCodeResponse:

def verify_source_code(
self,
standard_json_output: Dict,
standard_json_output: dict,
compiler_version: str,
contract_name: Optional[str] = None,
optimization_used: bool = False,
optimization_runs: Optional[int] = 200,
constructor_arguments: Optional[str] = None,
evm_version: Optional[str] = None,
license_type: Optional[int] = None,
libraries: Optional[Dict[str, str]] = None,
libraries: Optional[dict[str, str]] = None,
) -> str:
libraries = libraries or {}
if len(libraries) > 10:
Expand Down Expand Up @@ -384,7 +385,7 @@ def check_verify_status(self, guid: str) -> str:
response = self._get(params=json_dict, raise_on_exceptions=False)
return str(response.value)

def get_creation_data(self) -> List[ContractCreationResponse]:
def get_creation_data(self) -> list[ContractCreationResponse]:
params = {
**self.base_params,
"action": "getcontractcreation",
Expand All @@ -409,7 +410,7 @@ def get_all_normal_transactions(
end_block: Optional[int] = None,
offset: int = 100,
sort: str = "asc",
) -> Iterator[Dict]:
) -> Iterator[dict]:
page_num = 1
last_page_results = offset # Start at offset to trigger iteration
while last_page_results == offset:
Expand All @@ -430,7 +431,7 @@ def _get_page_of_normal_transactions(
end_block: Optional[int] = None,
offset: int = 100,
sort: str = "asc",
) -> List[Dict]:
) -> list[dict]:
params = {
**self.base_params,
"action": "txlist",
Expand Down
18 changes: 14 additions & 4 deletions ape_etherscan/dependency.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from pathlib import Path

from ape.api.projects import DependencyAPI
from ape.exceptions import ProjectError
from ape.types import AddressType
from ethpm_types import PackageManifest
from hexbytes import HexBytes
from pydantic import AnyUrl, HttpUrl, field_validator
from pydantic import field_validator

from .explorer import Etherscan

Expand All @@ -27,8 +29,12 @@ def address(self) -> AddressType:
return self.network_manager.ethereum.decode_address(self.etherscan)

@property
def uri(self) -> AnyUrl:
return HttpUrl(f"{self.explorer.get_address_url(self.address)}#code")
def package_id(self) -> str:
return self.address

@property
def uri(self) -> str:
return f"{self.explorer.get_address_url(self.address)}#code"

@property
def explorer(self) -> Etherscan:
Expand All @@ -43,7 +49,11 @@ def explorer(self) -> Etherscan:
# Assume Ethereum
return self.network_manager.ethereum.mainnet.explorer

def extract_manifest(self, use_cache: bool = True) -> PackageManifest:
def fetch(self, destination: Path):
manifest = self._get_manifest()
manifest.unpack_sources(destination)

def _get_manifest(self) -> PackageManifest:
ecosystem = self.network_manager.get_ecosystem(self.ecosystem)
network = ecosystem.get_network(self.network)

Expand Down
6 changes: 5 additions & 1 deletion ape_etherscan/explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ape.api import ExplorerAPI, PluginConfig
from ape.contracts import ContractInstance
from ape.exceptions import ProviderNotConnectedError
from ape.managers.project import ProjectManager
from ape.types import AddressType, ContractType
from ethpm_types import Compiler, PackageManifest
from ethpm_types.source import Source
Expand Down Expand Up @@ -120,5 +121,8 @@ def get_contract_type(self, address: AddressType) -> Optional[ContractType]:
return contract_type

def publish_contract(self, address: AddressType):
verifier = SourceVerifier(address, self._client_factory)
return self._publish_contract(address)

def _publish_contract(self, address: AddressType, project: Optional["ProjectManager"] = None):
verifier = SourceVerifier(address, self._client_factory, project=project)
return verifier.attempt_verification()
3 changes: 2 additions & 1 deletion ape_etherscan/query.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Iterator, Optional
from collections.abc import Iterator
from typing import Optional

from ape.api import PluginConfig, QueryAPI, QueryType, ReceiptAPI
from ape.api.query import AccountTransactionQuery, ContractCreationQuery
Expand Down
6 changes: 3 additions & 3 deletions ape_etherscan/types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import re
from dataclasses import dataclass
from typing import Dict, List, Union
from typing import Union

from ape.utils import cached_property
from ethpm_types import BaseModel
Expand All @@ -21,7 +21,7 @@ class EtherscanInstance:


class SourceCodeResponse(BaseModel):
abi: List = Field([], alias="ABI")
abi: list = Field([], alias="ABI")
name: str = Field("unknown", alias="ContractName")
source_code: str = Field("", alias="SourceCode")
compiler_version: str = Field("", alias="CompilerVersion")
Expand Down Expand Up @@ -67,7 +67,7 @@ class ContractCreationResponse:
txHash: str


ResponseValue = Union[List, Dict, str]
ResponseValue = Union[list, dict, str]


class EtherscanResponse:
Expand Down
53 changes: 25 additions & 28 deletions ape_etherscan/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import time
from enum import Enum
from pathlib import Path
from typing import Dict, Optional
from typing import Optional

from ape.api import CompilerAPI
from ape.contracts import ContractInstance
from ape.logging import LogLevel, logger
from ape.managers.project import ProjectManager
from ape.types import AddressType
from ape.utils import ManagerAccessMixin, cached_property
from ethpm_types import Compiler, ContractType
Expand Down Expand Up @@ -169,9 +170,15 @@ def from_spdx_id(cls, spdx_id: str) -> "LicenseType":


class SourceVerifier(ManagerAccessMixin):
def __init__(self, address: AddressType, client_factory: ClientFactory):
def __init__(
self,
address: AddressType,
client_factory: ClientFactory,
project: Optional[ProjectManager] = None,
):
self.address = address
self.client_factory = client_factory
self.project = project or self.local_project

@cached_property
def account_client(self) -> AccountClient:
Expand All @@ -193,13 +200,9 @@ def contract_type(self) -> ContractType:
def contract_name(self) -> str:
return self.contract.contract_type.name or ""

@property
def _base_path(self) -> Path:
return self.project_manager.contracts_folder

@property
def source_path(self) -> Path:
return self._base_path / (self.contract_type.source_id or "")
return self.project.path / (self.contract_type.source_id or "")

@property
def ext(self) -> str:
Expand Down Expand Up @@ -242,8 +245,7 @@ def license_code(self) -> LicenseType:
"""
The license type used in the code.
"""

spdx_id = self.source_path.read_text().split("\n")[0]
spdx_id = self.source_path.read_text().splitlines()[0]
return LicenseType.from_spdx_id(spdx_id)

@property
Expand All @@ -262,17 +264,17 @@ def compiler_name(self) -> str:
@property
def compiler(self) -> Compiler:
# Check the cached manifest for the compiler artifacts.
if manifest := self.project_manager.local_project.cached_manifest:
if manifest := self.local_project.manifest:
if compiler := manifest.get_contract_compiler(self.contract_name):
return compiler

# Look in the publishable manifest, as Ape includes these there.
manifest = self.project_manager.extract_manifest()
manifest = self.local_project.extract_manifest()
if compiler := manifest.get_contract_compiler(self.contract_name):
return compiler

# Build a default one and hope for the best.
return Compiler(name=self.compiler_name, contractType=[self.contract_name], version=None)
return Compiler(name=self.compiler_name, contractType=[self.contract_name], version="")

def attempt_verification(self):
"""
Expand Down Expand Up @@ -314,8 +316,7 @@ def attempt_verification(self):
optimized = optimizer.get("enabled", False)
runs = optimizer.get("runs", DEFAULT_OPTIMIZATION_RUNS)
source_id = self.contract_type.source_id
base_folder = self.project_manager.contracts_folder
standard_input_json = self._get_standard_input_json(source_id, base_folder, **settings)
standard_input_json = self._get_standard_input_json(source_id, **settings)
evm_version = settings.get("evmVersion")
license_code = self.license_code
license_code_value = license_code.value if license_code else None
Expand Down Expand Up @@ -355,39 +356,35 @@ def attempt_verification(self):

self._wait_for_verification(guid)

def _get_new_settings(self, version: str) -> Dict:
def _get_new_settings(self, version: str) -> dict:
logger.warning(
"Settings missing from cached manifest. " "Attempting to re-calculate find settings."
)

# Attempt to re-calculate settings.
compiler_plugin = self.compiler_manager.registered_compilers[self.ext]
all_settings = compiler_plugin.get_compiler_settings(
[self.source_path], base_path=self._base_path
[self.source_path], project=self.project
)

# Hack to allow any Version object work.
return {str(v): s for v, s in all_settings.items() if str(v) == version}[version]

def _get_standard_input_json(
self, source_id: str, base_folder: Optional[Path] = None, **settings
) -> Dict:
base_dir = base_folder or self.project_manager.contracts_folder
source_path = base_dir / source_id
def _get_standard_input_json(self, source_id: str, **settings) -> dict:
source_path = self.local_project.sources.lookup(source_id)
compiler = self.compiler_manager.registered_compilers[source_path.suffix]
sources = {self.source_path.name: {"content": source_path.read_text()}}
sources = {source_id: {"content": source_path.read_text()}}

def build_map(_source_id: str):
_source_path = base_dir / _source_id
_source_path = self.local_project.sources.lookup(_source_id)
source_imports = compiler.get_imports([_source_path]).get(_source_id, [])
for imported_source_id in source_imports:
sources[imported_source_id] = {
"content": (base_dir / imported_source_id).read_text()
}
build_map(imported_source_id)
if imp_path := self.local_project.sources.lookup(imported_source_id):
sources[imported_source_id] = {"content": imp_path.read_text()}
build_map(imported_source_id)

def flatten_source(_source_id: str) -> str:
_source_path = base_dir / _source_id
_source_path = self.local_project.sources.lookup(_source_id)
flattened_source = str(compiler.flatten_contract(_source_path))
return flattened_source

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_etherscan/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
Loading

0 comments on commit 094b76d

Please sign in to comment.