def test_namespace_collision(tester, build): build["abi"].append({ "constant": False, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" }, { "name": "_test", "type": "uint256" }, ], "name": "bytecode", "outputs": [{ "name": "", "type": "bool" }], "payable": False, "stateMutability": "nonpayable", "type": "function", }) with pytest.raises(AttributeError): Contract.from_abi(None, tester.address, build["abi"])
def test_abi_deployment_enabled_by_default(network, build): network.connect("mainnet") address = "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e" Contract.from_abi("abiTester", address, build["abi"]) assert _get_deployment(address) != (None, None) # cleanup Contract.remove_deployment(address)
def test_overloaded(testproject, tester, build): build["abi"].append( { "constant": False, "inputs": [ {"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}, {"name": "_test", "type": "uint256"}, ], "name": "revertStrings", "outputs": [{"name": "", "type": "bool"}], "payable": False, "stateMutability": "nonpayable", "type": "function", } ) del testproject.BrownieTester[0] c = Contract.from_abi(None, tester.address, build["abi"]) fn = c.revertStrings assert type(fn) == OverloadedMethod assert len(fn) == 2 assert type(fn["uint"]) == ContractTx assert fn["address", "uint256", "uint256"] == fn["address, uint256, uint256"] assert fn["uint"] == fn["uint256"] assert fn["uint"] != fn["address, uint256, uint256"] repr(fn)
def test_comparison(testproject, tester): del testproject.BrownieTester[0] assert tester != 123 assert tester == str(tester.address) assert tester == Contract.from_abi("BrownieTester", tester.address, tester.abi) repr(tester)
def _load_deployments(self) -> None: if CONFIG.network_type != "live" and not CONFIG.settings["dev_deployment_artifacts"]: return chainid = CONFIG.active_network["chainid"] if CONFIG.network_type == "live" else "dev" path = self._build_path.joinpath(f"deployments/{chainid}") path.mkdir(exist_ok=True) deployments = list(path.glob("*.json")) deployments.sort(key=lambda k: k.stat().st_mtime) deployment_map = self._load_deployment_map() for build_json in deployments: with build_json.open() as fp: build = json.load(fp) contract_name = build["contractName"] if contract_name not in self._containers: build_json.unlink() continue if "pcMap" in build: contract = ProjectContract(self, build, build_json.stem) else: contract = Contract.from_abi( # type: ignore contract_name, build_json.stem, build["abi"] ) contract._project = self container = self._containers[contract_name] _add_contract(contract) container._contracts.append(contract) # update deployment map for the current chain instances = deployment_map.setdefault(chainid, {}).setdefault(contract_name, []) if build_json.stem in instances: instances.remove(build_json.stem) instances.insert(0, build_json.stem) self._save_deployment_map(deployment_map)
def __enter__(self) -> "Multicall": """Enter the Context Manager and substitute `ContractCall.__call__`""" # we set the code objects on ContractCall class so we can grab them later active_network = CONFIG.active_network if "multicall2" in active_network: self.address = active_network["multicall2"] elif "cmd" in active_network: deployment = self.deploy({"from": accounts[0]}) self.address = deployment.address # type: ignore self.block_number = deployment.tx.block_number # type: ignore self.block_number = self.block_number or web3.eth.get_block_number() if self.address is None: raise ContractNotFound( "Must set Multicall address via `brownie.multicall(address=...)`" ) elif not web3.eth.get_code(self.address, block_identifier=self.block_number): raise ContractNotFound( f"Multicall at address {self.address} does not exist at block {self.block_number}" ) self._contract = Contract.from_abi("Multicall", self.address, MULTICALL2_ABI) getattr(ContractCall, "__multicall")[get_ident()] = self
def force_deploy(deployer, contract, *deploy_args): """ Brownieだとエラーが発生するコントラクトを強制的にデプロイする。 Brownieでは brownie.network.contract.Contract に定義済みプロパティと 同名の公開関数を持つコントラクトはエラーとなりデプロイできない。 この関数はBrownieを利用せずweb3で直接デプロイすることでエラーを回避する。 なお、この関数により生成したContractオブジェクトではBrownieが提供する一部のDebug機能は使用できない。 使用例 >>> returned_contract = force_deploy(deployer, contract, *deploy_args) >>> # 普通の関数はそのまま使用できる。 >>> returned_contract.nameOfFunction.transact({'from': deployer}) >>> # エラーの原因となる関数は `.functions` 経由でアクセスする。 >>> returned_contract.functions.signatures() >>> returned_contract.functions.remove.transact({'from': deployer}) :param deployer: コントラクトをデプロイするアカウント :param contract: Brownieのコントラクトオブジェクト :param deploy_args: コントラクトのコンストラクタ引数 :return: Brownieのコントラクトインスタンス """ # 引数の型変換 (Note: web3.pyとBrownieでは型変換規則が異なる) constructor_abi = list(filter(lambda entry: entry['type'] == 'constructor', contract.abi)) if len(constructor_abi) == 1: deploy_args = brownie.convert.normalize.format_input(constructor_abi[0], deploy_args) # web3を用いてデプロイする web3_contract = web3.eth.contract(abi=contract.abi, bytecode=contract.bytecode) txn_hash = web3_contract.constructor(*deploy_args).transact({'from': deployer.address}) receipt = web3.eth.waitForTransactionReceipt(txn_hash) contract_address = receipt['contractAddress'] # Brownieでエラーを発生させるメソッドを取り除いたABIを作成する # このABIを用いることでBrownieのContractオブジェクトが作成できるようになる brownie_safe_abi = [] excluded_function_abi = [] for abi_entry in contract.abi: if abi_entry['type'] == 'function' and abi_entry['name'] in _BROWNIE_RESERVED_NAMES: excluded_function_abi.append(abi_entry) else: brownie_safe_abi.append(abi_entry) contract_name = _resolve_contract_name(contract) + '__brownie_utils' brownie_contract = Contract.from_abi(contract_name, contract_address, brownie_safe_abi) # ABIから削除したメソッドを復元する # (オーバロードには未対応) brownie_contract.functions = _BrownieUnsafeFunctionContainer() for abi_entry in excluded_function_abi: name = abi_entry['name'] if _is_constant(abi_entry): recovered_function = ContractCall(contract_address, abi_entry, name, None) else: recovered_function = ContractTx(contract_address, abi_entry, name, None) setattr(brownie_contract.functions, name, recovered_function) return brownie_contract
def diamond_cut(diamond: ProjectContract, DiamondCut: ContractContainer) -> Contract: """Diamond contract with Diamond Cut Facet functions available. Note: Debugging is not available since the bytecodes of diamond and DiamondCut do not match. https://eth-brownie.readthedocs.io/en/stable/api-network.html#ContractContainer.at """ return Contract.from_abi("Diamond Cut", diamond.address, DiamondCut.abi)
def test_alias_in_development(tester): contract = Contract.from_abi("BrownieTester", tester.address, tester.abi) with pytest.raises(ValueError): contract.set_alias("testalias")
def test_deprecated_init_abi(tester): old = Contract("BrownieTester", tester.address, tester.abi) assert old == Contract.from_abi("BrownieTester", tester.address, tester.abi)
def test_contractabi_replace_contract(testproject, tester): Contract.from_abi("BrownieTester", tester.address, tester.abi) del testproject.BrownieTester[0] Contract.from_abi("BrownieTester", tester.address, tester.abi) Contract.from_abi("BrownieTester", tester.address, tester.abi)
def test_deprecated_init_abi(tester): with pytest.warns(DeprecationWarning): old = Contract("BrownieTester", tester.address, tester.abi) assert old == Contract.from_abi("BrownieTester", tester.address, tester.abi)
def test_lookup(network): network.connect("mainnet") c = Contract.from_abi("Test", "ens.snakecharmers.eth", []) assert c == "0x808B53bF4D70A24bA5cb720D37A4835621A9df00" assert c == "ens.snakecharmers.eth"
def test_unset(network): network.connect("mainnet") with pytest.raises(UnsetENSName): Contract.from_abi("Test", "pleasedonot.buythisoryouwill.breakmytests.eth", [])
def test_invalid(network): network.connect("mainnet") with pytest.raises(InvalidName): Contract.from_abi("Test", "this-is-not-an-ENS-address,isit?.eth", [])
def test_abi_deployment_disabled(network, build): network.connect("mainnet") address = "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e" Contract.from_abi("abiTester", address, build["abi"], persist=False) assert _get_deployment(address) == (None, None)