API Reference
High-Level Functionality
- boa.env: boa.environment.Env
The global environment object.
- boa.load(fp: str, *args: Any, **kwargs: Any) VyperContract | VyperBlueprint
Compile source from disk and return a deployed instance of the contract.
- Parameters:
fp – The contract source code file path.
args – Contract constructor arguments.
kwargs – Keyword arguments to pass to the
loads()
function.
Example
# Foo.vy @external def addition(a: uint256, b: uint256) -> uint256: return a + b
>>> import boa >>> boa.load("Foo.vy") <tmp/Foo.vy at 0x0000000000000000000000000000000000000066, compiled with ...>
>>> import boa >>> from vyper.compiler.settings import OptimizationLevel, Settings >>> boa.load("Foo.vy", compiler_args={"settings": Settings(optimize=OptimizationLevel.CODESIZE)}) <tmp/Foo.vy at 0xf2Db9344e9B01CB353fe7a2d076ae34A9A442513, compiled with ...>
- boa.loads(source: str, *args: Any, as_blueprint: bool = False, name: str | None = None, compiler_args: dict | None = None, **kwargs) VyperContract | VyperBlueprint
Compile source code and return a deployed instance of the contract.
- Parameters:
source – The source code to compile and deploy.
args – Contract constructor arguments.
as_blueprint – Whether to deploy an EIP-5202 blueprint of the compiled contract.
name – The name of the contract.
compiler_args – Argument to be passed to the Vyper compiler.
kwargs – Keyword arguments to pass to the
VyperContract
orVyperBlueprint
__init__
method.
Example
>>> import boa >>> src = """ ... value: public(uint256) ... @external ... def __init__(_initial_value: uint256): ... self.value = _initial_value ... """ >>> boa.loads(src, 69) <VyperContract at 0x0000000000000000000000000000000000000066, compiled with ...>
- boa.load_partial(fp: str, compiler_args: dict | None = None) VyperDeployer
Compile source from disk and return a
VyperDeployer
.- Parameters:
fp – The contract source code file path.
compiler_args – Argument to be passed to the Vyper compiler.
- Returns:
A
VyperDeployer
factory instance.
Example
# Foo.vy @external def addition(a: uint256, b: uint256) -> uint256: return a + b
>>> import boa >>> boa.load_partial("Foo.vy") <boa.vyper.contract.VyperDeployer object at ...>
- boa.loads_partial(source: str, name: str | None = None, dedent: bool = True, compiler_args: dict | None = None) VyperDeployer
Compile source and return a
VyperDeployer
.- Parameters:
source – The Vyper source code.
name – The name of the contract.
dedent – If True, remove any common leading whitespace from every line in source.
compiler_args – Argument to be passed to the Vyper compiler.
- Returns:
A
VyperDeployer
factory instance.
Example
>>> import boa >>> src = """ ... @external ... def main(): ... pass ... """ >>> boa.loads_partial(src, "Foo") <boa.vyper.contract.VyperDeployer object at ...>
- boa.load_abi(filename: str, name: str = None) ABIContractFactory
Return a
ABIContractFactory
from an ABI file (.json)- Parameters:
filename – The file containing the ABI as a JSON string (something like
my_abi.json
)name – The name of the contract.
- Returns:
A
ABIContractFactory
factory instance.
Example
>>> import boa >>> filename = "foo.json" >>> boa.load_abi(src, name="Foo") <boa.vyper.contract.ABIContractFactory at 0x7ff0f14a1550>
- boa.loads_abi(json_str: str, name: str = None) ABIContractFactory
Return a
ABIContractFactory
from an ABI string- Parameters:
json_str – The ABI as a JSON string (something which can be passed to
json.loads()
)name – The name of the contract.
- Returns:
A
ABIContractFactory
factory instance.
Example
>>> import boa >>> src = """[{"stateMutability": "nonpayable", "type": "function", "name": "foo", "inputs": [{"name": "", "type": "bytes"}], "outputs": [{"name": "", "type": "bytes"}]}]""" >>> boa.loads_abi(src, name="Foo") <boa.vyper.contract.ABIContractFactory at 0x7ff0f14a1550>
- boa.from_etherscan(address: str | bytes | Address, name: str = None, uri: str = 'https://api.etherscan.io/api', api_key: str = None) ABIContract
Fetch the ABI for an address from etherscan and return an
ABIContract
- Parameters:
address – The address. Can be str, bytes or Address
name – (Optional) The name of the contract.
- Returns:
A
ABIContract
instance.
Example
>>> import boa, os >>> boa.env.fork(os.environ["ALCHEMY_MAINNET_ENDPOINT"]) >>> crvusd = boa.from_etherscan("0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", name="crvUSD") >>> crvusd <crvUSD interface at 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E> >>> crvusd.totalSupply() 730773174461124520709282012
- boa.eval(statement: str) Any
Evaluate a Vyper statement in the context of a contract with no state.
- Parameters:
statement – A valid Vyper statement.
- Returns:
The result of the statement execution.
Example
>>> import boa >>> boa.eval("keccak256('Hello World!')").hex() '3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0' >>> boa.eval("empty(uint256[10])") (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
- boa.reverts(reason: str | None = None, /, **kwargs: str)
A context manager which validates an execution error occurs with optional reason matching.
Note
If a keyword argument is provided, as opposed to a positional argument, the argument name and value will be used to validate against a developer revert comment.
- Parameters:
reason – A string to match against the execution error.
compiler – A string to match against the internal compiler revert reason.
vm_error – A string to match against the revert reason string.
- Raises:
AssertionError – If there is more than one argument.
ValueError – If the execution did not have an error.
ValueError – If the reason string provided does not match the error that occurred.
Examples
Revert reason provided as a positional argument:
import boa source = """ @external def foo(): raise "0xdeadbeef" @external def bar(): raise # dev: 0xdeadbeef """ contract = boa.loads(source) with boa.reverts("0xdeadbeef"): contract.foo() with boa.reverts("0xdeadbeef"): contract.bar()
Compiler revert reason:
import boa source = """ @external def subtract(a: uint256, b: uint256) -> uint256: return a - b @external def addition(a: uint256, b: uint256) -> uint256: return a + b """ contract = boa.loads(source) with boa.reverts(compiler="safesub"): contract.subtract(1, 2) with boa.reverts(compiler="safeadd"): contract.addition(1, 2**256 - 1)
VM error reason:
import boa source = """ @external def main(a: uint256): assert a == 0, "A is not 0" """ contract = boa.loads(source) with boa.reverts(vm_error="A is not 0"): contract.main(69)
Developer revert comment:
import boa source = """ @external def main(a: uint256): assert a == 0 # dev: a is not 0 """ contract = boa.loads(source) with boa.reverts(dev="a is not 0"): contract.main(69)
- boa.register_precompile(address: str, fn: Callable[[eth.abc.ComputationAPI], None], force: bool = False)
Register a precompile.
- Parameters:
address – The address to register the precompile at.
fn – The function to execute when the precompile is called.
force – Whether to overwrite the precompile function if one is already registered at the specified address.
- Raises:
ValueError – If a precompile is already registered at the specified address and the force argument is
False
.
Example
>>> import boa >>> log = lambda computation: print("0x" + computation.msg.sender.hex()) >>> boa.register_precompile("0x00000000000000000000000000000000000000ff", log) >>> boa.eval("raw_call(0x00000000000000000000000000000000000000ff, b'')") 0x0000000000000000000000000000000000000069
- boa.deregister_precompile(address: str, force: bool = True)
Deregister a precompile.
- Parameters:
address – The address of a previously registered precompile.
force – Whether to force removal of the precompile at the specified address.
- Raises:
ValueError – If a precompile is not registered at the specified address and the force argument is
False
.
- boa.patch_opcode(opcode: int, fn: Callable[[eth.abc.ComputationAPI], None])
Patch an opcode.
- Parameters:
opcode – The opcode to patch.
fn – The function implementing the desired opcode functionality.
Note
The function provided as an argument should be defined with a single keyword parameter,
computation
, like so:def baz(computation: eth.abc.ComputationAPI): ...
Example
The following code snippet implements tracing for the
CREATE
opcode and stores all newly created accounts in a list.# example.py import boa class CreateTracer: def __init__(self, super_fn): """Track addresses of contracts created via the CREATE opcode. Parameters: super_fn: The original opcode implementation. """ self.super_fn = super_fn self.trace = [] def __call__(self, computation): # first, dispatch to the original opcode implementation provided by py-evm self.super_fn(computation) # then, store the output of the CREATE opcode in our `trace` list for later self.trace.append("0x" + computation._stack.values[-1][-1].hex()) if __name__ == "__main__": create_tracer = CreateTracer(boa.env.vm.state.computation_class.opcodes[0xf0]) boa.patch_opcode(0xf0, create_tracer) source = """ @external def main(): for _ in range(10): addr: address = create_minimal_proxy_to(self) """ contract = boa.loads(source) contract.main() # execute the contract function print(create_tracer.trace)
Running the code would produce the following results:
$ python example.py [ "0xd130b7e7f212ecadcfcca3cecc89f85ce6465896", "0x37fdb059bf647b88dbe172619f00b8e8b1cf9338", "0x40bcd509b3c1f42d535d1a8f57982729d4b52adb", "0xaa35545ac7a733600d658c3f516ce2bb2be99866", "0x29e303d13a16ea18c6b0e081eb566b55a74b42d6", "0x3f69d814da1ebde421fe7dc99e24902b15af960b", "0x719c0dc21639008a2855fdd13d0d6d89be53f991", "0xf6086a85f5433f6fbdcdcf4f2ace7915086a5130", "0x097dec6ea6b9eb5fc04db59c0d343f0e3b4097a0", "0x905794c5566184e642ef14fb0e72cf68ff8c79bf" ]
Low-Level Functionality
- class boa.environment.Env
A wrapper class around py-evm which provides a “contract-centric” API.
- eoa: str
The account to use as
msg.sender
for top-level calls andtx.origin
in the context of state mutating function calls.
- chain: eth.abc.ChainAPI
The global py-evm chain instance.
- enable_fast_mode(flag: bool = True) None:
Enable or disable fast mode. This can be useful for speeding up tests.
- Parameters:
flag – Whether to enable or disable fast mode.
- alias(address: str, name: str) None
Associates an alias with an address. This is useful to make the address more human-readable in tracebacks.
- Parameters:
address – The address to alias.
name – The alias to use for the address.
- generate_address(alias: str | None = None) str
Generate an address and optionally alias it.
- Parameters:
alias – The alias to use for the generated address.
Example
>>> import boa >>> boa.env.generate_address() '0xd13f0Bd22AFF8176761AEFBfC052a7490bDe268E'
- set_random_seed(seed: Any = None) None
Set the random seed used by this
Env
to generate addresses. Useful in case you want to introduce some more randomization to howEnv
generates addresses.- param seed:
The seed to pass to this
Env
’s instance ofrandom.Random
. Can be any value thatrandom.Random()
accepts.
Example
>>> import boa >>> boa.env.set_random_seed(100) >>> boa.env.generate_address() '0x93944a25b3ADa3759918767471C5A3F3601652c5
- set_balance(address: str, value: int)
Set the ether balance of an account.
- get_balance(address: str) int
Get the ether balance of an account.
- fork(provider: str, **kwargs: Any)
Fork the state of an external node allowing local simulation of state mutations.
- Parameters:
provider – The URL of the node provider to fork the state of.
block_identifier – The block identifier to fork the state at. The value may be an integer, bytes, a hexadecimal string or a pre-defined block identifier (
"earliest"
,"latest"
,"pending"
,"safe"
or"finalized"
). Defaults to"safe"
.
Example
>>> import boa >>> boa.env.vm.state.block_number 1 >>> boa.env.fork("https://rpc.ankr.com/eth") >>> boa.env.vm.state.block_number 16038471
- anchor()
A context manager which snapshots the state and the vm, and reverts to the snapshot on exit.
Example
>>> import boa >>> src = """ ... value: public(uint256) ... """ >>> contract = boa.loads(src) >>> contract.value() 0 >>> with boa.env.anchor(): ... contract.eval("self.value += 1") ... contract.value() ... 1 >>> contract.value() 0
- prank(address: str)
A context manager which temporarily sets
eoa
and resets it on exit.Example
>>> import boa >>> boa.env.eoa '0x0000000000000000000000000000000000000065' >>> with boa.env.prank("0x00000000000000000000000000000000000000ff"): ... boa.env.eoa ... '0x00000000000000000000000000000000000000ff' >>> boa.env.eoa
- deploy_code(at: str = '0x0000000000000000000000000000000000000000', sender: str | None = None, gas: int | None = None, value: int = 0, bytecode: bytes = b'', data: bytes = b'', pc: int = 0) bytes
Deploy bytecode at a specific account.
- Parameters:
at – The account the deployment bytecode will run at.
sender – The account to set as
tx.origin
for the execution context andmsg.sender
for the top-level call.gas – The gas limit provided for the execution (a.k.a.
msg.gas
).value – The ether value to attach to the execution (a.k.a
msg.value
).bytecode – The deployment bytecode.
data – The data to attach to the execution (a.k.a.
msg.data
).pc – The program counter to start the execution at.
- Returns:
The return value from the top-level call (typically the runtime bytecode of a contract).
Example
>>> import boa >>> code = bytes.fromhex("333452602034f3") # simply returns the caller >>> boa.env.deploy_code(bytecode=code, sender="0x0000000022D53366457F9d5E68Ec105046FC4383").hex() '0000000000000000000000000000000022d53366457f9d5e68ec105046fc4383' >>> boa.env.vm.state.get_code(b"\x00" * 20).hex() '0000000000000000000000000000000022d53366457f9d5e68ec105046fc4383'
- execute_code(at: str = '0x0000000000000000000000000000000000000000', sender: str | None = None, gas: int | None = None, value: int = 0, bytecode: bytes = b'', data: bytes = b'', pc: int = 0) bytes
Execute bytecode at a specific account.
- Parameters:
at – The account to target.
sender – The account to set as
tx.origin
for the execution context andmsg.sender
for the top-level call.gas – The gas limit provided for the execution (a.k.a.
msg.gas
).value – The ether value to attach to the execution (a.k.a
msg.value
).bytecode – The runtime bytecode.
data – The data to attach to the execution (a.k.a.
msg.data
).pc – The program counter to start the execution at.
- Returns:
The return value from the top-level call.
- raw_call(to_address: str, sender: str | None = None, gas: int | None = None, value: int = 0, data: bytes = b'') bytes
Simple wrapper around execute_code, to execute as if the contract is being called from an EOA.
- Parameters:
to_address – The contract to target.
sender – The account to set as
tx.origin
for the execution context andmsg.sender
for the top-level call.gas – The gas limit provided for the execution (a.k.a.
msg.gas
).value – The ether value to attach to the execution (a.k.a
msg.value
).data – The data to attach to the execution (a.k.a.
msg.data
).
- Returns:
The return value from the top-level call.
- time_travel(seconds: int = None, blocks: int = None, block_delta: int = 12)
Fast forward, increase the chain timestamp and block number.
- Parameters:
seconds – Change current timestamp by seconds seconds.
blocks – Change block number by blocks blocks.
block_delta – The time between two blocks. Set to 12 as default.
- class boa.vyper.contract.VyperDeployer
Vyper contract factory.
- at(address: str) VyperContract
Return a
VyperContract
instance for a contract deployed at the provided address.- Parameters:
address – The address of the contract.
- Returns:
A contract instance.
Example
>>> import boa >>> src = """ ... @external ... def main(): ... pass ... """ >>> ContractFactory = boa.loads_partial(src, "Foo") >>> ContractFactory.at("0xD130B7E7F212ECADCfcCa3cecC89f85ce6465896") <Foo at 0xD130B7E7F212ECADCfcCa3cecC89f85ce6465896, compiled with ...>
- deploy(*args: Any, **kwargs: Any) VyperContract
Deploy a new contract.
- Parameters:
args – The contract constructor arguments.
kwargs – Keyword arguments to pass to the
VyperContract
__init__
method.
Example
>>> import boa >>> src = """ ... @external ... def main(): ... pass ... """ >>> ContractFactory = boa.loads_partial(src, "Foo") >>> ContractFactory.deploy() <Foo at 0x0000000000000000000000000000000000000066, compiled with ...>
- deploy_as_blueprint(*args: Any, **kwargs: Any) VyperBlueprint
Deploy a new EIP-5202 blueprint instance.
- Parameters:
args – Positional arguments to pass to the
VyperBlueprint
__init__
method.kwargs – Keyword arguments to pass to the
VyperBlueprint
__init__
method.
Example
>>> import boa >>> src = """ ... @external ... def main(): ... pass ... """ >>> ContractFactory = boa.loads_partial(src, "Foo") >>> ContractFactory.deploy_as_blueprint() <boa.vyper.contract.VyperBlueprint object at ...>
- class boa.vyper.contract.VyperContract
A contract instance.
Internal and external contract functions are available as methods on
VyperContract
instances.Example
>>> import boa >>> src = """ ... @external ... def main(): ... pass ... ... @internal ... def foo() -> uint256: ... return 123 ... """ >>> contract = boa.loads(src) >>> type(contract.main) <class 'boa.vyper.contract.VyperFunction'> >>> type(contract.foo) <class 'boa.vyper.contract.VyperInternalFunction'> >>> contract.internal.foo() 123
- eval(statement: str, value: int = 0, gas: int | None = None, sender: str | None = None) Any
Evaluate a Vyper statement in the context of the contract.
- Parameters:
statement – A vyper statment.
value – The ether value to attach to the statement evaluation (a.k.a
msg.value
).gas – The gas limit provided for statement evaluation (a.k.a.
msg.gas
).sender – The account which will be the
tx.origin
, andmsg.sender
in the context of the evaluation.
- Returns:
The result of the statement evaluation.
Example
>>> import boa >>> src = "value: public(uint256)" >>> contract = boa.loads(src) >>> contract.value() 0 >>> contract.eval("self.value += 1") >>> contract.value() 1
- property deployer: VyperDeployer
- class boa.vyper.contract.VyperFunction
- prepare_calldata(*args: Any, **kwargs: Any) bytes:
- Prepare the calldata that a function call would use.
- This is useful for saving calldata for use in a transaction later.
- Parameters:
args – The positional arguments of the contract function.
kwargs – Keyword arguments of the contract function.
- Returns:
The calldata that a call with a particular set of arguments would use, in bytes.
Example
>>> import boa >>> src = """ ... @external ... def main(a: uint256) -> uint256: ... return 1 + a ... """ >>> c = boa.loads(src) >>> contract.main.prepare_calldata(68) b'\xab:\xe2U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0D'
- __call__(*args: Any, value: int = 0, gas: int | None = None, sender: str | None = None, **kwargs: Any) Any
Execute the function.
- Parameters:
args – The positional arguments of the contract function.
value – The ether value to attach to the execution of the function (a.k.a
msg.value
).gas – The gas limit provided for function execution (a.k.a.
msg.gas
).sender – The account which will be the
tx.origin
of the execution, andmsg.sender
of the top-level call.kwargs – Keyword arguments of the contract function.
- Returns:
The result of the function.
Example
>>> import boa >>> src = """ ... @external ... def main(a: uint256) -> uint256: ... return 1 + a ... """ >>> contract = boa.loads(src) >>> contract.main(68) 69
- contract: VyperContract
The
VyperContract
instance thisVyperFunction
instance is attached to.
- env: boa.environment.Env
The
boa.environment.Env
instance of thecontract
attribute.
- fn_ast: vyper.ast.nodes.FunctionDef
The Vyper AST of this function.
- property assembly: list[str]
The function’s runtime bytecode as a list of mnemonics.
- property bytecode: bytes
The function’s runtime bytecode in bytes form.
- property fn_signature: vyper.ast.signatures.function_signature.FunctionSignature
The internal Vyper representation of the function’s signature.
- property opcodes: str
The function’s runtime bytecode as a string of mnemonics.
- property ir: vyper.codegen.ir_node.IRnode
The internal representation of the function (a.k.a. VenomIR).
- class boa.vyper.contract.VyperInternalFunction
Internal contract functions are exposed by wrapping it with a dummy external contract function, appending the wrapper’s ast at the top of the contract and then generating bytecode to run internal methods (as external methods). Therefore, they share the same API as
boa.vyper.contract.VyperFunction
. Internal functions can be accessed using the internal namespace of aVyperContract
.>>> import boa >>> src = """ ... @internal ... def main(a: uint256) -> uint256: ... return 1 + a ... """ >>> contract = boa.loads(src) >>> contract.internal.main(68) 69
Exceptions
- exception boa.BoaError
Raised when an error occurs during contract execution.