Contract Deployment

Porting A Project From Brownie to Ape

written by
Chris Cao
Date Last Updated
February 14, 2023
Download Project Files

Welcome to a long awaited tutorial about porting a brownie project to Ape Framework. The goal of this tutorial is to provide you with general rules and guidelines on how to port. Ape was intentionally made to be similar to Brownie so you can use Ape intuitively.

Porting a Project from Brownie to Ape

Are you ready to switch from Brownie to Ape for your project? Look no further! This technical documentation will guide you through porting your Brownie Project to an Ape Project. We've got you covered, from uninstalling eth-brownie and installing eth-ape to updating your project hierarchy and config file. Additionally, we'll take you through the changes you need to make to your scripts and tests, including updates to imports, events, and more. By the end of this document, you'll be fully equipped to transition to Ape easily.

Part 1: Making the Brownie

This tutorial is going to take a simple ERC20 Brownie template made with Quicknode and turn it into an Ape ERC20 project. As a prerequisite, you should have a working Brownie project. We do not want unaccounted errors due to a Brownie project not compiling. However if you can’t get it to work, you can still try and if all else fails, come say hello in our discord and we can help out as best as we can.

Here is a link to the Brownie Tutorial I used to make this project. Please follow the instructions on how to make it and when you are ready come back!

Part 2: Porting a Brownie Project into Ape Framework

Here is a checklist of items to look out for when porting:

     [ ] Install

        [ ] pip uninstall eth-brownie

        [ ] pip install pip -U

        [ ] pip install eth-ape -U

    [ ] Rename file
        [ ] Change brownie-config.yaml to ape-config.yaml

   [ ] Move directory

        [ ] Make the interface folder a subdirectory under contracts (solidity requirement)

        [ ] import brownie and replace with import ape

The first file to check is your ape-config.yaml. You want to include all of your plugins you use to make this project work. If you do not know which plugins to install,,

  1. In ape console type `ape plugins list --all`
  2. Consider a compiler to start with and if you use any of the other plugins, you can look at the github repo to see if you use it.
  3. Here is the link to ApeWorX github
  4. search for a plugin ape-<plugin name>

Typically, you would need to install the solidity and/or vyper plugins, depending on which compiler your project is using. This project is using solidity, so we need the solidity plugin. Also, it's usually the case that brownie projects use a node implementation like Ganache or Hardhat for testing. In this example, we are going to use the Anvil node by installing the foundry plugin. Therefore we are going describe it in the config like this:

name: erc20
plugins:
- name: solidity
- name: foundry

Scripts folder

After the ape-config.yaml is settled, I will look into the scripts folder. It looks like the token.py is a deployment script. So we are going to edit it like so:

Brownie

from brownie import Token, accounts
def main():
      acct = accounts.load('testac')
      return Token.deploy("Test Token", "TST", 18, 1e21, {'from': acct})

Ape

from ape import project, accounts
def main():
      acct = accounts.load('testac')
      return project.Token.deploy("Test Token", "TST", 18, int(1e21), sender=acct)

Let’s break this down:

In Ape every contract type is generated in project. Therefore, we import project and then we do project.<contract name>

The {“from”:acct} syntax is changed to sender=acct or with accounts.use_sender. sender=acct is the direct translation. Another option to call the acct is to use accounts class and call it to use the test accounts.

Lastly we want to wrap big float literals to an integer. Wonderful the scripts folder is done!

Test folder

The test folder uses python testing suite so we should configure it to work with Ape natively. This is probably where most of the conversion will happen and the source of most of your headaches :)

conftest.py

Brownie

import pytest

@pytest.fixture(scope="function", autouse=True)
def isolate(fn_isolation):
    # perform a chain rewind after completing each test, to ensure proper isolation
    # https://eth-brownie.readthedocs.io/en/v1.10.3/tests-pytest-intro.html#isolation-fixtures
    pass

@pytest.fixture(scope="module")
def token(Token, accounts):
    return Token.deploy("Test Token", "TST", 18, 1e21, {'from': accounts[0]})

Ape

import pytest

@pytest.fixture(scope="module")
def token(project, accounts):
    return project.Token.deploy("Test Token", "TST", 18, int(1e21), sender=accounts[0])

We know from the scripts folder that we need to

  1. use project.Token instead of Token
  2. {“from”:acct} is changed to sender=accounts[0]
  3. Wrap int(1e21)

NOTE: The definition method isolate is INCLUDED in ape be default. We do not need to explicitly write it and so we can remove it. The conftest.py is looking good now.

The next 3 files are going to use mostly the same changes so you can do one and try to apply your knowledge on the other 2.

Across the rest of the tests, you should find and replace these 3:

From:account[0] to sender=accounts[0]
From:account[1] to sender=accounts[1]
import brownie to import ape

Event Tests

The test that trigger events should match the syntax we use.

def test_approval_event_fires(accounts, token):

Brownie Assert Statement:

assert tx.events["Approval"].values() == [accounts[0], accounts[1], 10**19]

Ape Assert Statement:

assert tx.events[0].owner == accounts[0] 
assert tx.events[0].spender == accounts[1] 
assert tx.events[0].value == 10**19

def test_transfer_event_fires(accounts, token):

Brownie Assert Statement:

assert tx.events["Transfer"].values() == [accounts[0], accounts[1], amount]

Ape Assert Statement:

assert tx.events[0].get('from') == accounts[0] 
assert tx.events[0].to == accounts[1] 
assert tx.events[0].value == amount

def test_transfer_event_fires(accounts, token):

Brownie Assert Statement:

assert tx.events["Transfer"].values() == [accounts[0], accounts[2], amount] 

Ape Assert Statement:

assert tx.events[0].get('from') == accounts[0] 
assert tx.events[0].to == accounts[2] 
assert tx.events[0].value == amount

Foundry install

For this project, it uses returns_value. returns_value is a keyword that is not recognized by default in Ape. In order to use it, you should use a plugin that enables the use of returns_value like Foundry binaries found in the Foundry plugin.

Here is the link to install it:

  1. Open a new terminal window.
  2. Make sure you deactivate your virtual environment
  3. According to Foundry github instructions, install it via curl -L https://foundry.paradigm.xyz | bash
  4. In the terminal type foundryup
  5. source ~/.bashrc
  6. Activate your python virtual environment
  7. Type anvil to check if it works, control+c
  8. Activate ape test –network ::foundry -x

If it fails for whatever reason, read the error code and look at the glossary to port to an ape project. We will improve the glossary every time someone finds a new error :) So please let us know if you find something we did not find.

This should be it! You have compiled and tested the code and it works in Ape! You can check out how to deploy in a short 5 min video and mint your first ERC20 Token on chain or you can see how to write this whole project from scratch in Ape with Vyper!

Thank you so much for watching and reading.

I host office hours on Mondays at 1 PM PST.

Come say hello!

Glossary

Here is a quick look place to find and replace the difference