Setting Up a StarkNet Dev Environment with Python
TL;DR; Commands to create a StarkNet dev environment using only Python.
# Install global dependencies (macOS)
$ brew install gmp python@3.9
# Create virtual environment
$ python3.9 -m venv env
# Activate virtual environment
$ source env/bin/activate
# Upgrade pip to latest version
(env) $ pip install --upgrade pip
# Install project dependencies inside virtual environment
(env) $ pip install cairo-lang openzeppelin-cairo-contracts pytest pytest-asyncio starknet-devnet starknet.py
# Track project dependencies in a file
(env) $ pip freeze > requirements.txt
File to set up the environment upon cloning (setup.sh).
#!/bin/bash
rm -rf env
python3.9 -m venv env
source env/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
deactivate
To use the script run bash setup.sh
The Problem We Want to Solve
Cairo docs provide instructions on how to configure your dev environment using Python’s built-in tools. This recommendation works well if it’s your first time using StarkNet or if you are working on a single project and on your own. As soon as you start maintaining a second project or start to collaborate with other people this setup will be insufficient because of improper dependency management.
In this article, we are going to explore how to make the most out of Python’s built-in tools for better collaboration and maintenance of your StarkNet projects without the overhead of having to learn new APIs and configuration files of advanced package managers and tooling.
Global Dependencies
Even though our goal is to isolate dependencies between StarkNet projects, there are two packages that must be available globally: gmp and python.
The release notes of Cairo 0.10.0 mentions that it depends on Python 3.9. On macOS, you can install both dependencies globally using Homebrew.
$ brew install gmp python@3.9
Installing a specific version of Python on Ubuntu in a way that plays nicely with pip is surprisingly difficult. If you are looking for a cross-platform alternative, take a look at this other article we wrote that uses Docker to create a StarkNet dev environment.
Project Scaffolding
We are going to name the root folder of our sample project as my-starknet-project but you can give it any name you want.
$ mkdir my-starknet-project && cd my-starknet-project
Inside this folder we are going to create four directories to better organize our code:
- src: Where StarkNet smart contracts are stored
- libs: Where Cairo functions for reusable functionality are stored
- comp: Where the compiled version StarkNet smart contracts are stored
- abis: Where the ABIs of StarkNet smart contracts are stored
- tests: Where unit and integration tests (written in Python) are stored
$ mkdir src libs comp abis tests
The number of folders and their naming convention is only one of many possibilities. Different projects might use different conventions so feel free to come up with your own.
Finally, there are two files that are always needed for collaboration, .gitignore and README.md
$ touch .gitignore README.md
At the end you should end up with the following folder structure:
.
├── .gitignore
├── README.md
├── abis/
├── comp/
├── libs/
├── src/
└── tests/
Virtual Environment
Instead of having a single virtual environment shared with all projects, we are going to create a dedicated one inside the project’s root folder so packages can be managed in isolation.
Because this project is going to use Cairo 0.10.0 we should create the virtual environment using python3.9 binary.
$ python3.9 -m venv env
This will create a folder named env inside the project folder.
.
├── .gitignore
├── README.md
├── abis/
├── comp/
├── env/
├── libs/
├── src/
└── tests/
To activate the virtual environment we can run:
$ source env/bin/activate
Because the env folder stores python and pip binaries along with any package you install from pypi, it should not be added to your repository to avoid bloating.
$ echo “env” >> .gitignore
Instead, we can create a shell script to help recreate the virtual environment with the correct version of Python.
setup.sh
#!/bin/bash
rm -rf env
python3.9 -m venv env
This script can be executed by running:
$ bash setup.sh
Project Dependencies
With the project’s virtual environment activated we can install cairo-lang.
(env) $ pip install cairo-lang
To run unit tests on our Cairo files, we will need to install the packages pytest and pytest-asyncio.
(env) $ pip install pytest pytest-asyncio
To run integration tests with our smart contracts, we will need the packages starknet-devnet and starknet.py.
(env) $ pip install starknet-devnet starknet.py
starknet-devnet allows you to create a local version of StarkNet’s testnet to deploy and interact with your smart contracts (similar to Ganache) while starknet.py is an SDK to interact with this local testnet. starknet.py can also be used to create complex deployments to either Goerli or Mainnet.
Finally, install Open Zeppelin’s Cairo smart contracts to avoid reinventing the wheel for common use cases like ERC20 tokens and NFTs.
(env) $ pip install openzeppelin-cairo-contracts
To make our dependencies explicit to any other team member working on the project, we can create a requirements.txt file to list them.
(env) $ pip freeze > requirements.txt
At this point, our folder structure should look like this:
.
├── .gitignore
├── README.md
├── abis/
├── comp/
├── env/
├── libs/
├── requirements.txt
├── setup.sh
├── src/
└── tests/
Now when a team member clones the project they can install the same dependencies by running:
(env) $ pip install -r requirements.txt
We can further automate the project initialization by modifying setup.sh to also install the project dependencies.
setup.sh
#!/bin/bash
rm -rf env
python3.9 -m venv env
source env/bin/activate
pip install -r requirements.txt
deactivate
You can find the source code for this development environment here.
Conclusion
The development environment shown in this article aims at being functional while remaining simple using only what Python has to offer out of the box.
Unfortunately, Python’s built-in dependency manager (pip) has some shortcomings. It is not as feature complete as tools like npm (Javascript) or cargo (Rust); a lot more manual intervention is required to keep dependencies in sync and to create reusable commands.
If you are looking for a more robust dependency management solution you can use Poetry. If you want to also define global dependencies as code in your project you can use Docker but it might be overkill for most projects.
You can also setup a non-Python based development environment using Hardhat or Protostar as we discussed in our previous article “Choosing a StarkNet Development Tool”.
What other Python packages do you normally use with Cairo? Let us know in the comment section below.