How to Deploy a Smart Contract on the Ropsten Testnet in Brownie

In my previous article, we looked at how to set up Brownie, create a project with a simple smart contract, interact with it on a local blockchain and run a unit test. If you haven’t read it yet, please check the following link.

Brownie – Smart Contracts in Python

Now, we are ready to deploy our smart contract to a public testnet so that other people can try it out. 

In this article, we first look at deploying using a third-party API provider (Infura). Then, we will have a quick look at how to use our own Ethereum node. 

Ethereum networks

Public testnets

When people buy and sell Ether on an exchange or NFTs on a platform like OpenSea, the transactions occur on the public Ethereum production blockchain, the Mainnet. But there are also several public Ethereum networks that are used for development and testing purposes. They are called testnets.

Due to the nature of blockchain technology, it is difficult to update a smart contract once it has been deployed to the Mainnet, so it is generally a good idea to thoroughly test it on a testnet in advance. 

Some of the widely used Ethereum public testnets are listed below. Each testnet is a separate blockchain, meaning they are independent and do not interact with each other. For example, an account on the Ropsten testnet cannot send Ether to an account on the Rinkeby network. 

NameDescriptionChain ID
RopstenA proof-of-work testnet. This means it’s the best like-for-like representation of Ethereum.3
RinkebyA proof-of-authority testnet for those running Geth client.4
GĂśrliA proof-of-authority testnet that works across clients.5
KovanA proof-of-authority testnet for those running OpenEthereum clients. 42

How to connect to a public testnet

Ethereum is a collection of computers (or nodes) running specific software (or Ethereum client) communicating with each other over the Internet. So, we need to connect to one of the nodes when deploying a smart contract. There are two ways to connect to a node:

  • Running a node ourselves
  • Using an API provider

As Ethereum is an open-source project, anyone with sufficient Linux administration knowledge and necessary hardware resources can download the client software and run an Ethereum node. I think it is an excellent way to learn how Ethereum works. Still, it might not be suitable for everyone because of the lack of necessary hardware resources, knowledge, interests, time, or a combination of any of them.

The second way is to use an API provider, which gives us unique API endpoints (URLs) to interact with Ethereum networks. As there is no upfront financial cost and it is much quicker than setting up and running a node ourselves, this option is probably suitable for everyone.

There are several popular providers, but in this article, we use Infura as an example.

Set up Infura

We first need to create a free Infura account. Go to https://infura.io/ and click on the “SIGN UP” button in the top right-hand corner.

Follow the instructions and create an account. 

Then, log on to the website and click on the “CREATE NEW PROJECT” button on the main screen.

Select “Ethereum” from the PRODUCT drop-down menu and fill in the project name (any name we like).

On the next page, we can find the KEYS section. We need the project ID on the left-hand side, so copy the project ID. 

Then, open a terminal window and go to the Brownie project directory we created in the previous article. 

Now, we need to set the project ID to the environment variable WEB3_INFURA_PROJECT_ID. We can set it as shown below.

[~/brownie_test]$ export WEB3_INFURA_PROJECT_ID=691a20ed16eb439f8006a2b3edb45cdf

But this is probably not the best way because we will need to set the environment variable again if we start a new terminal session. So, instead of manually setting the environment variable each time, we can do the following to make it persistent.

First, create a file called .env in the project root directory and add the following line (replace the project ID value with your ID):

.env

WEB3_INFURA_PROJECT_ID=691a20ed16eb439f8006a2b3edb45cdf

Then, create another file called brownie-config.yaml in the project root directory and add the following line:

brownie-config.yaml
dotenv: .env 

Ensure that the .gitignore file contains the entry .env. This is to make sure that the .env file won’t be committed to the Git repository. 

The Infura setup is now complete.

Brownie network configuration

We can find the network settings in Brownie by running the brownie networks list command on the terminal.

[~/brownie_test]$ brownie networks list                
Brownie v1.17.1 - Python development framework for Ethereum

The following networks are declared:

Ethereum
  ├─Mainnet (Infura): mainnet
  ├─Ropsten (Infura): ropsten
  ├─Rinkeby (Infura): rinkeby
  ├─Goerli (Infura): goerli
  └─Kovan (Infura): kovan

Ethereum Classic
  ├─Mainnet: etc
  └─Kotti: kotti

Arbitrum
  └─Mainnet: arbitrum-main

Binance Smart Chain
  ├─Testnet: bsc-test
  └─Mainnet: bsc-main

Fantom Opera
  ├─Testnet: ftm-test
  └─Mainnet: ftm-main

