Skip to content

Commit 40c85cb

Browse files
feat(execute): skip deterministic factory deploy when it can't be bootstrapped (#2944)
* 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. * chore: update comment --------- Co-authored-by: LouisTsai <q1030176@gmail.com>
1 parent 55f61ab commit 40c85cb

3 files changed

Lines changed: 64 additions & 9 deletions

File tree

packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/contracts.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
logger = get_logger(__name__)
2121

2222

23+
class DeterministicFactoryNotDeployableError(Exception):
24+
"""
25+
Raised when the deterministic proxy cannot deploy.
26+
Example: fixed gas limit insufficient for network creation cost.
27+
"""
28+
29+
2330
def check_deterministic_factory_deployment(
2431
*,
2532
eth_rpc: EthRPC,
@@ -96,6 +103,28 @@ def deploy_deterministic_factory_contract(
96103
).with_signature_and_sender()
97104
deploy_tx_sender = deploy_tx.sender
98105
assert deploy_tx_sender is not None
106+
107+
# Pre-flight: skip deploy if network gas > fixed limit.
108+
# Gas limit is fixed as changing it alters sender/factory address.
109+
# If network requires more gas, transaction can never be included.
110+
try:
111+
required_gas = eth_rpc.estimate_gas(
112+
transaction={
113+
"from": f"{deploy_tx_sender}",
114+
"input": f"{deploy_tx.data}",
115+
}
116+
)
117+
except Exception:
118+
# If the estimate itself is unavailable, fall through and attempt the
119+
# deploy as before (failures are still handled by the caller).
120+
required_gas = None
121+
if required_gas is not None and required_gas > deploy_tx_gas_limit:
122+
raise DeterministicFactoryNotDeployableError(
123+
f"network requires {required_gas} gas to create the deterministic "
124+
f"deployment proxy, exceeding the keyless transaction's fixed gas "
125+
f"limit of {deploy_tx_gas_limit}"
126+
)
127+
99128
required_deployer_balance = deploy_tx_gas_price * deploy_tx_gas_limit
100129
current_balance = eth_rpc.get_balance(deploy_tx_sender)
101130
if current_balance < required_deployer_balance:

packages/testing/src/execution_testing/cli/pytest_commands/plugins/execute/pre_alloc.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ def execute_required_contracts(
152152
Deploy required contracts for the execute command.
153153
154154
- Deterministic deployment proxy
155+
156+
Proxy deploy failure doesn't abort the session.
157+
Tests skip deterministic deploys on use.
158+
Details check `(see Alloc._resolve_deterministic_deploys)`.
155159
"""
156160
base_lock_file = session_temp_folder / "execute_required_contracts.lock"
157161
with FileLock(base_lock_file):
@@ -171,12 +175,13 @@ def execute_required_contracts(
171175
gas_price=sender_funding_transactions_gas_price,
172176
)
173177
except Exception as e:
174-
raise RuntimeError(
175-
f"Error deploying deterministic deployment contract:\n{e}"
176-
"\nTry deploying the contract manually using a different "
177-
"RPC endpoint with the following command:\n"
178-
"uv run execute deploy-required-contracts"
179-
) from e
178+
logger.warning(
179+
"Could not deploy the deterministic deployment proxy; "
180+
"tests that require it will be skipped. To deploy it "
181+
"manually against a different RPC endpoint run "
182+
"`uv run execute deploy-required-contracts`. "
183+
f"Reason: {e}"
184+
)
180185

181186

182187
class PendingTransaction(Transaction):
@@ -794,12 +799,17 @@ def _resolve_deterministic_deploys(self) -> None:
794799
)
795800
else:
796801
if not factory_checked:
797-
assert (
802+
if (
798803
check_deterministic_factory_deployment(
799804
eth_rpc=self._eth_rpc, fork=fork
800805
)
801-
is not None
802-
), "Deployment contract code is not found"
806+
is None
807+
):
808+
pytest.skip(
809+
"deterministic deployment proxy is not available "
810+
"on this network; skipping test that requires a "
811+
"deterministic contract deployment"
812+
)
803813
factory_checked = True
804814

805815
logger.info(

packages/testing/src/execution_testing/rpc/rpc.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,22 @@ def get_balances(
634634
responses = self.post_batch_request(calls=calls)
635635
return [int(r.result_or_raise(), 16) for r in responses]
636636

637+
def estimate_gas(
638+
self,
639+
transaction: Dict[str, Any],
640+
block_number: BlockNumberType = "latest",
641+
) -> int:
642+
"""`eth_estimateGas`: Return the gas required to execute a tx."""
643+
block = (
644+
hex(block_number)
645+
if isinstance(block_number, int)
646+
else block_number
647+
)
648+
response = self.post_request(
649+
request=RPCCall(method="estimateGas", params=[transaction, block])
650+
).result_or_raise()
651+
return int(response, 16)
652+
637653
def get_code(
638654
self, address: Address, block_number: BlockNumberType = "latest"
639655
) -> Bytes:

0 commit comments

Comments
 (0)