From 0bf4bec87d6f36c0fcf7d5502b8752b941ac272a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20Wahrst=C3=A4tter?= Date: Sat, 30 May 2026 17:48:03 +0200 Subject: [PATCH 1/2] feat(execute): skip the deterministic factory deploy (and dependent tests) when it can't be bootstrapped The deterministic deployment proxy is bootstrapped in an autouse session fixture via a keyless transaction with a fixed gas limit. On chains where the contract-creation intrinsic gas exceeds that limit (so the keyless tx can never be mined), the deploy aborted the entire execute session, blocking even tests that never use the factory. - Pre-flight the deploy with `eth_estimateGas`: if the network requires more gas for the creation than the keyless tx's fixed gas limit, raise instead of attempting it (no funding tx, no doomed send, no inclusion wait). - Make the session fixture best-effort: warn instead of raising, so tests that don't need the factory still run. - Skip a test that requests a deterministic deployment when the factory is unavailable. - Add `EthRPC.estimate_gas` for the pre-flight. --- .../plugins/execute/contracts.py | 33 +++++++++++++++++++ .../plugins/execute/pre_alloc.py | 31 ++++++++++++----- .../testing/src/execution_testing/rpc/rpc.py | 16 +++++++++ 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py index 4be3f8aa36c..45040a2f551 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py @@ -20,6 +20,15 @@ logger = get_logger(__name__) +class DeterministicFactoryNotDeployableError(Exception): + """ + Raised when the deterministic deployment proxy cannot be bootstrapped on + the connected network because its keyless deployment transaction would be + rejected (its fixed gas limit cannot cover the gas the network requires for + the creation, e.g. a higher contract-creation intrinsic gas cost). + """ + + def check_deterministic_factory_deployment( *, eth_rpc: EthRPC, @@ -96,6 +105,30 @@ def deploy_deterministic_factory_contract( ).with_signature_and_sender() deploy_tx_sender = deploy_tx.sender assert deploy_tx_sender is not None + + # Pre-flight: don't even attempt the deploy if the network would reject the + # keyless transaction. Its gas limit is fixed (changing it would change the + # recovered sender and therefore the factory address), so if the network + # requires more gas for the creation than that limit, the transaction can + # never be included -- skip the funding tx, send and inclusion wait. + try: + required_gas = eth_rpc.estimate_gas( + transaction={ + "from": f"{deploy_tx_sender}", + "input": f"{deploy_tx.data}", + } + ) + except Exception: + # If the estimate itself is unavailable, fall through and attempt the + # deploy as before (failures are still handled by the caller). + required_gas = None + if required_gas is not None and required_gas > deploy_tx_gas_limit: + raise DeterministicFactoryNotDeployableError( + f"network requires {required_gas} gas to create the deterministic " + f"deployment proxy, exceeding the keyless transaction's fixed gas " + f"limit of {deploy_tx_gas_limit}" + ) + required_deployer_balance = deploy_tx_gas_price * deploy_tx_gas_limit current_balance = eth_rpc.get_balance(deploy_tx_sender) if current_balance < required_deployer_balance: diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py index 5cb131b3ba9..b8eda2e611c 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py @@ -152,6 +152,13 @@ def execute_required_contracts( Deploy required contracts for the execute command. - Deterministic deployment proxy + + Best-effort: if the proxy is absent and cannot be deployed (e.g. the + keyless deployment transaction can no longer be mined because the fork's + contract-creation intrinsic gas exceeds the transaction's fixed gas limit), + the session is not aborted. Tests that do not need the proxy still run; + tests that request a deterministic deployment are skipped when they try to + use it (see ``Alloc._resolve_deterministic_deploys``). """ base_lock_file = session_temp_folder / "execute_required_contracts.lock" with FileLock(base_lock_file): @@ -171,12 +178,13 @@ def execute_required_contracts( gas_price=sender_funding_transactions_gas_price, ) except Exception as e: - raise RuntimeError( - f"Error deploying deterministic deployment contract:\n{e}" - "\nTry deploying the contract manually using a different " - "RPC endpoint with the following command:\n" - "uv run execute deploy-required-contracts" - ) from e + logger.warning( + "Could not deploy the deterministic deployment proxy; " + "tests that require it will be skipped. To deploy it " + "manually against a different RPC endpoint run " + "`uv run execute deploy-required-contracts`. " + f"Reason: {e}" + ) class PendingTransaction(Transaction): @@ -752,12 +760,17 @@ def _resolve_deterministic_deploys(self) -> None: ) else: if not factory_checked: - assert ( + if ( check_deterministic_factory_deployment( eth_rpc=self._eth_rpc, fork=fork ) - is not None - ), "Deployment contract code is not found" + is None + ): + pytest.skip( + "deterministic deployment proxy is not available " + "on this network; skipping test that requires a " + "deterministic contract deployment" + ) factory_checked = True logger.info( diff --git a/packages/testing/src/execution_testing/rpc/rpc.py b/packages/testing/src/execution_testing/rpc/rpc.py index 2c8cdcb50b5..3f9c64c332f 100644 --- a/packages/testing/src/execution_testing/rpc/rpc.py +++ b/packages/testing/src/execution_testing/rpc/rpc.py @@ -634,6 +634,22 @@ def get_balances( responses = self.post_batch_request(calls=calls) return [int(r.result_or_raise(), 16) for r in responses] + def estimate_gas( + self, + transaction: Dict[str, Any], + block_number: BlockNumberType = "latest", + ) -> int: + """`eth_estimateGas`: Return the gas required to execute a tx.""" + block = ( + hex(block_number) + if isinstance(block_number, int) + else block_number + ) + response = self.post_request( + request=RPCCall(method="estimateGas", params=[transaction, block]) + ).result_or_raise() + return int(response, 16) + def get_code( self, address: Address, block_number: BlockNumberType = "latest" ) -> Bytes: From 3c183a54c3b01a66a1857d45c6dae4c8cff1b141 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Mon, 1 Jun 2026 15:04:06 +0800 Subject: [PATCH 2/2] chore: update comment --- .../pytest_commands/plugins/execute/contracts.py | 14 +++++--------- .../pytest_commands/plugins/execute/pre_alloc.py | 9 +++------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py index 45040a2f551..d13a09f9ab6 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py @@ -22,10 +22,8 @@ class DeterministicFactoryNotDeployableError(Exception): """ - Raised when the deterministic deployment proxy cannot be bootstrapped on - the connected network because its keyless deployment transaction would be - rejected (its fixed gas limit cannot cover the gas the network requires for - the creation, e.g. a higher contract-creation intrinsic gas cost). + Raised when the deterministic proxy cannot deploy. + Example: fixed gas limit insufficient for network creation cost. """ @@ -106,11 +104,9 @@ def deploy_deterministic_factory_contract( deploy_tx_sender = deploy_tx.sender assert deploy_tx_sender is not None - # Pre-flight: don't even attempt the deploy if the network would reject the - # keyless transaction. Its gas limit is fixed (changing it would change the - # recovered sender and therefore the factory address), so if the network - # requires more gas for the creation than that limit, the transaction can - # never be included -- skip the funding tx, send and inclusion wait. + # Pre-flight: skip deploy if network gas > fixed limit. + # Gas limit is fixed as changing it alters sender/factory address. + # If network requires more gas, transaction can never be included. try: required_gas = eth_rpc.estimate_gas( transaction={ diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py index b8eda2e611c..995a8e387b1 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py @@ -153,12 +153,9 @@ def execute_required_contracts( - Deterministic deployment proxy - Best-effort: if the proxy is absent and cannot be deployed (e.g. the - keyless deployment transaction can no longer be mined because the fork's - contract-creation intrinsic gas exceeds the transaction's fixed gas limit), - the session is not aborted. Tests that do not need the proxy still run; - tests that request a deterministic deployment are skipped when they try to - use it (see ``Alloc._resolve_deterministic_deploys``). + Proxy deploy failure doesn't abort the session. + Tests skip deterministic deploys on use. + Details check `(see Alloc._resolve_deterministic_deploys)`. """ base_lock_file = session_temp_folder / "execute_required_contracts.lock" with FileLock(base_lock_file):