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.