Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: avoid mentioning explorer plugins for local-network #2264

Merged
merged 4 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions src/ape/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,18 +587,28 @@ class ContractNotFoundError(ChainError):
Raised when a contract is not found at an address.
"""

def __init__(self, address: "AddressType", has_explorer: bool, network_name: str):
# 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):
msg = f"Failed to get contract type for address '{address}'."
msg += (
" Contract may need verification."
if has_explorer
else (
f" Current network '{network_name}' has no associated "

# 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

if has_explorer:
msg += " Contract may need verification."
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 "
"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)


Expand Down
7 changes: 5 additions & 2 deletions tests/functional/test_contract_call_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from ape.contracts.base import ContractCallHandler
from ape.exceptions import ContractNotFoundError
from ape.utils import ZERO_ADDRESS


def test_struct_input(
Expand All @@ -14,20 +15,22 @@ def test_struct_input(
def test_call_contract_not_found(mocker, method_abi_with_struct_input, networks):
(networks.ethereum.local.__dict__ or {}).pop("explorer", None)
contract = mocker.MagicMock()
contract.address = ZERO_ADDRESS
contract.is_contract = False
method = method_abi_with_struct_input
handler = ContractCallHandler(contract=contract, abis=[method])
expected = ".*Current network 'ethereum:local:test'.*"
expected = f"Failed to get contract type for address '{ZERO_ADDRESS}'."
with pytest.raises(ContractNotFoundError, match=expected):
handler()


def test_transact_contract_not_found(mocker, owner, method_abi_with_struct_input, networks):
(networks.ethereum.local.__dict__ or {}).pop("explorer", None)
contract = mocker.MagicMock()
contract.address = ZERO_ADDRESS
contract.is_contract = False
method = method_abi_with_struct_input
handler = ContractCallHandler(contract=contract, abis=[method])
expected = ".*Current network 'ethereum:local:test'.*"
expected = rf"Failed to get contract type for address '{ZERO_ADDRESS}'\."
with pytest.raises(ContractNotFoundError, match=expected):
handler.transact(sender=owner)
51 changes: 36 additions & 15 deletions tests/functional/test_contracts_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,34 @@ def fn(addr, default=None):


@explorer_test
def test_instance_at_contract_type_not_found(chain, eth_tester_provider):
def test_instance_at_contract_type_not_found_local_network(chain, eth_tester_provider):
eth_tester_provider.network.__dict__["explorer"] = None
new_address = "0x4a986a6dca6dbF99Bc3D17F8d71aFB0D60E740F9"
expected = (
rf"Failed to get contract type for address '{new_address}'. "
r"Current network 'ethereum:local:test' has no associated explorer plugin. "
"Try installing an explorer plugin using .*ape plugins install etherscan.*, "
r"or using a network with explorer support\."
)
expected = rf"Failed to get contract type for address '{new_address}'."
with pytest.raises(ContractNotFoundError, match=expected):
chain.contracts.instance_at(new_address)


@explorer_test
def test_instance_at_contract_type_not_found_live_network(chain, eth_tester_provider):
eth_tester_provider.network.__dict__["explorer"] = None
real_name = eth_tester_provider.network.name
eth_tester_provider.network.name = "sepolia"
try:
new_address = "0x4a986a6dca6dbF99Bc3D17F8d71aFB0D60E740F9"
expected = (
rf"Failed to get contract type for address '{new_address}'. "
r"Current network 'ethereum:sepolia:test' has no associated explorer plugin. "
"Try installing an explorer plugin using .*ape plugins install etherscan.*, "
r"or using a network with explorer support\."
)
with pytest.raises(ContractNotFoundError, match=expected):
chain.contracts.instance_at(new_address)

finally:
eth_tester_provider.network.name = real_name


def test_instance_at_use_abi(chain, solidity_fallback_contract, owner):
new_instance = owner.deploy(solidity_fallback_contract.contract_type)
del chain.contracts[new_instance.address]
Expand Down Expand Up @@ -161,14 +176,20 @@ def test_cache_default_contract_type_when_used(solidity_contract_instance, chain
def test_contracts_getitem_contract_not_found(chain, eth_tester_provider):
eth_tester_provider.network.__dict__["explorer"] = None
new_address = "0x4a986a6dca6dbF99Bc3D17F8d71aFB0D60E740F9"
expected = (
rf"Failed to get contract type for address '{new_address}'. "
r"Current network 'ethereum:local:test' has no associated explorer plugin. "
"Try installing an explorer plugin using .*ape plugins install etherscan.*, "
r"or using a network with explorer support\."
)
with pytest.raises(KeyError, match=expected):
_ = chain.contracts[new_address]
real_name = eth_tester_provider.network.name
eth_tester_provider.network.name = "sepolia"
try:
expected = (
rf"Failed to get contract type for address '{new_address}'. "
r"Current network 'ethereum:sepolia:test' has no associated explorer plugin. "
"Try installing an explorer plugin using .*ape plugins install etherscan.*, "
r"or using a network with explorer support\."
)
with pytest.raises(KeyError, match=expected):
_ = chain.contracts[new_address]

finally:
eth_tester_provider.network.name = real_name


def test_deployments_mapping_cache_location(chain):
Expand Down
23 changes: 23 additions & 0 deletions tests/functional/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import pytest

from ape.api import ReceiptAPI
from ape.api.networks import LOCAL_NETWORK_NAME
from ape.exceptions import (
Abort,
ContractLogicError,
ContractNotFoundError,
NetworkNotFoundError,
TransactionError,
handle_ape_exception,
)
from ape.types import SourceTraceback
from ape.utils import ZERO_ADDRESS
from ape_ethereum.transactions import DynamicFeeTransaction, Receipt


Expand Down Expand Up @@ -224,3 +227,23 @@ def revert_type(self) -> Optional[str]:
actual = error.message
expected = "CUSTOM_ERROR"
assert actual == expected


class TestContractNotFoundError:
def test_local_network(self):
"""
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}'."

def test_fork_network(self):
err = ContractNotFoundError(ZERO_ADDRESS, False, "ethereum:sepolia-fork:test")
assert str(err) == (
f"Failed to get contract type for address '{ZERO_ADDRESS}'. "
"Current network 'ethereum:sepolia-fork:test' 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."
)
Loading