Polygon
  ├─Mainnet (Infura): polygon-main
  └─Mumbai Testnet (Infura): polygon-test

XDai
  ├─Mainnet: xdai-main
  └─Testnet: xdai-test

Development
  ├─Ganache-CLI: development
  ├─Geth Dev: geth-dev
  ├─Hardhat: hardhat
  ├─Hardhat (Mainnet Fork): hardhat-fork
  ├─Ganache-CLI (Mainnet Fork): mainnet-fork
  ├─Ganache-CLI (BSC-Mainnet Fork): bsc-main-fork
  ├─Ganache-CLI (FTM-Mainnet Fork): ftm-main-fork
  ├─Ganache-CLI (Polygon-Mainnet Fork): polygon-main-fork
  └─Ganache-CLI (XDai-Mainnet Fork): xdai-main-fork

As we can see, Brownie comes with the network settings using Infura by default, so we don’t need to do anything. We only need to set the environment variable WEB3_INFURA_PROJECT_ID, which we already did in the previous section.

Let’s connect to the Ropsten testnet from the Brownie console. Run the brownie console command with the --network ropsten option as shown below. 

[~/brownie_test]$ brownie console --network ropsten
Brownie v1.17.1 - Python development framework for Ethereum

BrownieTestProject is the active project.
Brownie environment is ready.
>>> network.show_active()
'ropsten'
>>> network.is_connected()
True

Note: If you’ve just added your Infura project ID to the .env file, you might need to reopen a terminal window so that Brownie can read it.

Now, the network configuration in Brownie is complete.

How to deploy a smart contract

To deploy a smart contract to an Ethereum network, we need an Ethereum account. In my previous article, we used an account created by Ganache to deploy our smart contract, but it is not a persistent account. When deploying a smart contract to a testnet, we need a permanent account on the network, so let’s create a new account first. 

Create a deployment account

First, let’s check if there are any local accounts. Run the brownie accounts list command.

[~/brownie_test]$ brownie accounts list                     
Brownie v1.17.1 - Python development framework for Ethereum

Found 0 accounts:

In this case, there is no account. Let’s create a new one called deployment_account. When prompted, type a new password. Be sure to remember it because we will need it later.

[~/brownie_test]$ brownie accounts generate deployment_account
Brownie v1.17.1 - Python development framework for Ethereum

Generating a new private key...
mnemonic: 'xxxxx xxxx xxxx xxxxx xxxx xxxx xxxxx xxxxx xxxxxx xxxx xxxxxx xxxxxx'
Enter the password to encrypt this account with: 
SUCCESS: A new account '0x30e4E6290941A35d6988B52451B32badE7C7CbAC' has been generated with the id 'deployment_account'

The hex-decimal number (0x30e4E6290941A35d6988B52451B32badE7C7CbAC) is the account address. 

We can check that the account has been created by running the brownie accounts list command again.

[~/brownie_test]$ brownie accounts list
Brownie v1.17.1 - Python development framework for Ethereum

Found 1 account:
 └─deployment_account: 0x30e4E6290941A35d6988B52451B32badE7C7CbAC

Unlike the test accounts created by Ganache, this account does not have any Ether yet. We can check the balance from the console. Open a console with the --network ropsten  option to connect to the Ropsten testnet and load deployment_account we’ve just created. When prompted, type in the password when generating the account. Then run the balance() function, which shows that the balance is currently 0. 

[~/brownie_test]$ brownie console --network ropsten
Brownie v1.17.1 - Python development framework for Ethereum

BrownieTestProject is the active project.
Brownie environment is ready.
>>> account = accounts.load('deployment_account')
Enter password for "deployment_account": 
>>> account.balance()
0

When deploying a smart contract, we need to pay the gas because deployment is a transaction. So, we need to send some Ether to this account first. Unless you already have some Ether on the Ropsten testnet, the easiest way is to request some Ether on the Faucet. Go to Ropsten Ethereum Faucet website and type in the account address (make sure to replace the address value with your own) and press the “Send me test Ether” button.

As long as the Faucet is working, we should get 0.3 Ether after a few minutes. 

>>> web3.fromWei(account.balance(), 'ether')
Decimal('0.3')

Another way to get test Ether is by mining some on our own. As the Ropsten testnet uses the proof-of-work consensus mechanism, we can run our node and mine test Ether (at least until Ethereum moves to proof-of-stake). I will explain the overview later in this article.

Once the account has some Ether (0.1 ether should be enough), we are ready to go to the next step. 

Deploy a smart contract

