provider.ganache

Documentation for eth_defi.provider.ganache Python module.

Ganache integration.

Ganache is an EVM test backend and mainnet forking written in JavaScript from Truffle project.

This module contains utilities to automatically launch and manipulate ganache-cli process.

You need to have ganache-cli installed in order to use these.

How to install ganache-cli using npm:

npm install -g ganache

For more information about Ganache see

Most of this code is lifted from Brownie project (MIT) and it is not properly cleaned up yet.

Module Attributes

EVM_DEFAULT

The default hardfork rules used by Ganache

Functions

fork_network(json_rpc_url[, ...])

Creates the ganache "fork" of given JSON-RPC endpoint.

Classes

GanacheLaunch

Control ganache-cli processes launched on background.

Exceptions

InvalidArgumentWarning

Warned when there are issued with ganache-cli command line.

NoGanacheInstalled

We could not launch because ganache-cli command is missing

EVM_DEFAULT = 'london'

The default hardfork rules used by Ganache

exception NoGanacheInstalled

Bases: Exception

We could not launch because ganache-cli command is missing

__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 InvalidArgumentWarning

Bases: Warning

Warned when there are issued with ganache-cli command line.

__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.

class GanacheLaunch

Bases: object

Control ganache-cli processes launched on background.

Comes with a helpful close() method when it is time to put Ganache rest.

port: int

Which port was bound by the ganache

cmd: List[str]

Used command-line to spin up ganache-cli

json_rpc_url: str

Where does Ganache listen to JSON-RPC

process: psutil.Popen

UNIX process that we opened

close(verbose=False, block=True, block_timeout=30)

Kill the ganache-cli process.

Ganache is pretty hard to kill, so keep killing it until it dies and the port is free again.

Parameters
  • block – Block the execution until Ganache has terminated

  • block_timeout – How long we give for Ganache to clean up after itself

  • verbose – If set, dump anything in Ganache stdout to the Python logging using level INFO.

__init__(port, cmd, json_rpc_url, process)
Parameters
  • port (int) –

  • cmd (List[str]) –

  • json_rpc_url (str) –

  • process (psutil.Popen) –

Return type

None

fork_network(json_rpc_url, unlocked_addresses=[], cmd='ganache-cli', port=19999, evm_version='london', block_time=0, quiet=False, launch_wait_seconds=20.0)

Creates the ganache “fork” of given JSON-RPC endpoint.

Warning

This function is not recommended due to stability issues with Ganache. Use eth_defi.anvil.fork_network_anvil() instead.

Forking a mainnet is common way to test against live deployments. This function invokes ganache-cli command and tells it to fork a given JSON-RPC endpoint.

A subprocess is started on the background. To stop this process, call eth_defi.ganache.GanacheLaunch.close(). This function waits launch_wait_seconds in order to ganache-cli process to start and complete the chain fork.

Note

Currently only supports HTTP JSON-RPC connections.

Warning

Forking a network with ganache-cli is a slow process. It is recommended that you use fast Ethereum Tester based testing if possible.

Here is an example that forks BNB chain mainnet and transfer 500 BUSD stablecoin to a test account we control:

@pytest.fixture()
def large_busd_holder() -> HexAddress:
    # A random account picked from BNB Smart chain that holds a lot of BUSD.
    # Binance Hot Wallet 6
    return HexAddress(HexStr("0x8894E0a0c962CB723c1976a4421c95949bE2D4E3"))


@pytest.fixture()
def ganache_bnb_chain_fork(large_busd_holder) -> str:
    # Create a testable fork of live BNB chain.
    mainnet_rpc = os.environ["BNB_CHAIN_JSON_RPC"]
    launch = fork_network(
        mainnet_rpc,
        unlocked_addresses=[large_busd_holder],
    )
    yield launch.json_rpc_url
    # Wind down Ganache process after the test is complete
    launch.close()


@pytest.fixture
def web3(ganache_bnb_chain_fork: str):
    # Set up a local unit testing blockchain
    return Web3(HTTPProvider(ganache_bnb_chain_fork))


def test_mainnet_fork_transfer_busd(web3: Web3, large_busd_holder: HexAddress, user_1: LocalAccount):
    # 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.

Polygon needs to set a specific EVM version:

mainnet_rpc = os.environ["POLYGON_JSON_RPC"]
launch = fork_network(mainnet_rpc, evm_version="istanbul")

If ganache-cli refuses to terminate properly, you can kill a process by a port with:

# Kill any process listening to localhost:19999
kill -SIGKILL $(lsof -ti:19999)

This function uses Python logging subsystem. If you want to see error/info/debug logs with pytest you can do:

pytest --log-cli-level=debug

For public JSON-RPC endpoints check

Parameters
  • cmd – Override ganache-cli command. If not given we look up from PATH.

  • json_rpc_url (str) – HTTP JSON-RPC URL of the network we want to fork

  • 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

  • port – Localhost port we bind for Ganache JSON-RPC

  • launch_wait_seconds – How long we wait ganache-cli to start until giving up

  • evm_version – “london” for the default hard fork

  • block_time – How long Ganache takes to mine a block. Default is zero and any RPC transaction will immediately return with the transaction inclusion. Set to 1 so that you can poll the transaction as you would do with a live JSON-RPC node.

  • quiet – Disable extensive logging. If there is a lot of Ganache logging it seems to crash on Github CI.

Return type

eth_defi.provider.ganache.GanacheLaunch