diff --git a/src/ape_ethereum/ecosystem.py b/src/ape_ethereum/ecosystem.py index 07c70c2250..81766a8e99 100644 --- a/src/ape_ethereum/ecosystem.py +++ b/src/ape_ethereum/ecosystem.py @@ -639,7 +639,9 @@ def create_transaction(self, **kwargs) -> TransactionAPI: if isinstance(kwargs.get("chainId"), str): kwargs["chainId"] = int(kwargs["chainId"], 16) - elif "chainId" not in kwargs and self.network_manager.active_provider is not None: + elif ( + "chainId" not in kwargs or kwargs["chainId"] is None + ) and self.network_manager.active_provider is not None: kwargs["chainId"] = self.provider.chain_id if "input" in kwargs: @@ -660,7 +662,16 @@ def create_transaction(self, **kwargs) -> TransactionAPI: kwargs["gas"] = kwargs.pop("gas_limit", kwargs.get("gas")) if "value" in kwargs and not isinstance(kwargs["value"], int): - kwargs["value"] = self.conversion_manager.convert(kwargs["value"], int) + value = kwargs["value"] or 0 # Convert None to 0. + kwargs["value"] = self.conversion_manager.convert(value, int) + + # This causes problems in pydantic for some reason. + if "gas_price" in kwargs and kwargs["gas_price"] is None: + del kwargs["gas_price"] + + # None is not allowed, the user likely means `b""`. + if "data" in kwargs and kwargs["data"] is None: + kwargs["data"] = b"" return txn_class(**kwargs) diff --git a/tests/functional/test_ecosystem.py b/tests/functional/test_ecosystem.py index 3c165eef8e..ad3221c259 100644 --- a/tests/functional/test_ecosystem.py +++ b/tests/functional/test_ecosystem.py @@ -10,7 +10,7 @@ from ape.types import AddressType from ape.utils import DEFAULT_LOCAL_TRANSACTION_ACCEPTANCE_TIMEOUT from ape_ethereum.ecosystem import BLUEPRINT_HEADER, Block -from ape_ethereum.transactions import TransactionType +from ape_ethereum.transactions import DynamicFeeTransaction, StaticFeeTransaction, TransactionType LOG = { "removed": False, @@ -378,6 +378,38 @@ def test_create_transaction_uses_network_gas_limit(tx_type, ethereum, eth_tester assert tx.gas_limit == eth_tester_provider.max_gas +def test_create_transaction_with_none_values(ethereum, eth_tester_provider): + """ + Tests against None being in place of values in kwargs, + causing their actual defaults not to get used and ValidationErrors + to occur. + """ + static = ethereum.create_transaction( + value=None, data=None, chain_id=None, gas_price=None, nonce=None, receiver=None + ) + dynamic = ethereum.create_transaction( + value=None, + data=None, + chain_id=None, + max_fee=None, + max_prioriy_fee=None, + nonce=None, + receiver=None, + ) + assert isinstance(static, StaticFeeTransaction) # Because used gas_price + assert isinstance(dynamic, DynamicFeeTransaction) # Because used max_fee + + for tx in (static, dynamic): + assert tx.data == b"" # None is not allowed. + assert tx.value == 0 # None is same as 0. + assert tx.chain_id == eth_tester_provider.chain_id + assert tx.nonce is None + + assert static.gas_price is None + assert dynamic.max_fee is None + assert dynamic.max_priority_fee is None + + @pytest.mark.parametrize("tx_type", TransactionType) def test_encode_transaction(tx_type, ethereum, vyper_contract_instance, owner, eth_tester_provider): abi = vyper_contract_instance.contract_type.methods[0]