Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor!: use provider in contract error instead of network choice s…
Browse files Browse the repository at this point in the history
…tring (#2385)
antazoey committed Dec 11, 2024
1 parent 2962fae commit 22a6550
Showing 7 changed files with 47 additions and 35 deletions.
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -121,6 +121,7 @@
# ** Dependencies maintained by Ethereum Foundation **
"eth-abi>=5.1.0,<6",
"eth-account>=0.13.4,<0.14",
"eth-tester>=0.12.0b2,<0.13", # Peer: stricter pin needed for [tester].
"eth-typing>=5.0.1,<6",
"eth-utils>=5.1.0,<6",
"hexbytes>=1.2.1,<2",
6 changes: 1 addition & 5 deletions src/ape/contracts/base.py
Original file line number Diff line number Diff line change
@@ -257,11 +257,7 @@ def decode_input(self, calldata: bytes) -> tuple[str, dict[str, Any]]:

def _validate_is_contract(self):
if not self.contract.is_contract:
raise ContractNotFoundError(
self.contract.address,
self.provider.network.explorer is not None,
self.provider.network_choice,
)
raise ContractNotFoundError(self.contract.address, provider=self.provider)


class ContractCallHandler(ContractMethodHandler):
36 changes: 23 additions & 13 deletions src/ape/exceptions.py
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
from ethpm_types.contract_type import ContractType

from ape.api.networks import NetworkAPI
from ape.api.providers import SubprocessProvider
from ape.api.providers import ProviderAPI, SubprocessProvider
from ape.api.trace import TraceAPI
from ape.api.transactions import ReceiptAPI, TransactionAPI
from ape.managers.project import ProjectManager
@@ -590,29 +590,39 @@ class ContractNotFoundError(ChainError):
Raised when a contract is not found at an address.
"""

# TODO: In 0.9, pass in provider object directly (instead of network choice + name)
def __init__(self, address: "AddressType", has_explorer: bool, network_choice: str):
def __init__(self, address: "AddressType", provider: Optional["ProviderAPI"] = None):
try:
msg = self._create_message(address, provider=provider)
except Exception as err:
# Don't let errors occurring within exception handling to
# ruin the exception completely.
logger.error(f"Failed to create proper error message because of: {err}")
msg = f"Failed to get contract type for address '{address}'."

super().__init__(msg)

@classmethod
def _create_message(
cls, address: "AddressType", provider: Optional["ProviderAPI"] = None
) -> str:
msg = f"Failed to get contract type for address '{address}'."
if not provider:
return msg

# NOTE: Network name is optional to avoid breaking change.
choice_parts = network_choice.split(":")
if len(choice_parts) > 1:
network_name = network_choice.split(":")[1]
else:
network_name = network_choice
network_name = provider.network_choice.split(":")[1]

if has_explorer:
msg += " Contract may need verification."
if provider.network.explorer:
msg += " Contract may need verification (if relying on an explorer)."
elif network_name != "local":
# Only bother mentioning explorer plugins if we are not the local network.
msg += (
f" Current network '{network_choice}' has no associated "
f" Current network '{provider.network_choice}' has no associated "
"explorer plugin. Try installing an explorer plugin using "
f"{click.style(text='ape plugins install etherscan', fg='green')}, "
"or using a network with explorer support."
)

super().__init__(msg)
return msg


class UnknownSnapshotError(ChainError):
10 changes: 2 additions & 8 deletions src/ape/managers/chain.py
Original file line number Diff line number Diff line change
@@ -972,9 +972,7 @@ def __getitem__(self, address: AddressType) -> ContractType:
contract_type = self.get(address)
if not contract_type:
# Create error message from custom exception cls.
err = ContractNotFoundError(
address, self.provider.network.explorer is not None, self.provider.network_choice
)
err = ContractNotFoundError(address, provider=self.provider)
# Must raise KeyError.
raise KeyError(str(err))

@@ -1242,11 +1240,7 @@ def instance_at(
)

if not contract_type:
raise ContractNotFoundError(
contract_address,
self.provider.network.explorer is not None,
self.provider.network_choice,
)
raise ContractNotFoundError(contract_address, provider=self.provider)

elif not isinstance(contract_type, ContractType):
raise TypeError(
5 changes: 3 additions & 2 deletions src/ape_ethereum/provider.py
Original file line number Diff line number Diff line change
@@ -1593,8 +1593,9 @@ def ots_get_contract_creator(self, address: "AddressType") -> Optional[dict]:

result = self.make_request("ots_getContractCreator", [address])
if result is None:
# NOTE: Skip the explorer part of the error message via `has_explorer=True`.
raise ContractNotFoundError(address, True, self.network_choice)
# Don't pass provider so the error message is simplifer in this case
# (avoids mentioning explorer plugins).
raise ContractNotFoundError(address)

return result

3 changes: 3 additions & 0 deletions tests/functional/test_contract_container.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
ProjectError,
)
from ape_ethereum.ecosystem import ProxyType
from tests.conftest import explorer_test


def test_deploy(
@@ -166,6 +167,7 @@ def test_at(vyper_contract_instance, vyper_contract_container):
assert instance == vyper_contract_instance


@explorer_test
def test_at_fetch_from_explorer_false(
project_with_contract, mock_explorer, eth_tester_provider, owner
):
@@ -188,3 +190,4 @@ def test_at_fetch_from_explorer_false(

# Clean up test.
eth_tester_provider.network.explorer = None
assert eth_tester_provider.network.explorer is None
21 changes: 14 additions & 7 deletions tests/functional/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
handle_ape_exception,
)
from ape.types.trace import SourceTraceback
from ape.utils.misc import LOCAL_NETWORK_NAME, ZERO_ADDRESS
from ape.utils.misc import ZERO_ADDRESS
from ape_ethereum.transactions import DynamicFeeTransaction, Receipt


@@ -230,20 +230,27 @@ def revert_type(self) -> Optional[str]:


class TestContractNotFoundError:
def test_local_network(self):
def test_local_network(self, eth_tester_provider):
"""
Testing we are NOT mentioning explorer plugins
for the local-network, as 99.9% of the time it is
confusing.
"""
err = ContractNotFoundError(ZERO_ADDRESS, False, f"ethereum:{LOCAL_NETWORK_NAME}:test")
assert str(err) == f"Failed to get contract type for address '{ZERO_ADDRESS}'."
eth_tester_provider.network.explorer = None # Ensure no explorer is set.
err = ContractNotFoundError(ZERO_ADDRESS, provider=eth_tester_provider)
actual = f"{err}"
expected = f"Failed to get contract type for address '{ZERO_ADDRESS}'."
assert actual == expected

def test_fork_network(self):
err = ContractNotFoundError(ZERO_ADDRESS, False, "ethereum:sepolia-fork:test")
def test_fork_network(self, mocker, mock_sepolia):
provider = mocker.MagicMock()
provider.network = mock_sepolia
mock_sepolia.explorer = None
provider.network_choice = "ethereum:sepolia:node"
err = ContractNotFoundError(ZERO_ADDRESS, provider=provider)
assert str(err) == (
f"Failed to get contract type for address '{ZERO_ADDRESS}'. "
"Current network 'ethereum:sepolia-fork:test' has no associated explorer plugin. "
"Current network 'ethereum:sepolia:node' has no associated explorer plugin. "
"Try installing an explorer plugin using \x1b[32mape plugins install etherscan"
"\x1b[0m, or using a network with explorer support."
)

0 comments on commit 22a6550

Please sign in to comment.