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.