provider.anvil
Documentation for eth_defi.provider.anvil Python module.
Anvil integration.
_ ..anvil:
This module provides Python integration for Anvil.
Anvil is a blazing-fast local testnet node implementation in Rust from Foundry project
Anvil can replace
eth_tester.main.EthereumTesteras the unit/integration test backend.Anvil is mostly used in mainnet fork test cases.
Anvil is a more stable an alternative to Ganache (
eth_defi.ganache)Anvil is part of Foundry, a toolkit for Ethereum application development.
To install Anvil on:
curl -L https://foundry.paradigm.xyz | bash
PATH=~/.foundry/bin:$PATH
foundryup # Needs to be in path, or installation fails
This will install foundryup, anvil at ~/.foundry/bin and adds the folder to your shell rc file PATH.
For more information see Anvil reference.
See also eth_defi.trace for Solidity tracebacks using Anvil.
The code was originally lifted from Brownie project.
Module Attributes
Mappings between Anvil command line parameters and our internal argument names |
Functions
|
On Anvil forked mainnet, create a wallet with some USDC funds. |
|
Call evm_snapshot on Anvil |
|
Creates Anvil unit test backend or mainnet fork. |
|
Are we connected to Anvil node. |
|
Have we forked mainnet for this test. |
|
Creates Anvil unit test backend or mainnet fork. |
|
Call evm_snapshot on Anvil |
|
Make a request to special named EVM JSON-RPC endpoint. |
|
Call evm_setNextBlockTimestamp on Anvil. |
|
Call evm_revert on Anvil |
|
Call anvil_setBalance on Anvil |
|
Call emv_increaseTime on Anvil |
|
Call evm_snapshot on Anvil |
|
Make Anvil mainnet fork to accept transactions to any Ethereum account. |
Classes
Control Anvil processes launched on background. |
Exceptions
Lifted from Brownie. |
|
Lifted from Brownie. |
- exception InvalidArgumentWarning
Bases:
WarningLifted from Brownie.
- __init__(*args, **kwargs)
- __new__(**kwargs)
- add_note()
Exception.add_note(note) – add a note to the exception
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- exception RPCRequestError
Bases:
ExceptionLifted from Brownie.
- __init__(*args, **kwargs)
- __new__(**kwargs)
- add_note()
Exception.add_note(note) – add a note to the exception
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- CLI_FLAGS = {'block_time': '--block-time', 'chain_id': '--chain-id', 'code_size_limit': '--code-size-limit', 'default_balance': '--balance', 'fork': '--fork-url', 'fork_block_number': '--fork-block-number', 'gas_limit': '--gas-limit', 'hardfork': '--hardfork', 'host': '--host', 'port': '--port', 'steps_tracing': '--steps-tracing', 'verbose': '-vvvvv'}
Mappings between Anvil command line parameters and our internal argument names
- make_anvil_custom_rpc_request(web3, method, args=None)
Make a request to special named EVM JSON-RPC endpoint.
- Parameters
- Returns
RPC result
- Raises
RPCRequestError – In the case RPC method errors
- Return type
- class AnvilLaunch
Bases:
objectControl Anvil processes launched on background.
Comes with a helpful
close()method when it is time to put Anvil rest.- process: psutil.Popen
UNIX process that we opened
- close(log_level=None, block=True, block_timeout=30)
Close the background Anvil process.
- launch_anvil(fork_url=None, unlocked_addresses=None, cmd='anvil', port=(19999, 29999, 25), block_time=0, launch_wait_seconds=20.0, attempts=3, hardfork='cancun', gas_limit=None, steps_tracing=False, test_request_timeout=3.0, fork_block_number=None, log_wait=False, code_size_limit=None, rpc_smoke_test=True, verbose=False)
Creates Anvil unit test backend or mainnet fork.
Anvil can be used as web3.py test backend instead of EthereumTester. Anvil offers faster execution and tracing - see
eth_defi.trace.Forking a mainnet is a common way to test against live deployments. This function invokes anvil command and tells it to fork a given JSON-RPC endpoint.
When called, a subprocess is started on the background. To stop this process, call
eth_defi.anvil.AnvilLaunch.close().This function waits launch_wait_seconds in order to anvil process to start and complete the chain fork.
Unit test backend:
See eth_defi.tests.enzyme.conftest for an example how to use Anvil in your Python based unit test suite
Mainnet fork: Here is an example that forks BNB chain mainnet and transfer 500 BUSD stablecoin to a test account we control:
from eth_defi.anvil import fork_network_anvil from eth_defi.chain import install_chain_middleware from eth_defi.gas import node_default_gas_price_strategy @pytest.fixture() def large_busd_holder() -> HexAddress: # An onchain address with BUSD balance # Binance Hot Wallet 6 return HexAddress(HexStr("0x8894E0a0c962CB723c1976a4421c95949bE2D4E3")) @pytest.fixture() def user_1() -> LocalAccount: # Create a test account return Account.create() @pytest.fixture() def anvil_bnb_chain_fork(request, large_busd_holder, user_1, user_2) -> str: # Create a testable fork of live BNB chain. mainnet_rpc = os.environ["BNB_CHAIN_JSON_RPC"] launch = fork_network_anvil(mainnet_rpc, unlocked_addresses=[large_busd_holder]) try: yield launch.json_rpc_url finally: # Wind down Anvil process after the test is complete launch.close(log_level=logging.ERROR) @pytest.fixture() def web3(anvil_bnb_chain_fork: str): # Set up a local unit testing blockchain # https://web3py.readthedocs.io/en/stable/examples.html#contract-unit-tests-in-python web3 = Web3(HTTPProvider(anvil_bnb_chain_fork)) # Anvil needs POA middlware if parent chain needs POA middleware install_chain_middleware(web3) web3.eth.set_gas_price_strategy(node_default_gas_price_strategy) return web3 def test_anvil_fork_transfer_busd(web3: Web3, large_busd_holder: HexAddress, user_1: LocalAccount): # Forks the BNB chain mainnet and transfers from USDC to the user. # BUSD deployment on BNB chain # https://bscscan.com/token/0xe9e7cea3dedca5984780bafc599bd69add087d56 busd_details = fetch_erc20_details(web3, "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56") busd = busd_details.contract # Transfer 500 BUSD to the user 1 tx_hash = busd.functions.transfer(user_1.address, 500 * 10**18).transact({"from": large_busd_holder}) # Because Ganache has instamine turned on by default, we do not need to wait for the transaction receipt = web3.eth.get_transaction_receipt(tx_hash) assert receipt.status == 1, "BUSD transfer reverted" assert busd.functions.balanceOf(user_1.address).call() == 500 * 10**18
See the full example in tests source code.
If anvil refuses to terminate properly, you can kill a process by a port in your terminal:
# Kill any process listening to localhost:19999 kill -SIGKILL $(lsof -ti:19999)
See also
Note
Looks like we have some issues Anvil instance lingering around even after AnvilLaunch.close() if scoped pytest fixtures are used.
- Parameters
cmd – Override anvil command. If not given we look up from PATH.
HTTP JSON-RPC URL of the network we want to fork.
If not given launch an empty test backend.
unlocked_addresses (list[Union[eth_typing.evm.HexAddress, str]]) – List of addresses of which ownership we take to allow test code to transact as them
Localhost port we bind for Anvil JSON-RPC.
The tuple format is (min port, max port, opening attempts).
By default, takes a tuple range and tries to open a a random port in the range, until empty found. This allows to run multiple parallel Anvil’s during unit testing with
pytest -n auto.You can also specify an individual port.
launch_wait_seconds – How long we wait anvil to start until giving up
block_time –
How long Anvil takes to mine a block. Default is zero: Anvil is in automining mode and creates a new block for each new transaction.
Set to 1 or higher so that you can poll the transaction as you would do with a live JSON-RPC node.
attempts –
How many attempts we do to start anvil.
Anvil launch may fail without any output. This could be because the given JSON-RPC node is throttling your API requests. In this case we just try few more times again by killing the Anvil process and starting it again.
hardfork (str | None) – EVM version to use
step_tracing –
Enable Anvil step tracing.
Needed to get structured logs.
Only needed on GoEthereum style tracing, not needed for Parity style tracing.
test_request_timeout – Set the timeout fro the JSON-RPC requests that attempt to determine if Anvil was successfully launched.
fork_block_number (Optional[int]) –
For at a specific block height of the parent chain.
If not given, fork at the latest block. Needs an archive node to work.
rpc_smoke_test – Check that the RPC is working before attempting to start Anvil
verbose –
Make Anvil the proces to dump a lot of stuff to stdout/stderr.
code_size_limit (int) –
- Parma code_size_limit
Max smart contract size
- Parma log_wait
Display info level logging while waiting for Anvil to start.
- Return type
- unlock_account(web3, address)
Make Anvil mainnet fork to accept transactions to any Ethereum account.
This is even when we do not have a private key for the account.
- Parameters
web3 (web3.main.Web3) – Web3 instance
address (str) – Account to unlock
- sleep(web3, seconds)
Call emv_increaseTime on Anvil
- mine(web3, timestamp=None, increase_timestamp=0)
Call evm_setNextBlockTimestamp on Anvil.
Mine blocks, optionally set the time of the new block.
- revert(web3, snapshot_id)
Call evm_revert on Anvil
- load_state(web3, state)
Call evm_snapshot on Anvil
- set_balance(web3, address, raw_amount)
Call anvil_setBalance on Anvil
- is_anvil(web3)
Are we connected to Anvil node.
You need to change some behavior depending if you are connected to a real node or Anvil simulation.
This can be either
Mainnet work (chain id copied from the forked blockchain)
Anvil test backend
See also
launch_anvil()Warning
This method will crash with Base mainnet sequencer:
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://mainnet-sequencer.base.org/.- Parameters
web3 (web3.main.Web3) – Web3 connection instance to check
- Returns
True if we think we are connected to Anvil
- Return type
- is_mainnet_fork(web3)
Have we forked mainnet for this test.
Only relevant with
is_anvil()
- Returns
True if we think we are connected to a forked mainnet, False if we think we are a standalone local dev chain.
- Parameters
web3 (web3.main.Web3) –
- Return type
- create_fork_funded_wallet(web3, usdc_address, large_usdc_holder, usdc_amount=Decimal('10000'), eth_amount=Decimal('10'))
On Anvil forked mainnet, create a wallet with some USDC funds.
Make a new private key account on a forked mainnet
Top this up with ETH and USDC from a large USDC holder
- Parameters
web3 (web3.main.Web3) –
usdc_address (eth_typing.evm.HexAddress) –
large_usdc_holder (eth_typing.evm.HexAddress) –
- Return type
eth_defi.hot_wallet.HotWallet
- fork_network_anvil(fork_url=None, unlocked_addresses=None, cmd='anvil', port=(19999, 29999, 25), block_time=0, launch_wait_seconds=20.0, attempts=3, hardfork='cancun', gas_limit=None, steps_tracing=False, test_request_timeout=3.0, fork_block_number=None, log_wait=False, code_size_limit=None, rpc_smoke_test=True, verbose=False)
Creates Anvil unit test backend or mainnet fork.
Anvil can be used as web3.py test backend instead of EthereumTester. Anvil offers faster execution and tracing - see
eth_defi.trace.Forking a mainnet is a common way to test against live deployments. This function invokes anvil command and tells it to fork a given JSON-RPC endpoint.
When called, a subprocess is started on the background. To stop this process, call
eth_defi.anvil.AnvilLaunch.close().This function waits launch_wait_seconds in order to anvil process to start and complete the chain fork.
Unit test backend:
See eth_defi.tests.enzyme.conftest for an example how to use Anvil in your Python based unit test suite
Mainnet fork: Here is an example that forks BNB chain mainnet and transfer 500 BUSD stablecoin to a test account we control:
from eth_defi.anvil import fork_network_anvil from eth_defi.chain import install_chain_middleware from eth_defi.gas import node_default_gas_price_strategy @pytest.fixture() def large_busd_holder() -> HexAddress: # An onchain address with BUSD balance # Binance Hot Wallet 6 return HexAddress(HexStr("0x8894E0a0c962CB723c1976a4421c95949bE2D4E3")) @pytest.fixture() def user_1() -> LocalAccount: # Create a test account return Account.create() @pytest.fixture() def anvil_bnb_chain_fork(request, large_busd_holder, user_1, user_2) -> str: # Create a testable fork of live BNB chain. mainnet_rpc = os.environ["BNB_CHAIN_JSON_RPC"] launch = fork_network_anvil(mainnet_rpc, unlocked_addresses=[large_busd_holder]) try: yield launch.json_rpc_url finally: # Wind down Anvil process after the test is complete launch.close(log_level=logging.ERROR) @pytest.fixture() def web3(anvil_bnb_chain_fork: str): # Set up a local unit testing blockchain # https://web3py.readthedocs.io/en/stable/examples.html#contract-unit-tests-in-python web3 = Web3(HTTPProvider(anvil_bnb_chain_fork)) # Anvil needs POA middlware if parent chain needs POA middleware install_chain_middleware(web3) web3.eth.set_gas_price_strategy(node_default_gas_price_strategy) return web3 def test_anvil_fork_transfer_busd(web3: Web3, large_busd_holder: HexAddress, user_1: LocalAccount): # Forks the BNB chain mainnet and transfers from USDC to the user. # BUSD deployment on BNB chain # https://bscscan.com/token/0xe9e7cea3dedca5984780bafc599bd69add087d56 busd_details = fetch_erc20_details(web3, "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56") busd = busd_details.contract # Transfer 500 BUSD to the user 1 tx_hash = busd.functions.transfer(user_1.address, 500 * 10**18).transact({"from": large_busd_holder}) # Because Ganache has instamine turned on by default, we do not need to wait for the transaction receipt = web3.eth.get_transaction_receipt(tx_hash) assert receipt.status == 1, "BUSD transfer reverted" assert busd.functions.balanceOf(user_1.address).call() == 500 * 10**18
See the full example in tests source code.
If anvil refuses to terminate properly, you can kill a process by a port in your terminal:
# Kill any process listening to localhost:19999 kill -SIGKILL $(lsof -ti:19999)
See also
Note
Looks like we have some issues Anvil instance lingering around even after AnvilLaunch.close() if scoped pytest fixtures are used.
- Parameters
cmd – Override anvil command. If not given we look up from PATH.
HTTP JSON-RPC URL of the network we want to fork.
If not given launch an empty test backend.
unlocked_addresses (list[Union[eth_typing.evm.HexAddress, str]]) – List of addresses of which ownership we take to allow test code to transact as them
Localhost port we bind for Anvil JSON-RPC.
The tuple format is (min port, max port, opening attempts).
By default, takes a tuple range and tries to open a a random port in the range, until empty found. This allows to run multiple parallel Anvil’s during unit testing with
pytest -n auto.You can also specify an individual port.
launch_wait_seconds – How long we wait anvil to start until giving up
block_time –
How long Anvil takes to mine a block. Default is zero: Anvil is in automining mode and creates a new block for each new transaction.
Set to 1 or higher so that you can poll the transaction as you would do with a live JSON-RPC node.
attempts –
How many attempts we do to start anvil.
Anvil launch may fail without any output. This could be because the given JSON-RPC node is throttling your API requests. In this case we just try few more times again by killing the Anvil process and starting it again.
hardfork (str | None) – EVM version to use
step_tracing –
Enable Anvil step tracing.
Needed to get structured logs.
Only needed on GoEthereum style tracing, not needed for Parity style tracing.
test_request_timeout – Set the timeout fro the JSON-RPC requests that attempt to determine if Anvil was successfully launched.
fork_block_number (Optional[int]) –
For at a specific block height of the parent chain.
If not given, fork at the latest block. Needs an archive node to work.
rpc_smoke_test – Check that the RPC is working before attempting to start Anvil
verbose –
Make Anvil the proces to dump a lot of stuff to stdout/stderr.
code_size_limit (int) –
- Parma code_size_limit
Max smart contract size
- Parma log_wait
Display info level logging while waiting for Anvil to start.
- Return type