def test_unknown_language(): with pytest.raises(UnsupportedLanguage): compiler.generate_input_json({"foo": ""}, language="Bar") with pytest.raises(UnsupportedLanguage): compiler.compile_from_input_json({"language": "FooBar"}) with pytest.raises(UnsupportedLanguage): compiler.generate_build_json({"language": "BarBaz"}, {})
def from_explorer( cls, address: str, as_proxy_for: Optional[str] = None, owner: Optional[AccountsType] = None, silent: bool = False, ) -> "Contract": """ Create a new `Contract` object with source code queried from a block explorer. Arguments --------- address : str Address where the contract is deployed. as_proxy_for : str, optional Address of the implementation contract, if `address` is a proxy contract. The generated object will send transactions to `address`, but use the ABI and NatSpec of `as_proxy_for`. This field is only required when the block explorer API does not provide an implementation address. owner : Account, optional Contract owner. If set, transactions without a `from` field will be performed using this account. """ address = _resolve_address(address) data = _fetch_from_explorer(address, "getsourcecode", silent) is_verified = bool(data["result"][0].get("SourceCode")) if is_verified: abi = json.loads(data["result"][0]["ABI"]) name = data["result"][0]["ContractName"] else: # if the source is not available, try to fetch only the ABI try: data_abi = _fetch_from_explorer(address, "getabi", True) except ValueError as exc: _unverified_addresses.add(address) raise exc abi = json.loads(data_abi["result"].strip()) name = "UnknownContractName" warnings.warn( f"{address}: Was able to fetch the ABI but not the source code. " "Some functionality will not be available.", BrownieCompilerWarning, ) if as_proxy_for is None and data["result"][0].get("Implementation"): as_proxy_for = _resolve_address( data["result"][0]["Implementation"]) # if this is a proxy, fetch information for the implementation contract if as_proxy_for is not None: implementation_contract = Contract.from_explorer(as_proxy_for) abi = implementation_contract._build["abi"] if not is_verified: return cls.from_abi(name, address, abi, owner) try: version = Version( data["result"][0]["CompilerVersion"].lstrip("v")).truncate() except Exception: version = Version("0.0.0") if version < Version("0.4.22") or ( # special case for OSX because installing 0.4.x versions is problematic sys.platform == "darwin" and version < Version("0.5.0") and f"v{version}" not in solcx.get_installed_solc_versions()): if not silent: warnings.warn( f"{address}: target compiler '{data['result'][0]['CompilerVersion']}' is " "unsupported by Brownie. Some functionality will not be available.", BrownieCompilerWarning, ) return cls.from_abi(name, address, abi, owner) optimizer = { "enabled": bool(int(data["result"][0]["OptimizationUsed"])), "runs": int(data["result"][0]["Runs"]), } evm_version = data["result"][0].get("EVMVersion", "Default") if evm_version == "Default": evm_version = None if data["result"][0]["SourceCode"].startswith("{"): # source was verified using compiler standard JSON input_json = json.loads(data["result"][0]["SourceCode"][1:-1]) sources = { k: v["content"] for k, v in input_json["sources"].items() } evm_version = input_json["settings"].get("evmVersion", evm_version) compiler.set_solc_version(str(version)) input_json.update( compiler.generate_input_json(sources, optimizer=optimizer, evm_version=evm_version)) output_json = compiler.compile_from_input_json(input_json) build_json = compiler.generate_build_json(input_json, output_json) else: # source was submitted as a single flattened file sources = { f"{name}-flattened.sol": data["result"][0]["SourceCode"] } build_json = compiler.compile_and_format(sources, solc_version=str(version), optimizer=optimizer, evm_version=evm_version) build_json = build_json[name] if as_proxy_for is not None: build_json.update( abi=abi, natspec=implementation_contract._build.get("natspec")) if not _verify_deployed_code(address, build_json["deployedBytecode"], build_json["language"]): warnings.warn( f"{address}: Locally compiled and on-chain bytecode do not match!", BrownieCompilerWarning, ) del build_json["pcMap"] self = cls.__new__(cls) _ContractBase.__init__(self, None, build_json, sources) # type: ignore _DeployedContractBase.__init__(self, address, owner) _add_deployment(self) return self