Example #1
0
    def get_proxy_contract(self, registry: BaseContractRegistry,
                           target_address: str,
                           proxy_name: str) -> VersionedContract:

        # Lookup proxies; Search for a registered proxy that targets this contract record
        records = registry.search(contract_name=proxy_name)

        dispatchers = list()
        for name, version, address, abi in records:
            proxy_contract = self.client.w3.eth.contract(
                abi=abi,
                address=address,
                version=version,
                ContractFactoryClass=self._CONTRACT_FACTORY)

            # Read this dispatchers target address from the blockchain
            proxy_live_target_address = proxy_contract.functions.target().call(
            )

            if proxy_live_target_address == target_address:
                dispatchers.append(proxy_contract)

        if len(dispatchers) > 1:
            message = f"Multiple Dispatcher deployments are targeting {target_address}"
            raise self.InterfaceError(message)

        try:
            return dispatchers[0]
        except IndexError:
            raise self.UnknownContract(
                f"No registered Dispatcher deployments target {target_address}"
            )
Example #2
0
    def deploy_contract(
            self,
            deployer_address: str,
            registry: BaseContractRegistry,
            contract_name: str,
            *constructor_args,
            enroll: bool = True,
            gas_limit: int = None,
            confirmations: int = 0,
            contract_version: str = 'latest',
            **constructor_kwargs) -> Tuple[VersionedContract, TxReceipt]:
        """
        Retrieve compiled interface data from the cache and
        return an instantiated deployed contract
        """

        #
        # Build the deployment transaction #
        #

        deploy_transaction = dict()
        if gas_limit:
            deploy_transaction.update({'gas': gas_limit})

        pprint_args = ', '.join(
            list(map(str, constructor_args)) +
            list(f"{k}={v}" for k, v in constructor_kwargs.items()))

        contract_factory = self.get_contract_factory(
            contract_name=contract_name, version=contract_version)
        self.log.info(
            f"Deploying contract {contract_name}:{contract_factory.version} with "
            f"deployer address {deployer_address} "
            f"and parameters {pprint_args}")

        constructor_function = contract_factory.constructor(
            *constructor_args, **constructor_kwargs)
        constructor_calldata = encode_constructor_arguments(
            self.client.w3, constructor_function, *constructor_args,
            **constructor_kwargs)
        if constructor_calldata:
            self.log.info(f"Constructor calldata: {constructor_calldata}")

        #
        # Transmit the deployment tx #
        #

        receipt = self.send_transaction(contract_function=constructor_function,
                                        sender_address=deployer_address,
                                        payload=deploy_transaction,
                                        confirmations=confirmations)

        # Success
        address = receipt['contractAddress']
        self.log.info(
            f"Confirmed {contract_name}:{contract_factory.version} deployment: new address {address}"
        )

        #
        # Instantiate & Enroll contract
        #

        contract = self.client.w3.eth.contract(
            address=address,
            abi=contract_factory.abi,
            version=contract_factory.version,
            ContractFactoryClass=self._CONTRACT_FACTORY)

        if enroll is True:
            registry.enroll(contract_name=contract_name,
                            contract_address=contract.address,
                            contract_abi=contract.abi,
                            contract_version=contract.version)

        return contract, receipt  # receipt
Example #3
0
    def get_contract_by_name(
            self,
            registry: BaseContractRegistry,
            contract_name: str,
            contract_version: str = None,
            enrollment_version: Union[int, str] = None,
            proxy_name: str = None,
            use_proxy_address: bool = True) -> VersionedContract:
        """
        Instantiate a deployed contract from registry data,
        and assimilate it with its proxy if it is upgradeable.
        """
        target_contract_records = registry.search(
            contract_name=contract_name, contract_version=contract_version)

        if not target_contract_records:
            raise self.UnknownContract(
                f"No such contract records with name {contract_name}:{contract_version}."
            )

        if proxy_name:

            # Lookup proxies; Search for a published proxy that targets this contract record
            proxy_records = registry.search(contract_name=proxy_name)

            results = list()
            for proxy_name, proxy_version, proxy_address, proxy_abi in proxy_records:
                proxy_contract = self.client.w3.eth.contract(
                    abi=proxy_abi,
                    address=proxy_address,
                    version=proxy_version,
                    ContractFactoryClass=self._CONTRACT_FACTORY)

                # Read this dispatcher's target address from the blockchain
                proxy_live_target_address = proxy_contract.functions.target(
                ).call()
                for target_name, target_version, target_address, target_abi in target_contract_records:

                    if target_address == proxy_live_target_address:
                        if use_proxy_address:
                            triplet = (proxy_address, target_version,
                                       target_abi)
                        else:
                            triplet = (target_address, target_version,
                                       target_abi)
                    else:
                        continue

                    results.append(triplet)

            if len(results) > 1:
                address, _version, _abi = results[0]
                message = "Multiple {} deployments are targeting {}".format(
                    proxy_name, address)
                raise self.InterfaceError(message.format(contract_name))

            else:
                try:
                    selected_address, selected_version, selected_abi = results[
                        0]
                except IndexError:
                    raise self.UnknownContract(
                        f"There are no Dispatcher records targeting '{contract_name}':{contract_version}"
                    )

        else:
            # TODO: use_proxy_address doesnt' work in this case. Should we raise if used?

            # NOTE: 0 must be allowed as a valid version number
            if len(target_contract_records) != 1:
                if enrollment_version is None:
                    m = f"{len(target_contract_records)} records enrolled " \
                        f"for contract {contract_name}:{contract_version} " \
                        f"and no version index was supplied."
                    raise self.InterfaceError(m)
                enrollment_version = self.__get_enrollment_version_index(
                    name=contract_name,
                    contract_version=contract_version,
                    version_index=enrollment_version,
                    enrollments=len(target_contract_records))

            else:
                enrollment_version = -1  # default

            _contract_name, selected_version, selected_address, selected_abi = target_contract_records[
                enrollment_version]

        # Create the contract from selected sources
        unified_contract = self.client.w3.eth.contract(
            abi=selected_abi,
            address=selected_address,
            version=selected_version,
            ContractFactoryClass=self._CONTRACT_FACTORY)

        return unified_contract