In my previous article, we manually deployed the smart contract to the local blockchain. Although we can do the same, let’s create a deployment script this time. In the scripts subdirectory, create a file called deploy.py with the following content.

from brownie import SimpleStorage, accounts

def main():
    account = accounts.load('deployment_account')
    SimpleStorage.deploy({'from': account})

As we can see, this is a normal Python script. Just to recap, SimpleStorage is the smart contract we created (stored in contracts/storage.sol), which was taken from the Solidity documentation:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

Now, open a terminal window and run the command brownie run deploy.py --network ropsten as shown below. When prompted, type the account password.

[~/brownie_test]$ brownie run deploy.py --network ropsten
Brownie v1.17.1 - Python development framework for Ethereum

BrownieTestProject is the active project.

Running 'scripts/deploy.py::main'...
Enter password for "deployment_account": 
Transaction sent: 0x51f0a466abc059da5185a7dc280990157ee7741e5c0acbeae5674b91cbc026ee
  Gas price: 1.599530008 gwei   Gas limit: 99592   Nonce: 0
  SimpleStorage.constructor confirmed   Block: 11512310   Gas used: 90539 (90.91%)
  SimpleStorage deployed at: 0xafB83356eeeAA6E18B9a76126DE8edFD61BE5385

We can check our smart contract on https://ropsten.etherscan.io/. Copy our smart contract address (0xafB83356eeeAA6E18B9a76126DE8edFD61BE5385 in the example above) and paste it into the search box, which shows the information about our smart contract.

 Interact with the smart contract

We can interact with the smart contract using the Brownie console. Let’s start the console and connect to the Ropsten testnet.

[~/brownie_test]$ brownie console --network ropsten      
Brownie v1.17.1 - Python development framework for Ethereum

BrownieTestProject is the active project.
Brownie environment is ready.
>>>

Load the smart contract by specifying the address. Make sure to use the address of the deployed smart contract.

>>> simple_storage = SimpleStorage.at('0xafB83356eeeAA6E18B9a76126DE8edFD61BE5385')

Then, we can run the function get(), which should return the value 0 because we haven’t set a value yet.

>>> simple_storage.get()
0

To update the value, we need to send a transaction, so we need an account. Let’s use deployment_account. When prompted, type in the account password. Then, run the function set() to set the value to 5 by specifying the deployment_account to thefrom address. It should return a transaction object after 10 seconds or so.

>>> simple_storage.set(5, {'from': account})
Transaction sent: 0xd3945cbb7f45f8c877f38f869f78040a1952c299ff27a8c9931950efba0095af
  Gas price: 1.504997803 gwei   Gas limit: 47843   Nonce: 1
  SimpleStorage.set confirmed   Block: 11512386   Gas used: 43494 (90.91%)

<Transaction '0xd3945cbb7f45f8c877f38f869f78040a1952c299ff27a8c9931950efba0095af'>

We can check the value by running the get() function again, which now shows the value 5.

>>> simple_storage.get()
5

We can also find the transaction on Etherscan by searching the transaction hash value, which shows that the storage value has changed from 0 to 5.

How to use your own node (overview)

As I mentioned in the previous section, another way to connect to the Ropsten testnet is to run an Ethereum node on the testnet ourselves. We will need to set up a Linux server (locally or on a cloud) where we can install the Ethereum client software (Geth) and keep it running (at least while connecting to the testnet). 

When I set up a local Ethereum node, the following article was very helpful:

How to mine Ropsten testnet Ether

I will summarise some of the key points in the following sections. For more details, please read the article above.

Install Geth

I used Ubuntu 20.04.3 LTS. To download Geth, run the following commands:

$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt-get install -y software-properties-common
$ sudo apt-get update
$ sudo apt-get -y install ethereum

Once finished, you can verify the installation by running geth version.

$ geth version
Geth
Version: 1.10.12-stable
Git Commit: 6c4dc6c38827296dec5a49a6ea25fd7f0eb4ac77
Architecture: amd64
Go Version: go1.17.2
Operating System: linux
GOPATH=
GOROOT=go

Run Geth

Now you can start the Ethereum client. First, it will download the blockchain data, which will take a few hours (depending on the Internet connection). 

 
geth --ropsten --http --http.api eth,net,web3,personal,miner,admin,txpool,debug --http.corsdomain '*' --http.addr 192.168.1.18 --http.port 8545

You will need to replace the IP address (192.168.1.18) with the IP address of your node. The disk space required to download the blockchain data on Ropsten was currently about 130GB. 

