Skip to content

Commit

Permalink
Merge branch 'master' into feat/fingerprint-perf
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper committed Oct 11, 2024
2 parents 5d5299c + bedd49e commit ea20b26
Show file tree
Hide file tree
Showing 20 changed files with 759 additions and 147 deletions.
50 changes: 22 additions & 28 deletions boa/__init__.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import contextlib
import sys

import boa.explorer
from boa.contracts.base_evm_contract import BoaError
from boa.contracts.vyper.vyper_contract import check_boa_error_matches
from boa.dealer import deal
from boa.debugger import BoaDebug
from boa.environment import Env
from boa.explorer import Etherscan, _set_etherscan, get_etherscan
from boa.interpret import (
from_etherscan,
load,
load_abi,
load_partial,
load_vyi,
loads,
loads_abi,
loads_partial,
loads_vyi,
)
from boa.network import NetworkEnv
from boa.precompile import precompile
from boa.test.strategies import fuzz
from boa.util.open_ctx import Open
from boa.verifiers import get_verifier, set_verifier, verify
from boa.vm.py_evm import enable_pyevm_verbose_logging, patch_opcode

# turn off tracebacks if we are in repl
Expand All @@ -31,37 +37,20 @@

@contextlib.contextmanager
def swap_env(new_env):
old_env = env
try:
set_env(new_env)
with set_env(new_env):
yield
finally:
set_env(old_env)


def set_env(new_env):
def _set_env(new):
global env
env = new_env

Env._singleton = new_env


# Simple context manager which functions like the `open()` builtin -
# if simply called, it never calls __exit__, but if used as a context manager,
# it calls __exit__ at scope exit
class _TmpEnvMgr:
def __init__(self, new_env):
global env
self.old_env = env
env = new
Env._singleton = new

set_env(new_env)

def __enter__(self):
# dummy
pass

def __exit__(self, *args):
set_env(self.old_env)
def set_env(new_env):
global env
get_env = lambda: env # noqa: E731
return Open(get_env, _set_env, new_env)


