Brownie to Ape Migration

Brownie is no longer actively maintained. The Brownie README directs Python Ethereum developers to Ape Framework. This guide documents a practical migration path for Brownie projects moving to Ape and references ApeWorX/ape issue #640, which originally tracked Brownie project migration support.

Imports

Brownie scripts often import accounts, contracts, config, and networks from brownie. Migrated Ape scripts import public Ape APIs and access contracts through project.

Brownie:

from brownie import accounts, config, SimpleStorage, network

Ape:

from ape import accounts, config, networks, project

Accounts

Use accounts.test_accounts for local generated accounts and accounts.load() for named account aliases. The alias must be imported by the user before live-network use.

Brownie:

if network.show_active() == "development":
    return accounts[0]

return accounts.add(config["wallets"]["from_key"])

Ape:

if not networks.provider.network.is_dev:
    return accounts.test_accounts[0]

return accounts.load("<alias>")

Official docs: Accounts

Contract Deployment and Transactions

Brownie transaction dictionaries become explicit Ape keyword arguments such as sender= and value=.

Brownie:

simple_storage = SimpleStorage.deploy({"from": account})
transaction = simple_storage.store(15, {"from": account})

Ape:

simple_storage = project.SimpleStorage.deploy(sender=account)
transaction = simple_storage.store(15, sender=account)

For payable calls:

Brownie:

tx = fund_me.fund({"from": account, "value": entrance_fee})

Ape:

tx = fund_me.fund(sender=account, value=entrance_fee)

Official docs: Contracts

Networks

Brownie’s active-network helper maps to Ape’s active provider network metadata.

Brownie:

print(f"The active network is {network.show_active()}")

Ape:

print(f"The active network is {networks.provider.network.name}")

Official docs: Networks

Testing and Reverts

Brownie revert helpers and exceptions map to Ape testing helpers and exceptions.

Brownie:

with brownie.reverts("Ownable: caller is not the owner"):
    fund_me.withdraw({"from": bad_actor})

Ape:

with ape.reverts("Ownable: caller is not the owner"):
    fund_me.withdraw(sender=bad_actor)

For Brownie tests catching the generic VM error exception:

Brownie:

with pytest.raises(exceptions.VirtualMachineError):
    fund_me.withdraw({"from": bad_actor})

Ape:

from ape.exceptions import ContractLogicError

with pytest.raises(ContractLogicError):
    fund_me.withdraw(sender=bad_actor)

Official docs: Testing

Testing: pytest Fixtures

Ape tests use pytest fixtures from the ape-test plugin. In Ape, contract types are not injected as pytest fixtures. Use the project fixture and access contracts as project.ContractName.

Remove Brownie’s fn_isolation fixture when migrating tests. Ape handles test isolation through its pytest plugin, so the fixture has no Ape equivalent and should be deleted.

Brownie:

@pytest.fixture(autouse=True)
def isolate(fn_isolation):
    pass

Use Ape’s accounts fixture with project for deployments:

Brownie:

def test_deploy(Token, accounts):
    account = accounts[0]
    token = Token.deploy({"from": account})

Ape:

def test_deploy(project, accounts):
    account = accounts[0]
    token = project.Token.deploy(sender=account)

Update contract fixture patterns the same way:

Brownie:

@pytest.fixture
def token(Token, accounts):
    return Token.deploy({"from": accounts[0]})

Ape:

@pytest.fixture
def token(project, accounts):
    return project.Token.deploy(sender=accounts[0])

If tests manually use chain snapshots, chain.snapshot() remains similar, but Brownie’s chain.revert() should become Ape’s chain.restore().

Brownie:

chain.snapshot()
chain.revert()

Ape:

chain.snapshot()
chain.restore()

Brownie provides a web3 pytest fixture for direct Web3.py access in tests. Ape does not provide a web3 fixture.

If direct Web3 access is needed, use the active provider.

Brownie:

def test_block_number(web3):
    assert web3.eth.block_number >= 0

Ape:

def test_block_number(chain):
    assert chain.provider.web3.eth.block_number >= 0

Official docs: Testing

Config Files

Brownie configuration values should move into Ape’s ape-config.yaml structure. Wallet private keys are not copied into config; import an Ape account alias instead with ape accounts import <alias> and use accounts.load("<alias>") from scripts.

Brownie (brownie-config.yaml):

dependencies:
  - smartcontractkit/[email protected]
compiler:
  solc:
    remappings:
      - "@chainlink=smartcontractkit/[email protected]"
wallets:
  from_key: ${PRIVATE_KEY}
networks:
  development:
    verify: false

Ape (ape-config.yaml):

name: migrated-ape-project
plugins:
  - name: solidity
solidity:
  version: 0.8.20
  import_remapping:
    - "@chainlink=smartcontractkit/[email protected]"
ethereum:
  default_network: local

Official docs: Config

Automated Migration with ApeShift

ApeShift applies deterministic Brownie-to-Ape rewrites, validation, reports, and manual-review TODOs.

npx codemod apeshift -t ./my-brownie-project

ApeShift is published in the Codemod registry: https://app.codemod.com/registry/apeshift

Warning

ApeShift is an unaudited third-party tool not officially built or supported by the ApeWorX team. Use it with caution.