Skip to content

Commit fe7d324

Browse files
authored
fix(execute): Add stub_name to pre.deploy_contract (#2104)
* fix(execute): Add `stub_name` to `pre.deploy_contract` * fix(execute): Don't force require address stubs in execute hive mode * feat(execute): Check that address in the stubbed contracts list contains code * docs: update stubbed contracts docs * rename "stub_name" -> "stub"
1 parent f14fe26 commit fe7d324

File tree

5 files changed

+52
-16
lines changed

5 files changed

+52
-16
lines changed

docs/running_tests/execute/remote.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,26 @@ COMPOUND_COMPTROLLER: 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B
107107
108108
### How Address Stubs Work
109109
110-
When a test uses a contract label that matches a key in the address stubs, the test framework will:
110+
When a test deploys a contract using `pre.deploy_contract(..., stub="NAME")` and the stub name matches a key in the address stubs, the test framework will:
111111

112112
1. Use the pre-deployed contract at the specified address instead of deploying a new contract
113113
2. Skip the contract deployment transaction, saving gas and time
114114
3. Use the existing contract's code and state for the test
115115

116116
This is particularly useful when testing interactions with well-known contracts that are expensive to deploy or when you want to test against the actual deployed versions of contracts.
117117

118+
If the address is _not_ present in the stubbed addresses list, the test will fail to execute.
119+
120+
If the address contained in the stubbed addresses list does not contain code on the remote chain, the test will fail.
121+
122+
The pre-alloc will be populated with live information from the chain, so the following lines will result in up-to-date information:
123+
124+
```python
125+
my_stubbed_contract = pre.deploy_contract(code, stub="uniswap")
126+
pre[my_stubbed_contract].nonce # Actual nonce of the contract on chain
127+
pre[my_stubbed_contract].balance # Actual balance of the contract on chain
128+
```
129+
118130
### Bloat-net Testing
119131

120132
Address stubs are especially valuable when testing on **bloat-net**, a specialized network that contains pre-existing contracts with extensive storage history. On bloat-net:

src/ethereum_test_types/account_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ def deploy_contract(
341341
address: Address | None = None,
342342
evm_code_type: EVMCodeType | None = None,
343343
label: str | None = None,
344+
stub: str | None = None,
344345
) -> Address:
345346
"""Deploy a contract to the allocation."""
346347
raise NotImplementedError("deploy_contract is not implemented in the base class")

src/pytest_plugins/execute/pre_alloc.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,6 @@ def pytest_addoption(parser):
119119
type=int,
120120
help="The default amount of wei to fund each EOA in each test with.",
121121
)
122-
pre_alloc_group.addoption(
123-
"--address-stubs",
124-
action="store",
125-
dest="address_stubs",
126-
default=AddressStubs(root={}),
127-
type=AddressStubs.model_validate_json_or_file,
128-
help="The address stubs for contracts that have already been placed in the chain and to "
129-
"use for the test. Can be a JSON formatted string or a path to a YAML or JSON file.",
130-
)
131122

132123

133124
@pytest.hookimpl(trylast=True)
@@ -143,9 +134,13 @@ def pytest_report_header(config):
143134

144135

145136
@pytest.fixture(scope="session")
146-
def address_stubs(request) -> AddressStubs:
147-
"""Return an address stubs object."""
148-
return request.config.getoption("address_stubs")
137+
def address_stubs(request) -> AddressStubs | None:
138+
"""
139+
Return an address stubs object.
140+
141+
If the address stubs are not supported by the subcommand, return None.
142+
"""
143+
return request.config.getoption("address_stubs", None)
149144

150145

151146
@pytest.fixture(scope="session")
@@ -223,6 +218,7 @@ def deploy_contract(
223218
address: Address | None = None,
224219
evm_code_type: EVMCodeType | None = None,
225220
label: str | None = None,
221+
stub: str | None = None,
226222
) -> Address:
227223
"""Deploy a contract to the allocation."""
228224
if storage is None:
@@ -232,8 +228,24 @@ def deploy_contract(
232228
if not isinstance(storage, Storage):
233229
storage = Storage(storage) # type: ignore
234230

235-
if label and label in self._address_stubs:
236-
return self._address_stubs[label]
231+
if stub is not None and self._address_stubs is not None:
232+
if stub not in self._address_stubs:
233+
raise ValueError(f"Stub name {stub} not found in address stubs")
234+
contract_address = self._address_stubs[stub]
235+
code = self._eth_rpc.get_code(contract_address)
236+
if code == b"":
237+
raise ValueError(f"Stub {stub} at {contract_address} has no code")
238+
balance = self._eth_rpc.get_balance(contract_address)
239+
nonce = self._eth_rpc.get_transaction_count(contract_address)
240+
super().__setitem__(
241+
contract_address,
242+
Account(
243+
nonce=nonce,
244+
balance=balance,
245+
code=code,
246+
storage={},
247+
),
248+
)
237249

238250
initcode_prefix = Bytecode()
239251

@@ -509,7 +521,7 @@ def pre(
509521
chain_config: ChainConfig,
510522
eoa_fund_amount_default: int,
511523
default_gas_price: int,
512-
address_stubs: AddressStubs,
524+
address_stubs: AddressStubs | None,
513525
request: pytest.FixtureRequest,
514526
) -> Generator[Alloc, None, None]:
515527
"""Return default pre allocation for all tests (Empty alloc)."""

src/pytest_plugins/execute/rpc/remote.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ethereum_test_rpc import EngineRPC, EthRPC
99
from ethereum_test_types.chain_config_types import ChainConfigDefaults
1010

11+
from ..pre_alloc import AddressStubs
1112
from .chain_builder_eth_rpc import ChainBuilderEthRPC
1213

1314

@@ -40,6 +41,15 @@ def pytest_addoption(parser):
4041
default=60,
4142
help="Maximum time in seconds to wait for a transaction to be included in a block",
4243
)
44+
remote_rpc_group.addoption(
45+
"--address-stubs",
46+
action="store",
47+
dest="address_stubs",
48+
default=AddressStubs(root={}),
49+
type=AddressStubs.model_validate_json_or_file,
50+
help="The address stubs for contracts that have already been placed in the chain and to "
51+
"use for the test. Can be a JSON formatted string or a path to a YAML or JSON file.",
52+
)
4353

4454
engine_rpc_group = parser.getgroup("engine_rpc", "Arguments defining engine RPC configuration")
4555
engine_rpc_group.addoption(

src/pytest_plugins/filler/pre_alloc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def deploy_contract(
142142
address: Address | None = None,
143143
evm_code_type: EVMCodeType | None = None,
144144
label: str | None = None,
145+
stub: str | None = None,
145146
) -> Address:
146147
"""
147148
Deploy a contract to the allocation.

0 commit comments

Comments
 (0)