A Vyper interpreter with pretty tracebacks, forking, debugging features and more! Titanoboa's goal is to provide a modern, advanced and integrated development experience for vyper users.
Titanoboa uses Vyper as a library to compile source code to bytecode, then runs that bytecode with py-evm and adds instrumentation for introspection. The py-evm backend is highly configurable, including opcode and precompile patching. Some optional modes trade EVM fidelity for speed; use the default mode for correctness-sensitive and gas-sensitive tests.
Usage and quickstart are below. For more detailed documentation, please see the documentation.
pip install titanoboa
Python packaging metadata currently does not declare a minimum Python version. Python 3.11 is used by the release workflow; verify compatibility with your project before choosing another version.
For latest dev version:
pip install git+https://github.com/vyperlang/titanoboa
If you are installing titanoboa from git alongside brownie, you may have to manually install titanoboa after installing brownie
pip install brownie
pip install git+https://github.com/vyperlang/titanoboa
To get a performance boost for forking, install the forking-recommended extra:
pip install "titanoboa[forking-recommended]"It installs requests-cache and ujson. The other published extra is colab, which installs a recent IPython kernel for Google Colab.
If you are running titanoboa on a local Vyper project folder, you might need to run python setup.py install on your Vyper project if you encounter errors such as ModuleNotFoundError: No module named 'vyper.version'
Titanoboa (/ˌtaɪtənəˈboʊə/; lit. 'titanic boa') is an extinct genus of giant boid (the family that includes all boas and anacondas) snake that lived during the middle and late Paleocene. Titanoboa was first discovered in the early 2000s by the Smithsonian Tropical Research Institute who, along with students from the University of Florida, recovered 186 fossils of Titanoboa from La Guajira in northeastern Colombia. It was named and described in 2009 as Titanoboa cerrejonensis, the largest snake ever found at that time. It was originally known only from thoracic vertebrae and ribs, but later expeditions collected parts of the skull and teeth. Titanoboa is in the subfamily Boinae, being most closely related to other extant boines from Madagascar and the Pacific.
Titanoboa could grow up to 12.8 m (42 ft) long, perhaps even up to 14.3 m (47 ft) long, and weigh around 730–1,135 kg (1,610–2,500 lb). The discovery of Titanoboa cerrejonensis supplanted the previous record holder, Gigantophis garstini, which is known from the Eocene of Egypt. Titanoboa evolved following the extinction of all non-avian dinosaurs, being one of the largest reptiles to evolve after the Cretaceous–Paleogene extinction event. Its vertebrae are very robust and wide, with a pentagonal shape in anterior view, as in other members of Boinae. Although originally thought to be an apex predator, the discovery of skull bones revealed that it was more than likely specialized in preying on fish.
import boa
boa.eval("empty(uint256)")# simple.vy
@external
def foo() -> uint256:
x: uint256 = 1
return x + 7>>> import boa
>>> simple = boa.load("examples/simple.vy")
>>> simple.foo()
8>>> import boa
>>> erc20 = boa.load("examples/ERC20.vy", 'titanoboa', 'boa', 18, 1)
>>> erc20.name()
titanoboa
>>> erc20.symbol()
boa
>>> erc20.balanceOf(erc20.address)
0
>>> erc20.totalSupply()
1000000000000000000>>> import boa
>>> s = boa.load_partial("examples/ERC20.vy")
>>> blueprint = s.deploy_as_blueprint()
>>> deployer = boa.load("examples/deployer.vy", blueprint)
>>> token = s.at(deployer.create_new_erc20("token", "TKN", 18, 10**18))
>>> token.totalSupply()
>>> 1000000000000000000000000000000000000>>> import boa
>>> erc20 = boa.load("examples/ERC20.vy", "titanoboa", "boa", 18, 0)
>>> with boa.env.prank(boa.env.generate_address()):
... with boa.reverts():
... erc20.mint(boa.env.eoa, 100) # non-minter cannot mint
...
>>> with boa.env.prank(boa.env.generate_address()):
... # you can be more specific about the failure reason
... with boa.reverts(rekt="non-minter tried to mint"):
... erc20.mint(boa.env.eoa, 100)In [1]: %load_ext boa.ipython
import boa
boa.interpret.set_cache_dir() # cache source compilations across sessions
In [2]: %vyper msg.sender # evaluate a vyper expression directly
Out[2]: '0x0000000000000000000000000000000000000065'
In [3]: %%vyper
...:
...: MY_IMMUTABLE: immutable(uint256)
...:
...: @deploy
...: def __init__(some_number: uint256):
...: MY_IMMUTABLE = some_number * 2
...:
...: @external
...: def foo() -> uint256:
...: return MY_IMMUTABLE
...:
Out[3]: <boa.contracts.vyper.vyper_contract.VyperDeployer object at 0x...>
In [4]: d = _
In [4]: c = d.deploy(5)
In [5]: c.foo()
Out[5]: 10>>> erc20 = boa.load("examples/ERC20.vy", 'titanoboa', 'boa', 18, 1)
>>> erc20.balanceOf(erc20.address)
0
>>> erc20.totalSupply()
1000000000000000000
>>> erc20.eval("self.totalSupply += 10") # manually mess with total supply
>>> erc20.totalSupply()
1000000000000000010
>>> erc20.eval("self.totalSupply") # same result when eval'ed
1000000000000000010
>>> erc20.eval("self.balanceOf[msg.sender] += 101") # manually mess with balance
>>> erc20.balanceOf(boa.env.eoa)
1000000000000000101
>>> erc20.eval("self.balanceOf[msg.sender]") # same result when eval'ed
1000000000000000101Note that in eval() mode, titanoboa uses slightly different optimization settings, so gas usage may not be the same as using the external interface.
Create a local py-evm fork backed by an RPC. boa.fork() replaces the singleton environment permanently when called normally, or restores the previous environment when used as a context manager. It rejects dirty local state unless allow_dirty=True; allowing dirty state discards that state because the fork uses a new environment.
In [1]: import boa; boa.fork("<rpc server address>")
In [2]: %load_ext boa.ipython
In [3]: %%vyper Test
...: interface HasName:
...: def name() -> String[32]: view
...:
...: @external
...: def get_name_of(addr: HasName) -> String[32]:
...: return staticcall addr.name()
Out[3]: <boa.contracts.vyper.vyper_contract.VyperDeployer object at 0x...>
In [4]: c = Test.deploy()
In [5]: c.get_name_of("0xD533a949740bb3306d119CC777fa900bA034cd52")
Out[5]: 'Curve DAO Token'Cast current deployed addresses to vyper contract
>>> import boa; boa.fork("<rpc server address>")
>>> c = boa.load_partial("examples/ERC20.vy").at("0xD533a949740bb3306d119CC777fa900bA034cd52")
>>> c.name()
'Curve DAO Token'>>> import boa
>>> boa.set_network_env("<rpc server address>")
>>> from eth_account import Account
>>> # in a real codebase, always load private keys safely from an encrypted store!
>>> boa.env.add_account(Account.from_key("<a private key>"))
>>> c = boa.load("examples/ERC20.vy", "My Token", "TKN", 10**18, 10)
>>> c.name()
'My Token'You can use Jupyter to execute Titanoboa code in network mode from your browser using a wallet.
We provide a BrowserSigner as a drop-in replacement for eth_account.Account.
The BrowserRPC may be used to interact with the RPC server from the browser.
For a full example, please see this example Jupyter notebook
Install Titanoboa and start JupyterLab normally. Titanoboa exposes a Jupyter server extension through Python package discovery; no manual frontend-extension activation is required.
pip install titanoboaTo activate our IPython extension, you need to run the following command in the notebook:
%load_ext boa.ipython
For ease of use, add the following to ipython_config.py:
c.InteractiveShellApp.extensions = ["boa.ipython"]
c.InteractiveShellApp.exec_lines = ['import boa']We provide a multi-user setup with JupyterLab in try.vyperlang.org, where the extension is installed and activated. The source code for this website is available in the GitHub repository.
It is also possible to run our plugin in Google Colab. To do this, you need to install the plugin by running the following commands:
!pip install titanoboa
%load_ext boa.ipython
This activates %vyper, %%vyper, %%contract, and %eval. %vyper and %eval evaluate one Vyper expression. %%vyper Name compiles a deployer and binds it to Name; %%contract Name compiles, deploys, and binds a contract instance.
$ python -m tests.integration.sim_veYFI