Skip to content

Commit

Permalink
fix: fix
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey committed Oct 19, 2023
1 parent 9ad463b commit 412dc6b
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 53 deletions.
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
rev: v4.5.0
hooks:
- id: check-yaml

Expand All @@ -10,24 +10,24 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 23.3.0
rev: 23.10.0
hooks:
- id: black
name: black

- repo: https://github.com/pycqa/flake8
rev: 6.0.0
rev: 6.1.0
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
rev: v1.6.1
hooks:
- id: mypy
additional_dependencies: [types-PyYAML, types-requests, types-setuptools, pydantic]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.14
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter]
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ A pull request represents the start of a discussion, and doesn't necessarily nee
If you are opening a work-in-progress pull request to verify that it passes CI tests, please consider
[marking it as a draft](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests).

Join the Ethereum Python [Discord](https://discord.gg/PcEJ54yX) if you have any questions.
Join the ApeWorX [Discord](https://discord.gg/apeworx) if you have any questions.
24 changes: 17 additions & 7 deletions ape_etherscan/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ape.logging import logger
from ape.utils import USER_AGENT, ManagerAccessMixin
from requests import Session
from yarl import URL

from ape_etherscan.exceptions import (
UnhandledResultError,
Expand Down Expand Up @@ -176,6 +177,11 @@ def _retries(self) -> int:
def _min_time_between_calls(self) -> float:
return 1 / self._rate_limit # seconds / calls per second

@property
def _clean_uri(self) -> str:
url = URL(self.base_uri).with_user(None).with_password(None)
return f"{url.with_path('')}/[hidden]" if url.path else f"{url}"

def _get(
self,
params: Optional[Dict] = None,
Expand Down Expand Up @@ -216,6 +222,7 @@ def _request(
) -> EtherscanResponse:
headers = headers or self.DEFAULT_HEADERS
for i in range(self._retries):
logger.debug(f"Request sent to {self._clean_uri}.")
response = self.session.request(
method.upper(),
self.base_uri,
Expand All @@ -225,14 +232,16 @@ def _request(
timeout=1024,
)
if response.status_code == 429:
time.sleep(2**i)
time_to_sleep = 2**i
logger.debug(f"Request was throttled. Retrying in {time_to_sleep} seconds.")
time.sleep(time_to_sleep)
continue

# Recieved a real response unrelated to rate limiting.
if raise_on_exceptions:
response.raise_for_status()
elif not 200 <= response.status_code < 300:
logger.error(response.text)
logger.error(f"Response was not successful: {response.text}")

break

Expand Down Expand Up @@ -264,9 +273,8 @@ def get_source_code(self) -> SourceCodeResponse:
"address": self._address,
}
result = self._get(params=params)
result_list = result.value or []

if not result_list:
if not (result_list := result.value or []):
return SourceCodeResponse()

elif len(result_list) > 1:
Expand Down Expand Up @@ -341,9 +349,11 @@ def get_creation_data(self) -> List[ContractCreationResponse]:
"contractaddresses": [self._address],
}
result = self._get(params=params)
assert isinstance(result.value, list)
assert all(isinstance(val, dict) for val in result.value)
return [ContractCreationResponse(**item) for item in result.value]
items = result.value or []
if not isinstance(items, list):
raise ValueError("Expecting list.")

return [ContractCreationResponse(**item) for item in items]


class AccountClient(_APIClient):
Expand Down
10 changes: 5 additions & 5 deletions ape_etherscan/explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ape.api import ExplorerAPI
from ape.contracts import ContractInstance
from ape.exceptions import ProviderNotConnectedError
from ape.logging import logger
from ape.types import AddressType, ContractType

from ape_etherscan.client import ClientFactory, get_etherscan_uri
Expand Down Expand Up @@ -35,20 +36,19 @@ def get_contract_type(self, address: AddressType) -> Optional[ContractType]:

client = self._client_factory.get_contract_client(address)
source_code = client.get_source_code()
abi_string = source_code.abi
if not abi_string:
if not (abi_string := source_code.abi):
return None

try:
abi = json.loads(abi_string)
except JSONDecodeError:
except JSONDecodeError as err:
logger.error(f"Error with contract ABI: {err}")
return None

contract_type = ContractType(abi=abi, contractName=source_code.name)
if source_code.name == "Vyper_contract" and "symbol" in contract_type.view_methods:
try:
checksummed_address = self.provider.network.ecosystem.decode_address(address)
contract = ContractInstance(checksummed_address, contract_type)
contract = ContractInstance(address, contract_type)
contract_type.name = contract.symbol() or contract_type.name
except ProviderNotConnectedError:
pass
Expand Down
7 changes: 4 additions & 3 deletions ape_etherscan/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ def get_account_transactions(self, query: AccountTransactionQuery) -> Iterator[R
def get_contract_creation_receipt(self, query: ContractCreationQuery) -> Iterator[ReceiptAPI]:
client = self._client_factory.get_contract_client(query.contract)
creation_data = client.get_creation_data()

if len(creation_data) != 1:
raise
if len(creation_data) == 0:
return
elif len(creation_data) != 1:
raise ValueError("Expecting single creation data.")

yield self.chain_manager.get_receipt(creation_data[0].txHash)
9 changes: 5 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
"pytest-mock", # Test mocker
],
"lint": [
"black>=23.3.0,<24", # auto-formatter and linter
"mypy>=0.991,<1", # Static type analyzer
"black>=23.10.0,<24", # auto-formatter and linter
"mypy>=1.6.1,<2", # Static type analyzer
"types-requests>=2.28.7", # Needed due to mypy typeshed
"types-setuptools", # Needed due to mypy typeshed
"flake8>=6.0.0,<7", # Style linter
"flake8>=6.1.0,<7", # Style linter
"isort>=5.10.1,<6", # Import sorting linter
"mdformat>=0.7.16", # Auto-formatter for markdown
"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
"pydantic<2", # Needed for successful type check.
],
"doc": [
"Sphinx>=3.4.3,<4", # Documentation generator
Expand Down
74 changes: 46 additions & 28 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,28 @@
import_remapping:
- "@bar=bar"
"""
STANDARD_INPUT_JSON = {
"language": "Solidity",
"sources": {
"foo.sol": {"content": FOO_SOURCE_CODE},
".cache/bar/local/bar.sol": {"content": BAR_SOURCE_CODE},
},
"settings": {
"optimizer": {"enabled": True, "runs": 200},
"outputSelection": {
"subcontracts/foo.sol": {"*": OUTPUT_SELECTION, "": ["ast"]},
".cache/bar/local/bar.sol": {"*": OUTPUT_SELECTION, "": ["ast"]},


@pytest.fixture(scope="session")
def standard_input_json(library):
return {
"language": "Solidity",
"sources": {
"foo.sol": {"content": FOO_SOURCE_CODE},
".cache/bar/local/bar.sol": {"content": BAR_SOURCE_CODE},
},
"remappings": ["@bar=.cache/bar/local"],
},
"libraryname1": "MyLib",
"libraryaddress1": "0x274b028b03A250cA03644E6c578D81f019eE1323",
}
"settings": {
"optimizer": {"enabled": True, "runs": 200},
"outputSelection": {
".cache/bar/local/bar.sol": {"": ["ast"], "*": OUTPUT_SELECTION},
"subcontracts/foo.sol": {"": ["ast"], "*": OUTPUT_SELECTION},
},
"remappings": ["@bar=.cache/bar/local"],
"viaIR": False,
},
"libraryname1": "MyLib",
"libraryaddress1": library.address,
}


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -445,7 +450,7 @@ def get_mock_response(


@pytest.fixture
def verification_params(address_to_verify):
def verification_params(address_to_verify, standard_input_json):
ctor_args = "" # noqa: E501

return {
Expand All @@ -459,16 +464,18 @@ def verification_params(address_to_verify):
"module": "contract",
"optimizationUsed": 1,
"runs": 200,
"sourceCode": StringIO(json.dumps(STANDARD_INPUT_JSON)),
"sourceCode": StringIO(json.dumps(standard_input_json)),
}


@pytest.fixture
def verification_params_with_ctor_args(address_to_verify_with_ctor_args):
@pytest.fixture(scope="session")
def verification_params_with_ctor_args(
address_to_verify_with_ctor_args, library, standard_input_json
):
# abi-encoded representation of uint256 value 42
ctor_args = "000000000000000000000000000000000000000000000000000000000000002a" # noqa: E501

json_data = STANDARD_INPUT_JSON.copy()
json_data = standard_input_json.copy()
json_data["libraryaddress1"] = "0xF2Df0b975c0C9eFa2f8CA0491C2d1685104d2488"

return {
Expand All @@ -487,15 +494,26 @@ def verification_params_with_ctor_args(address_to_verify_with_ctor_args):


@pytest.fixture(scope="session")
def address_to_verify(fake_connection, project, account):
# Deploy the library first.
library = account.deploy(project.MyLib)
ape.chain.contracts._local_contract_types[library.address] = library.contract_type
def chain():
return ape.chain

# Add the library to recompile contract `foo`.
solidity = project.compiler_manager.solidity
solidity.add_library(library)

@pytest.fixture(scope="session")
def solidity(project):
return project.compiler_manager.solidity


@pytest.fixture(scope="session")
def library(account, project, chain, solidity):
lib = account.deploy(project.MyLib)
chain.contracts._local_contract_types[lib.address] = lib.contract_type
solidity.add_library(lib)
return lib


@pytest.fixture(scope="session")
def address_to_verify(fake_connection, library, project, account):
_ = library # Ensure library is deployed first.
foo = project.foo.deploy(sender=account)
ape.chain.contracts._local_contract_types[address] = foo.contract_type
return foo.address
Expand Down

0 comments on commit 412dc6b

Please sign in to comment.