Example #4
0
    def deploy_contract(
            self,
            deployer_address: str,
            registry: BaseContractRegistry,
            contract_name: str,
            *constructor_args,
            enroll: bool = True,
            gas_limit: int = None,
            contract_version: str = 'latest',
            **constructor_kwargs) -> Tuple[VersionedContract, dict]:
        """
        Retrieve compiled interface data from the cache and
        return an instantiated deployed contract
        """

        if not is_checksum_address(deployer_address):
            raise ValueError(
                f"{deployer_address} is not a valid EIP-55 checksum address.")

        #
        # Build the deployment transaction #
        #

        deploy_transaction = dict()
        if gas_limit:
            deploy_transaction.update({'gas': gas_limit})

        pprint_args = str(tuple(constructor_args))
        pprint_args = pprint_args.replace("{", "{{").replace("}",
                                                             "}}")  # See #724

        contract_factory = self.get_contract_factory(
            contract_name=contract_name, version=contract_version)
        self.log.info(
            f"Deploying contract {contract_name}:{contract_factory.version} with "
            f"deployer address {deployer_address} "
            f"and parameters {pprint_args}")

        transaction_function = contract_factory.constructor(
            *constructor_args, **constructor_kwargs)

        #
        # Transmit the deployment tx #
        #

        receipt = self.send_transaction(contract_function=transaction_function,
                                        sender_address=deployer_address,
                                        payload=deploy_transaction)

        #
        # Verify deployment success
        #

        # Success
        address = receipt['contractAddress']
        self.log.info(
            f"Confirmed {contract_name}:{contract_factory.version} deployment: new address {address}"
        )

        #
        # Instantiate & Enroll contract
        #

        contract = self.client.w3.eth.contract(
            address=address,
            abi=contract_factory.abi,
            version=contract_factory.version,
            ContractFactoryClass=self._contract_factory)

        if enroll is True:
            registry.enroll(contract_name=contract_name,
                            contract_address=contract.address,
                            contract_abi=contract.abi,
                            contract_version=contract.version)

        return contract, receipt  # receipt
Example #5
0
    def get_contract_by_name(
            self,
            registry: BaseContractRegistry,
            name: str,
            version: int = None,
            proxy_name: str = None,
            use_proxy_address: bool = True) -> Union[Contract, List[tuple]]:
        """
        Instantiate a deployed contract from registry data,
        and assimilate it with its proxy if it is upgradeable,
        or return all registered records if use_proxy_address is False.
        """
        target_contract_records = registry.search(contract_name=name)

        if not target_contract_records:
            raise self.UnknownContract(
                f"No such contract records with name {name}.")

        if proxy_name:

            # Lookup proxies; Search for a published proxy that targets this contract record
            proxy_records = registry.search(contract_name=proxy_name)

            results = list()
            for proxy_name, proxy_addr, proxy_abi in proxy_records:
                proxy_contract = self.client.w3.eth.contract(
                    abi=proxy_abi,
                    address=proxy_addr,
                    ContractFactoryClass=self._contract_factory)

                # Read this dispatcher's target address from the blockchain
                proxy_live_target_address = proxy_contract.functions.target(
                ).call()
                for target_name, target_addr, target_abi in target_contract_records:

                    if target_addr == proxy_live_target_address:
                        if use_proxy_address:
                            pair = (proxy_addr, target_abi)
                        else:
                            pair = (target_addr, target_abi)
                    else:
                        continue

                    results.append(pair)

            if len(results) > 1:
                address, abi = results[0]
                message = "Multiple {} deployments are targeting {}".format(
                    proxy_name, address)
                raise self.InterfaceError(message.format(name))

            else:
                try:
                    selected_address, selected_abi = results[0]
                except IndexError:
                    raise self.UnknownContract(
                        f"There are no Dispatcher records targeting '{name}'")

        else:
            # NOTE: 0 must be allowed as a valid version number
            if len(target_contract_records) != 1:
                if version is None:
                    m = f"{len(target_contract_records)} records enrolled for contract {name} " \
                        f"and no version index was supplied."
                    raise self.InterfaceError(m)
                version = self.__get_version_index(
                    name=name,
                    version_index=version,
                    enrollments=len(target_contract_records))

            else:
                version = -1  # default

            _target_contract_name, selected_address, selected_abi = target_contract_records[
                version]

        # Create the contract from selected sources
        unified_contract = self.client.w3.eth.contract(
            abi=selected_abi,
            address=selected_address,
            ContractFactoryClass=self._contract_factory)

        return unified_contract