diff --git a/boa/contracts/vvm/vvm_contract.py b/boa/contracts/vvm/vvm_contract.py index 6a136dea..195892b9 100644 --- a/boa/contracts/vvm/vvm_contract.py +++ b/boa/contracts/vvm/vvm_contract.py @@ -1,18 +1,36 @@ import re from functools import cached_property from pathlib import Path -from typing import Optional +from typing import Any, Optional +import vvm from vyper.utils import method_id from boa.contracts.abi.abi_contract import ABIContract, ABIContractFactory, ABIFunction from boa.environment import Env from boa.rpc import to_bytes -from boa.util import cached_vvm from boa.util.abi import Address +from boa.util.disk_cache import get_disk_cache from boa.util.eip5202 import generate_blueprint_bytecode +def _compile_source(*args, **kwargs) -> Any: + """ + Compile Vyper source code via the VVM. + When a disk cache is available, the result of the compilation is cached. + """ + disk_cache = get_disk_cache() + + def _compile(): + return vvm.compile_source(*args, **kwargs) + + if disk_cache is None: + return _compile() + + cache_key = f"{args}{kwargs}" + return disk_cache.caching_lookup(cache_key, _compile) + + class VVMDeployer(ABIContractFactory): """ A deployer that uses the Vyper Version Manager (VVM). @@ -46,9 +64,8 @@ def bytecode(self): return to_bytes(self.compiler_output["bytecode"]) @classmethod - def from_compiler_output( + def from_source_code( cls, - compiler_output: dict, source_code: str, vyper_version: str, filename: Optional[str] = None, @@ -56,6 +73,9 @@ def from_compiler_output( ): if name is None: name = Path(filename).stem if filename is not None else "" + compiled_src = _compile_source(source_code, vyper_version=vyper_version) + compiler_output = compiled_src[""] + return cls(name, compiler_output, source_code, vyper_version, filename) @cached_property @@ -183,17 +203,18 @@ def internal(self): recompiled. """ - def internal(): + # an object with working setattr + def _obj(): return None - result = cached_vvm.compile_source( + result = _compile_source( self.source_code, vyper_version=self.vyper_version, output_format="metadata" )["function_info"] for fn_name, meta in result.items(): if meta["visibility"] == "internal": function = VVMInternalFunction(meta, self) - setattr(internal, function.name, function) - return internal + setattr(_obj, function.name, function) + return _obj class _VVMInternal(ABIFunction): @@ -212,9 +233,7 @@ def _override_bytecode(self) -> bytes: def _compiler_output(self): assert isinstance(self.contract, VVMContract) # help mypy source = "\n".join((self.contract.source_code, self.source_code)) - compiled = cached_vvm.compile_source( - source, vyper_version=self.contract.vyper_version - ) + compiled = _compile_source(source, vyper_version=self.contract.vyper_version) return compiled[""] @property @@ -331,9 +350,9 @@ class VVMInjectedFunction(_VVMInternal): It will temporarily change the bytecode at the contract's address. """ - def __init__(self, code: str, contract: VVMContract): + def __init__(self, source_code: str, contract: VVMContract): self.contract = contract - self.code = code + self._source_code = source_code abi = [i for i in self._compiler_output["abi"] if i not in contract.abi] if len(abi) != 1: err = "Expected exactly one new ABI entry after injecting function. " diff --git a/boa/interpret.py b/boa/interpret.py index 46faeb2a..d5601bcb 100644 --- a/boa/interpret.py +++ b/boa/interpret.py @@ -33,7 +33,6 @@ from boa.environment import Env from boa.explorer import Etherscan, get_etherscan from boa.rpc import json -from boa.util import cached_vvm from boa.util.abi import Address from boa.util.disk_cache import get_disk_cache @@ -231,8 +230,7 @@ def loads_partial( version = vvm.detect_vyper_version_from_source(source_code) if version is not None and version != Version(vyper.__version__): - # TODO: pass name to loads_partial_vvm, not filename - return _loads_partial_vvm(source_code, str(version), filename) + return _loads_partial_vvm(source_code, str(version), filename, name) compiler_args = compiler_args or {} @@ -261,11 +259,7 @@ def _loads_partial_vvm( # install the requested version if not already installed vvm.install_vyper(version=version) - compiled_src = cached_vvm.compile_source(source_code, vyper_version=version) - compiler_output = compiled_src[""] - return VVMDeployer.from_compiler_output( - compiler_output, source_code, version, filename, name - ) + return VVMDeployer.from_source_code(source_code, version, filename, name) def from_etherscan( diff --git a/boa/util/cached_vvm.py b/boa/util/cached_vvm.py deleted file mode 100644 index 653543b6..00000000 --- a/boa/util/cached_vvm.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Any - -import vvm - -from boa.util.disk_cache import get_disk_cache - - -def compile_source(*args, **kwargs) -> Any: - """ - Compile Vyper source code via the VVM. - When a disk cache is available, the result of the compilation is cached. - Note the cache only works if the function is called the same way (args/kwargs). - :param args: Arguments to pass to vvm.compile_source - :param kwargs: Keyword arguments to pass to vvm.compile_source - :return: Compilation output - """ - disk_cache = get_disk_cache() - - def _compile(): - return vvm.compile_source(*args, **kwargs) - - if disk_cache is None: - return _compile() - - cache_key = f"{args}{kwargs}" - return disk_cache.caching_lookup(cache_key, _compile)