Introducing Brownie

A Python framework for testing, deploying and interacting with Ethereum smart contracts.

Brownie is no longer in beta! Check out an updated version of this article here.

I have a confession… I don’t like JavaScript.

Somewhere in the last decade JS moved from front-end to back-end to pretty much everywhere, and now we have a multitude of full stack devs who know only one language. I recognize that JS is here to stay and in some cases I can see the benefits, but as a testing and auditing framework for smart contracts it is not the optimal choice:

The EVM is single threaded. There is no need for asynchronous calls when running unit tests and deployment scripts. Instead of navigating callback hell or littering your code with endless await / async , we could be using literally any other language that doesn’t have this behavior.

/ , we could be using literally any other language that doesn’t have this behavior. Solidity only uses integers, JavaScript lacks native integers. Name me one other modern language that handles all numbers as floating points. So why are we using it to test smart contracts when it can’t accurately represent the wei equivalent of 0.1 ether?

Dynamic typing, a chaotic mix of functional and block scoping (with hoisting!), and 500+ NPM dependencies sharing the same global namespace. Oh, and implied globals. What could possibly go wrong?

“One day a student came to Moon and said, ‘I understand how to avoid using BIGNUMs! We will simply use floats!’ Moon struck the student with a stick. The student was enlightened.”

So a few months ago I finally rage quit and hacked together an alternative solution via the path of least resistance known to me: Python. The uncluttered and readable syntax, and simplicity of getting shit done made it an obvious choice. After all, the goal is to spend time debugging Solidity contracts, not unit tests!

What started as a 4 day code bender in a dimly lit Bangkok hotel room has progressively grown and evolved, and somewhere along the way it turned into a fully fledged project in it’s own right. So today I’m very excited to present to you…

Brownie!

Brownie is a robust, easy to use framework for developing Ethereum smart contracts. Use cases include:

Deployment : Automate the deployment of many contracts onto the blockchain, and any transactions needed to initialize or integrate them.

: Automate the deployment of many contracts onto the blockchain, and any transactions needed to initialize or integrate them. Interaction : Write scripts or use the console to interact with your contracts on the mainnet, or for quick testing in a local environment.

: Write scripts or use the console to interact with your contracts on the mainnet, or for quick testing in a local environment. Debugging : Get detailed information when a transaction reverts, to help you pinpoint the issue quickly.

: Get detailed information when a transaction reverts, to help you pinpoint the issue quickly. Testing: Write unit tests in python and evaluate test coverage based on stack trace analysis. We make no promises.

Interaction

Brownie’s console provides a simple way to perform on-the-fly testing and debugging in a local RPC environment, or interact with contracts on a remote chain. Rather than list off all the capabilities, take a look at this video for a sample of what can be done:

using the Brownie console — so simple!

When working with the mainnet or one of the testnets you can set up persistent environments to save contract information between sessions, and add accounts using a MetaMask compatible mnemonic.

Debugging

If a transaction reverts, brownie offers the following information to help you quickly locate and fix the issue:

The section of source code that caused the error

Events that fired prior to the revert

A call trace showing every jump, internal and external, leading up to the revert

Scripting and Deployment

Common or repetitious tasks can be automated via scripts. Here is an example script that distributes tokens based on a json file:

And here is an example deployment script for a slightly more complicated project:

When running unit tests you can share a common deployment script across many tests, and isolate each test by reverting the EVM state to immediately after the deployment. This translates to time saved in both writing and executing your tests. Which leads to the next section…

Unit Testing

Take a look at the first two tests from Truffle’s example project MetaCoin:

And now compare to the equivalent tests in Brownie:

They’re very similar to read, but Brownie is notably missing calls to toNumber() and async / await wrappers.

Brownie also offers additional testing capabilities such as:

skipping tests or marking them as “expected to fail”

setting alerts to monitor for state changes

evaluating test coverage based on stack traces

Test Coverage Evaluation

Brownie gives a percentage estimate for test coverage by analyzing the stack trace of each transaction and checking which opcodes executed. You can use this as a quick reference to make sure that you haven’t lost significant coverage after a code refactor.

Key benefits to coverage evaluation using the stack trace:

Non-invasive : Littering your contract with events to evaluate coverage means your tests are now running on a different contract. This affects gas consumption, sequence of event firing, and increases the time to run the tests.

: Littering your contract with events to evaluate coverage means your tests are now running on a different contract. This affects gas consumption, sequence of event firing, and increases the time to run the tests. Expression based: Within a line such as require( (a && b) || c); you can see if your tests hit all conditions truthfully and falsely. Line based coverage reporting is insufficient when a single line can evaluate in 5 different ways.

Additionally, you can use a complimentary tool I’ve created called opview as a way to visualize your coverage:

test coverage visualization with opview

Green means the highlighted code was executed, red means it wasn’t. Yellow and orange are for conditional statements that only evaluated True or False.

What Next?

In the coming months, I will continue to test and expand Brownie as I use it to develop other projects. In announcing it today, I’m inviting the greater community to try it out and offer questions, comments, criticisms and suggestions for improvement. Issues and pull requests are welcomed! I would love this to be a more collaborative effort and look forward to hearing from you.

Getting Started

To get started using Brownie, you can view the source code on Github and view the documentation on Read The Docs.