Skip to content

Commit

Permalink
fix: avoid mentioning explorer plugins for local-network (#2264)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Sep 3, 2024
1 parent d9d6f56 commit 21ff959
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 24 deletions.
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."
)

0 comments on commit 21ff959

Please sign in to comment.