You can check the status using the Geth console. Open a Linux terminal and run the command geth attach http://<IP address> (make sure to use the IP address of your own Ethereum node).

$ geth attach http://192.168.1.18:8545
Welcome to the Geth JavaScript console!

instance: Geth/v1.10.12-stable-6c4dc6c3/linux-amd64/go1.17.2
at block: 11512999 (Sun Nov 28 2021 16:36:58 GMT+0100 (CET))
 datadir: /home/mikio/.ethereum/ropsten
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> 

The command eth.syncing will show you the current syncing status. You might also need to add more peers. Please refer to the article above for more details.

(Optional) Mine Ether

Once the Ethereum client has synchronised the blockchain data and is up and running, you can also mine some Ether. It will require computational power, so it is generally recommended to use powerful GPUs. But it all depends on the network utilisation and will change when Ethereum moves to the proof-of-stake consensus mechanism, which is planned to take place sometime in 2022 (for more details, see The merge).

To mine Ether, you first need an account. Create an account using Brownie as shown in the previous section, or use a wallet such as Metamask. Then, set it to the coinbase address of the node from the Geth console.

> miner.setEtherbase("0x30e4E6290941A35d6988B52451B32badE7C7CbAC")

Then start the mining process.

> miner.start(4)

The argument 4 means 4 CPU cores to use. When the miner mines a block, the account will automatically receive the reward (generally about two ether per block). In my environment, about 10 to 20 ether was mined per day without additional powerful GPUs like an Nvidia graphics card. 

Connect to the node in Brownie

We can use our node to interact with the Ropsten testnet instead of using the Infura API endpoints. All we need to do is add a new network in Brownie.

Go to the terminal and run the command below. It will create a new network called ropsten-local using the specified IP address, so make sure to use the IP address of your node. The Chain ID is Ropsten’s ID, 3 (see the section “Public testnets” above).

[~/brownie_test]$ brownie networks add "Ethereum" ropsten-local host="http://192.168.1.18:8545" name="Ropsten (Local Geth)" chainid=3
Brownie v1.17.1 - Python development framework for Ethereum

SUCCESS: A new network 'Ropsten (Local Geth)' has been added
  └─Ropsten (Local Geth)
    ├─id: ropsten-local
    ├─chainid: 3
    └─host: http://192.168.1.18:8545

Then, you can connect to the Ropsten testnet using the option –network ropsten-local.

[~/brownie_test]$ brownie console --network ropsten-local
Brownie v1.17.1 - Python development framework for Ethereum

BrownieTestProject is the active project.
Brownie environment is ready.
>>> network.show_active()
'ropsten-local'
>>> network.is_connected()
True

We can interact with the smart contract we deployed earlier in the same way.

>>> simple_storage = SimpleStorage.at('0xafB83356eeeAA6E18B9a76126DE8edFD61BE5385')
>>> simple_storage.get()
5

Summary

In this article, we looked at how to deploy a smart contract to a public Ethereum testnet in Brownie. There are two ways to connect to Ethereum; one uses an API provider such as Infura, and the other uses our own Ethereum node.

First, we looked at the first option. After creating an account on Infura, we set up a project ID and used it to configure a network in Brownie. Then we looked at how to create a deployment account in Brownie and use it to deploy our smart contract. Lastly, we looked at how to interact with the smart contract using the Brownie console and checked the information on Etherscan.io.

Finally, we looked at the second option. We can download and install the Ethereum client software (Geth) on an Ubuntu machine, run our own Ethereum node on the Ropsten testnet, and optionally mine some Ether (at least for now). We can use the node instead of using Infura endpoints to connect to the Ropsten testnet.

You can find more about the deployment in Brownie in the following section of the Brownie documentation.

Deployment Basics


Learn Solidity Course

Solidity is the programming language of the future.

It gives you the rare and sought-after superpower to program against the “Internet Computer”, i.e., against decentralized Blockchains such as Ethereum, Binance Smart Chain, Ethereum Classic, Tron, and Avalanche – to mention just a few Blockchain infrastructures that support Solidity.

In particular, Solidity allows you to create smart contracts, i.e., pieces of code that automatically execute on specific conditions in a completely decentralized environment. For example, smart contracts empower you to create your own decentralized autonomous organizations (DAOs) that run on Blockchains without being subject to centralized control.

NFTs, DeFi, DAOs, and Blockchain-based games are all based on smart contracts.

This course is a simple, low-friction introduction to creating your first smart contract using the Remix IDE on the Ethereum testnet – without fluff, significant upfront costs to purchase ETH, or unnecessary complexity.