def fork(
Expand All @@ -75,20 +64,25 @@ def fork(

new_env = Env()
new_env.fork(url=url, block_identifier=block_identifier, deprecated=False, **kwargs)
return _TmpEnvMgr(new_env)
return set_env(new_env)


def set_browser_env(address=None):
"""Set the environment to use the browser's network in Jupyter/Colab"""
# import locally because jupyter is generally not installed
from boa.integrations.jupyter import BrowserEnv

return _TmpEnvMgr(BrowserEnv(address))
return set_env(BrowserEnv(address))


def set_network_env(url):
"""Set the environment to use a custom network URL"""
return _TmpEnvMgr(NetworkEnv.from_url(url))
return set_env(NetworkEnv.from_url(url))


def set_etherscan(*args, **kwargs):
explorer = Etherscan(*args, **kwargs)
return Open(get_etherscan, _set_etherscan, explorer)


def reset_env():
Expand Down
39 changes: 39 additions & 0 deletions boa/contracts/vvm/vvm_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from boa.contracts.abi.abi_contract import ABIContractFactory, ABIFunction
from boa.environment import Env
from boa.util.eip5202 import generate_blueprint_bytecode

# TODO: maybe this doesn't detect release candidates
VERSION_RE = re.compile(r"\s*#\s*(pragma\s+version|@version)\s+(\d+\.\d+\.\d+)")
Expand All @@ -18,7 +19,19 @@ def _detect_version(source_code: str):


class VVMDeployer:
"""
A deployer that uses the Vyper Version Manager (VVM).
This allows deployment of contracts written in older versions of Vyper that
can interact with new versions using the ABI definition.
"""

def __init__(self, abi, bytecode, filename):
"""
Initialize a VVMDeployer instance.
:param abi: The contract's ABI.
:param bytecode: The contract's bytecode.
:param filename: The filename of the contract.
"""
self.abi = abi
self.bytecode = bytecode
self.filename = filename
Expand Down Expand Up @@ -55,6 +68,32 @@ def deploy(self, *args, env=None):

return self.at(address)

@cached_property
def _blueprint_deployer(self):
# TODO: add filename
return ABIContractFactory.from_abi_dict([])

def deploy_as_blueprint(self, env=None, blueprint_preamble=None, **kwargs):
"""
Deploy a new blueprint from this contract.
:param blueprint_preamble: The preamble to use for the blueprint.
:param env: The environment to deploy the blueprint in.
:param kwargs: Keyword arguments to pass to the environment `deploy_code` method.
:returns: A contract instance.
"""
if env is None:
env = Env.get_singleton()

blueprint_bytecode = generate_blueprint_bytecode(
self.bytecode, blueprint_preamble
)
address, _ = env.deploy_code(bytecode=blueprint_bytecode, **kwargs)

ret = self._blueprint_deployer.at(address)

env.register_blueprint(self.bytecode, ret)
return ret

def __call__(self, *args, **kwargs):
return self.deploy(*args, **kwargs)

Expand Down
39 changes: 17 additions & 22 deletions boa/contracts/vyper/vyper_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from vyper.codegen.module import generate_ir_for_module
from vyper.compiler import CompilerData
from vyper.compiler import output as compiler_output
from vyper.compiler.output import build_abi_output
from vyper.compiler.output import build_abi_output, build_solc_json
from vyper.compiler.settings import OptimizationLevel, anchor_settings
from vyper.exceptions import VyperException
from vyper.ir.optimizer import optimize
Expand Down Expand Up @@ -56,6 +56,7 @@
from boa.environment import Env
from boa.profiling import cache_gas_used_for_computation
from boa.util.abi import Address, abi_decode, abi_encode
from boa.util.eip5202 import generate_blueprint_bytecode
from boa.util.lrudict import lrudict
from boa.vm.gas_meters import ProfilingGasMeter
from boa.vm.utils import to_bytes, to_int
Expand Down Expand Up @@ -123,9 +124,15 @@ def at(self, address: Any) -> "VyperContract":
ret._set_bytecode(bytecode)

ret.env.register_contract(address, ret)

return ret

@cached_property
def solc_json(self):
"""
Generates a solc "standard json" representation of the Vyper contract.
"""
return build_solc_json(self.compiler_data)

@cached_property
def _constants(self):
# Make constants available at compile time. Useful for testing. See #196
Expand Down Expand Up @@ -155,6 +162,10 @@ def __init__(
msg += f"{capabilities.describe_capabilities()}"
raise Exception(msg)

@cached_property
def deployer(self):
return VyperDeployer(self.compiler_data, filename=self.filename)

@cached_property
def abi(self):
return build_abi_output(self.compiler_data)
Expand All @@ -173,24 +184,17 @@ def __init__(
compiler_data,
env=None,
override_address=None,
blueprint_preamble=b"\xFE\x71\x00",
blueprint_preamble=None,
filename=None,
gas=None,
):
# note slight code duplication with VyperContract ctor,
# maybe use common base class?
super().__init__(compiler_data, env, filename)

if blueprint_preamble is None:
blueprint_preamble = b""

blueprint_bytecode = blueprint_preamble + compiler_data.bytecode

# the length of the deployed code in bytes
len_bytes = len(blueprint_bytecode).to_bytes(2, "big")
deploy_bytecode = b"\x61" + len_bytes + b"\x3d\x81\x60\x0a\x3d\x39\xf3"

deploy_bytecode += blueprint_bytecode
deploy_bytecode = generate_blueprint_bytecode(
compiler_data.bytecode, blueprint_preamble
)

addr, computation = self.env.deploy(
bytecode=deploy_bytecode, override_address=override_address, gas=gas
Expand All @@ -204,10 +208,6 @@ def __init__(

self.env.register_blueprint(compiler_data.bytecode, self)

@cached_property
def deployer(self):
return VyperDeployer(self.compiler_data, filename=self.filename)


class FrameDetail(dict):
def __init__(self, fn_name, *args, **kwargs):
Expand Down Expand Up @@ -631,11 +631,6 @@ def __repr__(self):
def _immutables(self):
return ImmutablesModel(self)

@cached_property
def deployer(self):
# TODO add test
return VyperDeployer(self.compiler_data, filename=self.filename)

# is this actually useful?
def at(self, address):
return self.deployer.at(address)
Expand Down
Loading

0 comments on commit ea20b26

Please sign in to comment.