diff --git a/python/src/skale_contracts/network.py b/python/src/skale_contracts/network.py index 0f50478..1603ca9 100644 --- a/python/src/skale_contracts/network.py +++ b/python/src/skale_contracts/network.py @@ -1,7 +1,7 @@ """Module for work with networks""" from __future__ import annotations -from typing import TYPE_CHECKING +from typing import cast, TYPE_CHECKING from web3 import Web3 from web3.providers.base import BaseProvider @@ -28,9 +28,21 @@ def skale_contracts(self) -> SkaleContracts: """Get SkaleContracts object associated with the network""" return self._skale_contracts + def is_listed(self) -> bool: + """Return if the network is present in the skale-contract repository""" + return False + + def as_listed(self) -> ListedNetwork: + """Cast to ListedNetwork""" + return cast(ListedNetwork, self) + class ListedNetwork(Network): """Network that is listed in the metadata""" def __init__(self, skale_contracts: SkaleContracts, provider: BaseProvider, path: str): super().__init__(skale_contracts, provider) self.path = path + + def is_listed(self) -> bool: + """Return if the network is present in the skale-contract repository""" + return True diff --git a/python/src/skale_contracts/project.py b/python/src/skale_contracts/project.py index cb7c740..8be3bf7 100644 --- a/python/src/skale_contracts/project.py +++ b/python/src/skale_contracts/project.py @@ -2,24 +2,16 @@ from __future__ import annotations from abc import ABC, abstractmethod from typing import TYPE_CHECKING -from attr import dataclass from eth_utils.address import to_canonical_address import requests from .constants import REPOSITORY_URL, NETWORK_TIMEOUT from .instance import Instance, InstanceData -from .network import ListedNetwork if TYPE_CHECKING: from eth_typing import Address from .network import Network - - -@dataclass -class ProjectMetadata: - """Contains project metadata""" - name: str - path: str + from .project_metadata import ProjectMetadata class Project(ABC): @@ -30,8 +22,8 @@ def __init__(self, network: Network, metadata: ProjectMetadata) -> None: self.network = network self._metadata = metadata - @abstractmethod @property + @abstractmethod def github_repo(self) -> str: """URL of github repo with the project""" @@ -53,7 +45,10 @@ def get_instance(self, alias_or_address: str) -> Instance: def download_abi_file(self, version: str) -> str: """Download file with ABI""" - response = requests.get(self.get_abi_url(version), timeout=NETWORK_TIMEOUT) + url = self.get_abi_url(version) + response = requests.get(url, timeout=NETWORK_TIMEOUT) + if response.status_code != 200: + raise RuntimeError(f"Can't download abi file from {url}") return response.text def get_abi_url(self, version: str) -> str: @@ -66,8 +61,9 @@ def get_abi_filename(self, version: str) -> str: def get_instance_data_url(self, alias: str) -> str: """Get URL of a file containing address for provided alias""" - if isinstance(self.network, ListedNetwork): - return f'{REPOSITORY_URL}{self.network.path}/{self._metadata.path}/{alias}.json' + if self.network.is_listed(): + return f'{REPOSITORY_URL}{self.network.as_listed().path}/' + \ + f'{self._metadata.path}/{alias}.json' raise ValueError('Network is unknown') @abstractmethod diff --git a/python/src/skale_contracts/project_factory.py b/python/src/skale_contracts/project_factory.py index 604c6c0..9131384 100644 --- a/python/src/skale_contracts/project_factory.py +++ b/python/src/skale_contracts/project_factory.py @@ -4,9 +4,8 @@ from typing import TYPE_CHECKING from attr import dataclass -from .project import ProjectMetadata -from .projects.skale_manager import SkaleManagerProject - +from . import projects +from .project_metadata import ProjectMetadata if TYPE_CHECKING: from .project import Project @@ -17,10 +16,13 @@ class Projects: """Contains all known projects""" skale_manager = ProjectMetadata(name='skale-manager', path='skale-manager') + mainnet_ima = ProjectMetadata(name='mainnet-ima', path='mainnet-ima') def create_project(network: Network, name: str) -> Project: """Create Project object based on it's name""" if name == Projects.skale_manager.name: - return SkaleManagerProject(network, Projects.skale_manager) + return projects.SkaleManager(network, Projects.skale_manager) + if name == Projects.mainnet_ima.name: + return projects.MainnetIma(network, Projects.mainnet_ima) raise ValueError(f'Project with name {name} is unknown') diff --git a/python/src/skale_contracts/project_metadata.py b/python/src/skale_contracts/project_metadata.py new file mode 100644 index 0000000..5e4a2de --- /dev/null +++ b/python/src/skale_contracts/project_metadata.py @@ -0,0 +1,8 @@ +"""Tools for project metadata processing""" +from attr import dataclass + +@dataclass +class ProjectMetadata: + """Contains project metadata""" + name: str + path: str diff --git a/python/src/skale_contracts/projects/__init__.py b/python/src/skale_contracts/projects/__init__.py new file mode 100644 index 0000000..a2aaf92 --- /dev/null +++ b/python/src/skale_contracts/projects/__init__.py @@ -0,0 +1,5 @@ +"""Supported projects""" +from .ima import MainnetImaProject as MainnetIma +from .skale_manager import SkaleManagerProject as SkaleManager + +__all__ = ["MainnetIma", "SkaleManager"] diff --git a/python/src/skale_contracts/projects/ima.py b/python/src/skale_contracts/projects/ima.py index f34dca2..2010d1c 100644 --- a/python/src/skale_contracts/projects/ima.py +++ b/python/src/skale_contracts/projects/ima.py @@ -3,8 +3,10 @@ from __future__ import annotations from typing import cast, TYPE_CHECKING -from ..instance import Instance, DEFAULT_GET_VERSION_FUNCTION -from ..project import Project +from skale_contracts.instance import Instance, DEFAULT_GET_VERSION_FUNCTION +from skale_contracts.project import Project + +from .skale_manager import CONTRACT_MANAGER_ABI if TYPE_CHECKING: @@ -36,8 +38,42 @@ def github_repo(self) -> str: class MainnetImaInstance(ImaInstance): """Represents IMA instance on mainnet""" + + def __init__(self, project: Project, address: Address) -> None: + super().__init__(project, address) + self._contract_manager: Contract | None = None + def get_contract_address(self, name: str) -> Address: - raise NotImplementedError("get_contract_address") + if name == 'MessageProxyForMainnet': + return self.address + if name == 'CommunityPool': + return cast( + Address, + self.get_contract("MessageProxyForMainnet").functions.communityPool().call() + ) + if name == 'Linker': + return cast( + Address, + self.get_contract("MessageProxyForMainnet").functions.linker().call() + ) + return cast( + Address, + self.contract_manager.functions.getContract(name).call() + ) + + @property + def contract_manager(self) -> Contract: + """ContractManager contract of a skale-manager instance associated with the IMA""" + if self._contract_manager is None: + self._contract_manager = self.web3.eth.contract( + address=cast( + Address, + self.get_contract("MessageProxyForMainnet") + .functions.contractManagerOfSkaleManager().call() + ), + abi=CONTRACT_MANAGER_ABI + ) + return self._contract_manager class MainnetImaProject(ImaProject): @@ -47,4 +83,4 @@ def create_instance(self, address: Address) -> Instance: return MainnetImaInstance(self, address) def get_abi_filename(self, version: str) -> str: - return f'ima-{version}-abi.json' + return f'mainnet-ima-{version}-abi.json' diff --git a/python/src/skale_contracts/projects/skale_manager.py b/python/src/skale_contracts/projects/skale_manager.py index 1d74ca1..c79ae7e 100644 --- a/python/src/skale_contracts/projects/skale_manager.py +++ b/python/src/skale_contracts/projects/skale_manager.py @@ -3,8 +3,8 @@ from __future__ import annotations from typing import cast, TYPE_CHECKING -from ..instance import Instance, DEFAULT_GET_VERSION_FUNCTION -from ..project import Project +from skale_contracts.instance import Instance, DEFAULT_GET_VERSION_FUNCTION +from skale_contracts.project import Project if TYPE_CHECKING: