def test_evm_versions(tmp_path): # should compile differently because of SELFBALANCE code = """ @public def foo() -> uint256: return self.balance """ bar_path = tmp_path.joinpath('bar.vy') with bar_path.open('w') as fp: fp.write(code) byzantium_bytecode = compile_files( [bar_path], output_formats=['bytecode'], evm_version="byzantium" )[str(bar_path)]['bytecode'] istanbul_bytecode = compile_files( [bar_path], output_formats=['bytecode'], evm_version="istanbul" )[str(bar_path)]['bytecode'] assert byzantium_bytecode != istanbul_bytecode # SELFBALANCE opcode is 0x47 assert "47" not in byzantium_bytecode assert "47" in istanbul_bytecode
def test_import_parent_folder(tmp_path, assert_compile_failed): tmp_path.joinpath('contracts').mkdir() foo_path = tmp_path.joinpath('contracts/foo.vy') with foo_path.open('w') as fp: fp.write(FOO_CODE.format("from .. import Bar")) with tmp_path.joinpath('Bar.vy').open('w') as fp: fp.write(BAR_CODE) assert compile_files([foo_path], ['combined_json'], root_folder=tmp_path) # Cannot perform relative import outside of base folder with pytest.raises(FileNotFoundError): compile_files([foo_path], ['combined_json'], root_folder=tmp_path.joinpath('contracts'))
def test_local_namespace(tmp_path): # interface code namespaces should be isolated # all of these contract should be able to compile together codes = [ "import foo as FooBar", "import bar as FooBar", "import foo as BarFoo", "import bar as BarFoo", ] struct_def = """ struct FooStruct: foo_: uint256 """ compile_paths = [] for i, code in enumerate(codes): code += struct_def path = tmp_path.joinpath(f"code{i}.vy") with path.open("w") as fp: fp.write(code) compile_paths.append(path) for file_name in ("foo.vy", "bar.vy"): with tmp_path.joinpath(file_name).open("w") as fp: fp.write(BAR_CODE) assert compile_files(compile_paths, ["combined_json"], root_folder=tmp_path)
def test_derived_interface_imports(import_stmt_baz, import_stmt_foo, tmp_path): # contracts-as-interfaces should be able to contain import statements baz_code = """ {} @public def foo(a: address) -> uint256: return Foo(a).foo() @public def bar(_foo: address, _bar: address) -> uint256: return Foo(_foo).bar(_bar) """.format(import_stmt_baz) with tmp_path.joinpath('Foo.vy').open('w') as fp: fp.write(FOO_CODE.format(import_stmt_foo)) with tmp_path.joinpath('Bar.vy').open('w') as fp: fp.write(BAR_CODE) baz_path = tmp_path.joinpath('Baz.vy') with baz_path.open('w') as fp: fp.write(baz_code) assert compile_files([baz_path], ['combined_json'], root_folder=tmp_path)
def test_derived_interface_imports(import_stmt_baz, import_stmt_foo, tmp_path): # contracts-as-interfaces should be able to contain import statements baz_code = f""" {import_stmt_baz} struct FooStruct: foo_: uint256 @external def foo(a: address) -> FooStruct: return Foo(a).foo() @external def bar(_foo: address, _bar: address) -> FooStruct: return Foo(_foo).bar(_bar) """ with tmp_path.joinpath("Foo.vy").open("w") as fp: fp.write(FOO_CODE.format(import_stmt_foo, "Bar")) with tmp_path.joinpath("Bar.vy").open("w") as fp: fp.write(BAR_CODE) baz_path = tmp_path.joinpath("Baz.vy") with baz_path.open("w") as fp: fp.write(baz_code) assert compile_files([baz_path], ["combined_json"], root_folder=tmp_path)
def create_contract(w3, path): wd = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(wd, os.pardir, path)) as f: source = f.read() root_path = os.path.join(wd, os.pardir) source_path = os.path.join(root_path, path) out = compile_files([source_path], ['abi', 'bytecode'], root_path)[path] return w3.eth.contract(abi=out['abi'], bytecode=out['bytecode'])
def test_compile_outside_root_path(tmp_path): foo_path = tmp_path.joinpath('foo.vy') with foo_path.open('w') as fp: fp.write(FOO_CODE.format('import bar as Bar')) bar_path = tmp_path.joinpath('bar.vy') with bar_path.open('w') as fp: fp.write(BAR_CODE) assert compile_files([foo_path, bar_path], ['combined_json'], root_folder=".")
def test_compile_outside_root_path(tmp_path): foo_path = tmp_path.joinpath("foo.vy") with foo_path.open("w") as fp: fp.write(FOO_CODE.format("import bar as Bar", "Bar")) bar_path = tmp_path.joinpath("bar.vy") with bar_path.open("w") as fp: fp.write(BAR_CODE) assert compile_files([foo_path, bar_path], ["combined_json"], root_folder=".")
def test_import_same_folder(import_stmt, alias, tmp_path): tmp_path.joinpath("contracts").mkdir() foo_path = tmp_path.joinpath("contracts/foo.vy") with foo_path.open("w") as fp: fp.write(FOO_CODE.format(import_stmt, alias)) with tmp_path.joinpath("contracts/Bar.vy").open("w") as fp: fp.write(BAR_CODE) assert compile_files([foo_path], ["combined_json"], root_folder=tmp_path)
def test_import_same_folder(import_stmt, tmp_path): tmp_path.joinpath('contracts').mkdir() foo_path = tmp_path.joinpath('contracts/foo.vy') with foo_path.open('w') as fp: fp.write(FOO_CODE.format(import_stmt)) with tmp_path.joinpath('contracts/Bar.vy').open('w') as fp: fp.write(BAR_CODE) assert compile_files([foo_path], ['combined_json'], root_folder=tmp_path)
def test_combined_json_keys(tmp_path): bar_path = tmp_path.joinpath('bar.vy') with bar_path.open('w') as fp: fp.write("") combined_keys = { 'bytecode', 'bytecode_runtime', 'abi', 'source_map', 'method_identifiers' } compile_data = compile_files([bar_path], ['combined_json'], root_folder=tmp_path) assert set(compile_data.keys()) == {'bar.vy', 'version'} assert set(compile_data['bar.vy'].keys()) == combined_keys
def test_combined_json_keys(tmp_path): bar_path = tmp_path.joinpath("bar.vy") with bar_path.open("w") as fp: fp.write("") combined_keys = { "bytecode", "bytecode_runtime", "abi", "source_map", "method_identifiers", "userdoc", "devdoc", } compile_data = compile_files([bar_path], ["combined_json"], root_folder=tmp_path) assert set(compile_data.keys()) == {"bar.vy", "version"} assert set(compile_data["bar.vy"].keys()) == combined_keys
def test_import_self_interface(import_stmt, tmp_path): # a contract can access it's derived interface by importing itself code = """ {} @public def know_thyself(a: address) -> uint256: return Meta(a).be_known() @public def be_known() -> uint256: return 42 """.format(import_stmt) tmp_path.joinpath('contracts').mkdir() meta_path = tmp_path.joinpath('contracts/Meta.vy') with meta_path.open('w') as fp: fp.write(code) assert compile_files([meta_path], ['combined_json'], root_folder=tmp_path)
def test_import_self_interface(import_stmt, tmp_path): # a contract can access it's derived interface by importing itself code = f""" {import_stmt} @external def know_thyself(a: address) -> uint256: return Meta(a).be_known() @external def be_known() -> uint256: return 42 """ tmp_path.joinpath("contracts").mkdir() meta_path = tmp_path.joinpath("contracts/Meta.vy") with meta_path.open("w") as fp: fp.write(code) assert compile_files([meta_path], ["combined_json"], root_folder=tmp_path)
def test_local_namespace(tmp_path): # interface code namespaces should be isolated # all of these contract should be able to compile together codes = [ "import foo as FooBar", "import bar as FooBar", "import foo as BarFoo", "import bar as BarFoo", ] compile_paths = [] for i, code in enumerate(codes): path = tmp_path.joinpath(f'code{i}.vy') with path.open('w') as fp: fp.write(code) compile_paths.append(path) for file_name in ('foo.vy', 'bar.vy'): with tmp_path.joinpath(file_name).open('w') as fp: fp.write(BAR_CODE) assert compile_files(compile_paths, ['combined_json'], root_folder=tmp_path)
def test_invalid_root_path(): with pytest.raises(FileNotFoundError): compile_files([], [], root_folder="path/that/does/not/exist")
def compile(self): # check what kind of contract we are compiling if self.contract_filepath.endswith('.vy'): contract_type = 'vyper' else: contract_type = 'solidity' print( f'Compiling {contract_type.title()} contract {self.name} from {self.contract_filepath}' ) contract_filename = os.path.basename(self.contract_filepath) # load the contract source code from file with open(self.contract_filepath, 'r') as f: contract_content = f.read() if contract_type == 'solidity': from solc import compile_standard # compile the contract source code compiled_sol = compile_standard({ "language": "Solidity", "sources": { contract_filename: { "content": contract_content } }, "settings": { "outputSelection": { "*": { "*": [ "metadata", "evm.bytecode", "evm.bytecode.sourceMap" ] } } } }) # the contract ABI, used by web3 to interact with the contract self.abi = json.loads(compiled_sol['contracts'][contract_filename][ self.name]['metadata'])['output']['abi'] # the compiled bytecode, used for deploying the contract self.bytecode = compiled_sol['contracts'][contract_filename][ self.name]['evm']['bytecode']['object'] if contract_type == 'vyper': from vyper.cli.vyper_compile import compile_files output = compile_files(input_files=[self.contract_filepath], output_formats=['abi', 'bytecode']) self.abi = output[self.contract_filepath]['abi'] self.bytecode = output[self.contract_filepath]['bytecode'] # optionally save the ABI to file so that it can be re-used if self.abi_filepath: print(f'saving ABI to {self.abi_filepath}') with open(self.abi_filepath, 'w') as f: json.dump(self.abi, f) # optionally save the bytecode to file so that it can be re-used if self.bytecode_filepath: print(f'saving bytecode to {self.bytecode_filepath}') with open(self.bytecode_filepath, 'w') as f: f.write(self.bytecode)