Using Brownie to Deploy To Moonbeam¶
Introduction¶
Brownie is an Ethereum development environment that helps Python developers manage and automate the recurring tasks inherent to building smart contracts and DApps. Brownie can directly interact with Moonbeam's Ethereum API so it can also be used to deploy smart contracts on Moonbeam.
This guide will cover how to use Brownie to compile, deploy, and interact with Ethereum smart contracts on the Moonbase Alpha TestNet. This guide can also be adapted for Moonbeam, Moonriver, or Moonbeam development node.
Please note that Brownie is no longer actively maintained. You can check out Ape as an alternative Python Ethereum development environment.
Checking Prerequisites¶
To get started, you will need the following:
- Have MetaMask installed and connected to Moonbase Alpha
- Have an account with funds. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the Moonbase Alpha Faucet
- To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported Endpoint Providers
For this guide, Python version 3.9.10, pip version 22.0.3, and pipx version 1.0.0 were used.
Creating a Brownie Project¶
You will need to install Brownie and create a Brownie project if you don't already have one. You can choose to either create an empty project or use a Brownie mix, which is essentially a template to build your project on. For this example, you can create an empty project. You can get started by completing the following steps:
-
Create a directory for your project
mkdir brownie && cd brownie
-
If you don't already have
pipx
installed, go ahead and install itpython3 -m pip install --user pipx python3 -m pipx ensurepath
-
Install Brownie using
pipx
, which is used to run executables installed locally in your project. Brownie will be installed into a virtual environment and be available directly from the command linepipx install eth-brownie
Note
A common error while installing Brownie on Ubuntu is:
pip seemed to fail to build package: pyyaml==5.4.1 Some possibly relevant errors from pip install: error: subprocess-exited-with-error AttributeError: cython_sources Error installing eth-brownie.
This can be resolved by using the following command:
pip3 install wheel && \ pip3 install --no-build-isolation "Cython<3" "pyyaml==5.4.1" && \ pip3 install --upgrade --no-cache-dir eth-brownie
-
Create a project
brownie init
brownie init Brownie v1.19.1 - Python development framework for Ethereum
SUCCESS: A new Brownie project has been initialized at /home/moonbeam/brownie
ls build contracts interfaces reports scripts tests
Your Brownie project should contain the following empty directories:
- build - for project data such as contract artifacts from compilation
- contracts - to store the smart contract files
- interfaces - for smart contract interfaces that are required for your project
- reports - for JSON report files for use in the Brownie GUI
- scripts - where Python scripts used for deploying contracts or other automated tasks will live
- tests - to store Python scripts for testing your project. Brownie uses the
pytest
framework for unit testing
Another important file to note that is not included in an empty project is the brownie-config.yaml
configuration file. The configuration file is optional and comes in handy when customizing specific settings such as a default network, compiler version and settings, and more.
Network Configuration¶
To deploy to a Moonbeam-based network, you'll need to add and configure the network. Network configurations in Brownie are added from the command line. Brownie can be used with both development and live environments.
Moonbeam, Moonriver, and Moonbase Alpha are supported out of the box with Brownie as of version 1.19.3. To view the complete list of supported networks, you can run the following command:
brownie networks list
Moonbeam ├─Mainnet: moonbeam-main └─Moonbase Alpha: moonbeam-test Moonriver └─Mainnet: moonriver-main
If you're looking to deploy a contract to a Moonbeam development node you'll need to add the network configurations. Under the hood, Brownie uses Ganache for development environments. However, since a Moonbeam development node acts as your own personal development environment, Ganache isn't needed. Therefore, you can configure a development node as a "live" network.
To add Moonbeam development node configurations, you can run the following command:
brownie networks add Moonbeam moonbeam-dev host=http://127.0.0.1:9944 name=Development chainid=1281
If you successfully added the network, you'll see a success message along with the network details in the terminal.
To deploy to a Moonbeam network, or run tests on a specific network, you can specify the network by appending the following to the given command:
--network moonbeam-main
--network moonriver-main
--network moonbeam-test
--network moonbeam-dev
If you would like to set a default network, you can do so by adding the following snippet to the brownie-config.yaml
configuration file:
networks:
default: moonbeam-main
networks:
default: moonriver-main
networks:
default: moonbeam-test
networks:
default: moonbeam-dev
Note
Keep in mind that the brownie-config.yaml
file isn't automatically created, you can optionally create it yourself.
Setting your Networks RPC URLs¶
It is recommended that you override the default Brownie RPC URLs to your own RPC endpoint or the public Moonbeam network endpoints. You can override the default Brownie RPC URL for each network as follows:
brownie networks modify moonbeam-main host=INSERT_RPC_API_ENDPOINT
brownie networks modify moonriver-main host=INSERT_RPC_API_ENDPOINT
brownie networks modify moonbeam-test host=https://rpc.api.moonbase.moonbeam.network
SUCCESS: Network 'Mainnet' has been modified └─Mainnet ├─id: moonbeam-main ├─chainid: 1284 ├─explorer: https://api-moonbeam.moonscan.io/api ├─host: https://rpc.api.moonbeam.network └─multicall2: 0x1337BedC9D22ecbe766dF105c9623922A27963EC
Account Configuration¶
Before you can deploy a contract, you'll need to configure your account, which is also done from the command line. To add a new account you can run:
brownie accounts new INSERT_ACCOUNT_NAME
Make sure to replace INSERT_ACCOUNT_NAME
with your name of choice. For this example, alice
will be used as the account name.
You'll be prompted to enter in your private key and a password to encrypt the account with. If the account was successfully configured, you'll see your account address printed to the terminal.
Enter the private key you wish to add: 0x5b92d6e98884f76de468fa3f6278f880748bebc13595d45af5bdc4da702133 Enter the password to encrypt this account with: SUCCESS: A new account 'Oxf24FF3a9CF04c71Dbc94D06566f7A27B94566cac' has been generated with the id 'alice'
The Contract File¶
Next you can create a contract inside of the contracts
directory. The smart contract that you'll deploy as an example will be called Box
, it will let you store a value that can be retrieved later. You can create a Box.sol
contract by running the following command:
cd contracts && touch Box.sol
Open the file and add the following contract to it:
// contracts/Box.sol
pragma solidity ^0.8.1;
contract Box {
uint256 private value;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
}
Compiling Solidity¶
To compile the contract you can simply run:
brownie compile
New compatible solc version available: 0.8.4 Compiling contracts... Solc version: 0.8.4 Optimizer: Enabled Runs: 200 EVM Version: Istanbul Generating build data... - Box Project has been compiled. Build artifacts saved at /home/moonbeam/brownie/build/contracts
Note
The first time you compile your contracts it may take longer than usual while the solc
binary is installed.
After compilation, you'll find the build artifacts in the build/contracts
directory. The artifacts contain the bytecode and metadata of the contract, which are .json
files. The build
directory should already be in the .gitignore
file but if it's not, it’s a good idea to add it there.
If you want to specify the compiler version or compilation settings, you can do so in the brownie-config.yaml
file. Please note that if you haven't already created this file, you will need to do so. Then you can specify the compiler like so:
compiler:
evm_version: null
solc:
version: 0.8.13
optimizer:
enabled: true
runs: 200
Note
You can view the list of EVM versions supported by Brownie in their documentation.
Your contracts will only be compiled again if Brownie notices that a change has been made. To force a new compilation, you can run:
brownie compile --all
Deploying the Contract¶
In order to deploy the Box.sol
smart contract, you will need to write a simple deployment script. You can create a new file under the scripts
directory and name it deploy.py
:
cd scripts && touch deploy.py
Next, you need to write your deployment script. To get started start, take the following steps:
- Import the
Box
contract and theaccounts
module frombrownie
- Load your account using
accounts.load()
which decrypts a keystore file and returns the account information for the given account name - Use the
deploy
method that exists within this instance to instantiate the smart contract specifying thefrom
account and thegas_limit
# scripts/deploy.py
from brownie import Box, accounts
def main():
account = accounts.load("alice")
return Box.deploy({"from": account, "gas_limit": "200000"})
You can now deploy the Box.sol
contract using the run
command and specifying the network:
brownie run scripts/deploy.py --network moonbeam-main
brownie run scripts/deploy.py --network moonriver-main
brownie run scripts/deploy.py --network moonbeam-test
brownie run scripts/deploy.py --network moonbeam-dev
After a few seconds, the contract is deployed, and you should see the address in the terminal.
BrownieProject is the active project.
Running 'scripts/deploy.py: :main Enter password for "alice": Transaction sent: Oxf091d0415d1a4614ccd76a5f5f985fdf6abbd68d7481647fb3144dfecffb333 Gas price: 1.0 gwei Gas limit: 200000 Nonce: 15298 Box.constructor confirmed Block: 1901367 Gas used: 103095 (51.55%) Box deployed at: OxccA4aDdD51Af1E76beZaBE5FD04A82EB6063C65
Congratulations, your contract is live! Save the address, as you will use it to interact with this contract instance in the next step.
Interacting with the Contract¶
You can interact with contracts using the Brownie console for quick debugging and testing or you can also write a script to interact.
Using Brownie Console¶
To interact with your newly deployed contract, you can launch the Brownie console
by running:
brownie console --network moonbeam-main
brownie console --network moonriver-main
brownie console --network moonbeam-test
brownie console --network moonbeam-dev
The contract instance will automatically be accessible from the console. It will be wrapped in a ContractContainer
which also enables you to deploy new contract instances. To access the deployed contract you can use Box[0]
. To call the store
method and set the value to 5
, you can take the following steps:
- Create a variable for the contract
box = Box[0]
- Call the
store
method using your account and set the value to5
box.store(5, {'from': accounts.load('alice'), 'gas_limit': '50000'})
- Enter the password for your account
The transaction will be signed by your account and broadcasted to the network. Now, you can retrieve the value by taking these steps:
- Call the
retrieve
methodbox.retrieve({'from': accounts.load('alice')})
- Enter your password
You should see 5
or the value you have stored initially.
BrownieProject is the active project. Brownie environment is ready. box = Box[0]
box. store(5, {'from': accounts. load('alice"), 'gas-limit: "50000"). Enter password for "alice": Transaction sent: 0x2418038735934917861dfa77068fe6dadd0b3c7e4362d6f73c1712aaf6a89a Gas price: 1.0 gwei Box.store confirmed Gas limit: 50000 Nonce: 15299 Box.store confirmed Block: 1901372 Gas used: 44593 (89.19%)
Transaction '0×24180387359349178b1dfa770e68fe6dadd0b3c7e4362d6f73c1712aaf6a89a' box.retrieve ({'from': accounts. load('alice")}) Enter password for "alice": 5
Using a Script¶
You can also write a script to interact with your newly deployed contract. To get started, you can create a new file in the scripts
directory:
cd scripts && touch store-and-retrieve.py
Next, you need to write your script that will store and then retrieve a value. To get started, take the following steps:
- Import the
Box
contract and theaccounts
module frombrownie
- Load your account using
accounts.load()
which decrypts a keystore file and returns the account information for the given account name - Create a variable for the
Box
contract - Use the
store
andretrieve
functions to store a value and then retrieve it and print it to the console
# scripts/store-and-retrieve.py
from brownie import Box, accounts
def main():
account = accounts.load("alice")
box = Box[0]
store = box.store(5, {"from": accounts.load("alice"), "gas_limit": "50000"})
retrieve = box.retrieve({"from": accounts.load("alice")})
print("Transaction hash for updating the stored value: " + store)
print("Stored value: " + retrieve)
To run the script, you can use the following command:
brownie run scripts/store-and-retrieve.py --network moonbeam-main
brownie run scripts/store-and-retrieve.py --network moonriver-main
brownie run scripts/store-and-retrieve.py --network moonbeam-test
brownie run scripts/store-and-retrieve.py --network moonbeam-dev
You'll need to enter the password for Alice to send the transaction to update the stored value. Once the transaction goes through, you should see a transaction hash and a value of 5
printed to the console.
Congratulations, you have successfully deployed and interacted with a contract using Brownie!
| Created: March 25, 2022