Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/userguides/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,32 @@ contract = project.MyContract.deployments[0]

```{note}
Ape does not add or edit deployments in your `ape-config.yaml` file.
You should add them yourself after you deploy if you want to share them with your project.
```

For contracts that are deployed using deterministic deployment factories (such as using `CREATE2`),
the addresses are the same on all chains.
You can reference a contract type deployment as being deterministic, which means it will always deploy to the same address.

To specify a deterministic deployment, use the following config:

```toml
[[tool.ape.deployments.deterministic]]
contract_type = "MyContract"
address = "0x5FbDB2315678afecb367f032d93F642f64180aa3"
```

Or the equivalent YAML:

```yaml
deployments:
deterministic:
- contract_type: MyContract
address: 0x5FbDB2315678afecb367f032d93F642f64180aa3
```

And you can access them (on any public chain) via the same method mentioned above.

## Name

Configure the name of the project:
Expand Down
28 changes: 27 additions & 1 deletion src/ape/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ def __init__(self, *args, **kwargs):
Data for deployed contracts from the project.
"""

deterministic_deployments: list[DeploymentConfig] = Field(default_factory=list)
# NOTE: Unprocessed deployments to merge in later

display: DisplayConfig = DisplayConfig()
"""
Configure display settings in Ape.
Expand Down Expand Up @@ -415,6 +418,12 @@ def validate_model(cls, model):
if "contracts_folder" in fixed_model and isinstance(fixed_model["contracts_folder"], Path):
fixed_model["contracts_folder"] = str(fixed_model["contracts_folder"])

# NOTE: Parse these separately
if "deployments" in fixed_model and (
deterministic_deployments := (fixed_model["deployments"].pop("deterministic", []))
):
fixed_model["deterministic_deployments"] = deterministic_deployments

return fixed_model

@cached_property
Expand All @@ -425,12 +434,29 @@ def deployments(self) -> dict[str, dict[str, list[DeploymentConfig]]]:
raise ConfigError(f"Invalid ecosystem '{ecosystem_name}' in deployments config.")

ecosystem = self.network_manager.ecosystems[ecosystem_name]
for network_name, network_deploys in ecosystem_deploys.items():
for network_name in ecosystem_deploys:
if network_name not in ecosystem.networks:
raise ConfigError(
f"Invalid network '{ecosystem_name}:{network_name}' in deployments config."
)

# NOTE: Merge deterministic deployments after we initialize `NetworkManager.ecosystems`
for ecosystem_name, ecosystem in self.network_manager.ecosystems.items():
self.deployment_data.setdefault(ecosystem_name, {})

for network_name in ecosystem.networks:
if network_name == "local" or network_name.endswith("-fork"):
continue

elif network_name not in self.deployment_data[ecosystem_name]:
self.deployment_data[ecosystem_name][network_name] = (
self.deterministic_deployments
)
else:
self.deployment_data[ecosystem_name][network_name].extend(
self.deterministic_deployments
)

return self.deployment_data

def __ape_extra_attributes__(self) -> Iterator[ExtraModelAttributes]:
Expand Down
23 changes: 21 additions & 2 deletions tests/functional/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pydantic import ValidationError
from pydantic_settings import SettingsConfigDict

from ape.api.config import ApeConfig, ConfigEnum, PluginConfig
from ape.api.config import ApeConfig, ConfigEnum, DeploymentConfig, PluginConfig
from ape.exceptions import ConfigError
from ape.managers.config import CONFIG_FILE_NAME, merge_configs
from ape.types.gas import AutoGasLimit
Expand Down Expand Up @@ -303,6 +303,25 @@ def test_deployments(networks_connected_to_tester, owner, project):
assert deployment.address == instance.address


def test_deployments_deterministic(
networks_connected_to_tester, owner, vyper_contract_container, project
):
_ = networks_connected_to_tester # Connection needs to lookup config.

# First, obtain a "previously-deployed" contract.
instance = vyper_contract_container.deploy(1000200000, sender=owner)
address = instance.address

# Create a config using this new contract for a "later time".
deploy = DeploymentConfig(address=address, contract_type=instance.contract_type.name)
with project.temp_config(deployments=dict(deterministic=[deploy])):
deploy_config = project.config.deployments
assert deploy_config["ethereum"]["mainnet"][0]["address"] == address
deployment = vyper_contract_container.deployments[0]

assert deployment.address == instance.address


def test_deployments_integer_type_addresses(networks, project):
deploys = {
**_create_deployments(address=0x0C25212C557D00024B7CA3DF3238683A35541354),
Expand Down Expand Up @@ -581,7 +600,7 @@ def test_contracts_folder(project):


def test_contracts_folder_with_hyphen(project):
with project.temp_config(**{"contracts-folder": "src"}):
with project.temp_config(**{"contracts_folder": "src"}):
assert project.contracts_folder.name == "src"


Expand Down
Loading