Skip to content

Commit

Permalink
feat: ability to initialize contract objects with an address (#291)
Browse files Browse the repository at this point in the history
* feat: initialize contract from address

* chore: address PR feedback
  • Loading branch information
NotPeopling2day authored Dec 7, 2021
1 parent 42aa439 commit 6490c41
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/ape/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
accounts = _AccountManager(config, _converters, plugin_manager, networks) # type: ignore
"""Manages accounts for the current project. See :class:`ape.managers.accounts.AccountManager`."""

Project = _partial(_ProjectManager, config=config, compilers=compilers)
Project = _partial(_ProjectManager, config=config, compilers=compilers, networks=networks)
"""User-facing class for instantiating Projects (in addition to the currently
active ``project``). See :class:`ape.managers.project.ProjectManager`."""

Expand Down
20 changes: 5 additions & 15 deletions src/ape/api/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@

from ape.exceptions import AccountsError, AliasAlreadyInUseError, SignatureError
from ape.logging import logger
from ape.types import (
AddressType,
ContractType,
MessageSignature,
SignableMessage,
TransactionSignature,
)
from ape.types import AddressType, MessageSignature, SignableMessage, TransactionSignature
from ape.utils import cached_property

from .address import AddressAPI
Expand Down Expand Up @@ -137,26 +131,22 @@ def transfer(

return self.call(txn, send_everything=value is None)

def deploy(self, contract_type: ContractType, *args, **kwargs) -> ContractInstance:
c = ContractContainer( # type: ignore
_provider=self.provider,
_contract_type=contract_type,
)
def deploy(self, contract: ContractContainer, *args, **kwargs) -> ContractInstance:

txn = c(*args, **kwargs)
txn = contract(*args, **kwargs)
txn.sender = self.address
receipt = self.call(txn)

if not receipt.contract_address:
raise AccountsError(f"'{receipt.txn_hash}' did not create a contract.")

address = click.style(receipt.contract_address, bold=True)
logger.success(f"Contract '{contract_type.contractName}' deployed to: {address}")
logger.success(f"Contract '{contract.contract_type.contractName}' deployed to: {address}")

return ContractInstance( # type: ignore
_provider=self.provider,
_address=receipt.contract_address,
_contract_type=contract_type,
_contract_type=contract.contract_type,
)


Expand Down
22 changes: 10 additions & 12 deletions src/ape/api/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,41 +234,39 @@ def get_handler(abi_type: str) -> Any:

@dataclass
class ContractContainer:
_provider: ProviderAPI
_contract_type: ContractType
contract_type: ContractType
_provider: Optional[ProviderAPI]
# _provider is only None when a user is not connected to a provider.

def __repr__(self) -> str:
return f"<{self._contract_type.contractName}>"
return f"<{self.contract_type.contractName}>"

def at(self, address: str) -> ContractInstance:
return ContractInstance( # type: ignore
_address=address,
_provider=self._provider,
_contract_type=self._contract_type,
_contract_type=self.contract_type,
)

@property
def _deployment_bytecode(self) -> bytes:
if (
self._contract_type.deploymentBytecode
and self._contract_type.deploymentBytecode.bytecode
):
return to_bytes(hexstr=self._contract_type.deploymentBytecode.bytecode)
if self.contract_type.deploymentBytecode and self.contract_type.deploymentBytecode.bytecode:
return to_bytes(hexstr=self.contract_type.deploymentBytecode.bytecode)

else:
return b""

@property
def _runtime_bytecode(self) -> bytes:
if self._contract_type.runtimeBytecode and self._contract_type.runtimeBytecode.bytecode:
return to_bytes(hexstr=self._contract_type.runtimeBytecode.bytecode)
if self.contract_type.runtimeBytecode and self.contract_type.runtimeBytecode.bytecode:
return to_bytes(hexstr=self.contract_type.runtimeBytecode.bytecode)

else:
return b""

def __call__(self, *args, **kwargs) -> TransactionAPI:
constructor = ContractConstructor( # type: ignore
abi=self._contract_type.constructor,
abi=self.contract_type.constructor,
provider=self._provider,
deployment_bytecode=self._deployment_bytecode,
)
Expand Down
13 changes: 11 additions & 2 deletions src/ape/managers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import requests
from dataclassy import dataclass

from ape.api.contracts import ContractContainer
from ape.exceptions import ProjectError
from ape.managers.networks import NetworkManager
from ape.types import Checksum, Compiler, ContractType, PackageManifest, Source
from ape.utils import compute_checksum

Expand All @@ -18,6 +20,7 @@ class ProjectManager:
path: Path
config: ConfigManager
compilers: CompilerManager
networks: NetworkManager

dependencies: Dict[str, PackageManifest] = dict()

Expand Down Expand Up @@ -209,13 +212,19 @@ def contracts(self) -> Dict[str, ContractType]:

def __getattr__(self, attr_name: str):
contracts = self.load_contracts()
contract_type = None

if attr_name in contracts:
return contracts[attr_name]
contract_type = contracts[attr_name]
elif attr_name in self.dependencies:
return self.dependencies[attr_name]
contract_type = self.dependencies[attr_name] # type: ignore
else:
raise AttributeError(f"{self.__class__.__name__} has no attribute '{attr_name}'.")

return ContractContainer( # type: ignore
contract_type=contract_type, _provider=self.networks.active_provider
)

@property
def interfaces_folder(self) -> Path:
return self.path / "interfaces"
Expand Down

0 comments on commit 6490c41

Please sign in to comment.