In this article, we are going to deploy smart contracts using Python. This means that, if you know some Python, this could be your transition into smart contract and blockchain development!
You can see the original blog from Medium.
Introduction
I love Python, it has such an amazing developer experience. There is something so sweet about being able to just write print("hi") and not having to do anything verbose like System.out.println("hi"). This sentiment is shared by many, including those in the fintech world. Many of the hedge funds I’ve worked with (and worked at) have used Python as their main language.
I wish I could take Python with me everywhere. And even though Solidity (Ethereum’s native smart contract language) isn’t Python, it has a lot of nice features, and can still be deployed with Python. When I first started working with Solidity, I used Remix, a strong web IDE that allows you to visualize your smart contracts. Remix is great and I still use it, but a lot of productivity can be accomplished outside of a single IDE. This was when I started to learn about Truffle and HardHat, which are Nodejs frameworks for deploying smart contracts.
These are the dominant frameworks I’ve seen by far, and this is all great but we don’t like Javascript, we like Python. So I was excited to find Brownie and web3.py; a Python framework to deploy smart contracts and an open-sourced repo for working with blockchains. We will be looking at both Brownie and Web3.py in this article.
Why Python?
There is a reason why so many data scientists, academics, and fintech institutions use Python. It’s such a versatile language, has an easy developer experience, and is jam-packed with packages to make life easier. Top defi projects are starting to realize this, with projects like yearn.finance using python to deploy all their production code. Yearn.finance is run by a group of really talented fintech engineers turned blockchain, and they took with them the tool that they know and love, Python. Also it doesn’t touch npm, but I’ll leave my distaste for javascript package managers for another article.
What is Brownie?
Brownie is an open-sourced Python smart contract framework created by Ben Hauser, aka “iamdefinitelyahuman”, and is a work of art. This is the tool that yearn.finance uses this framework to deploy and maintain contracts. You can start a project with a simple command, and start working with the code right away. What a “sweet” project name.
Deploy your First Smart Contract with Python
1. Install Brownie and bake
Brownie has a “baking” feature that allows you to start your repos with some baseline code since most projects need a lot of the same pieces, similar to create-eth-app
.To get started, we just need to install Brownie the same way you install every other Python package.
pip install eth-brownie
We also need to install ganache-cli a package for deploying local blockchains. You’ll need to install npm and nodejs for this.
npm install -g ganache-cli
And boom! We are ready to go!
We are going to use the chainlink-mix to get started, since many of these top defi projects use Chainlink to get their asset data.
brownie bake chainlink-mix
cd chainlink
And a quick ls
command will show us the layout of the project
How to deploy a smart contract with python
Brownie project layout
build : This is where the project keeps track of your deployed smart contracts and compiled contracts
contracts : The source code of your contracts, typically written in solidity or vyper
interfaces : A layout of interfaces you’ll need to work with deployed contracts. Every interaction with a contract needs an ABI and an address. Interfaces are great ways to get a contract’s ABI
scripts : Scripts we create to automate processes of working with our contracts
tests : Tests
brownie-config.yaml : This is where we have all the information for brownie to understand how to work with our smart contract. What blockchain do we want to deploy to? Are there any special parameters we want to set? All these are set in the config file.
requirements.txt , README.md , LICENSE , and .gitignore can be ignored, for now, you’ll find out what they are for as you practice.
2. Setup your environment variables
If you’re familiar with blockchain development, you know that local blockchains, testnet blockchains, and mainnet blockchains are all different things. We will be deploying to a testnet so we can interact with a real live blockchain.
You’ll need a WEB3_INFURA_PROJECT_ID
which can be retrieved by making an Infura account. This will be what we use to connect to our testnetwork.
You’ll also want to get a metamask or other web3 ethereum wallet and fund it with some ETH. For this demo, we want to use the Kovan testnetwork.
If you’ve never used metamask, feel free to follow along in this video! You can skip the part about funding with LINK, we only need testnet ETH. We also will not be using Ropsten (as shown in the video) but Kovan. If you already have a wallet, grab some Kovan Ether from the faucet.
Install, Configure, and Fund Metamask
Once you have a metamask wallet, you can export your private key to your PRIVATE_KEY environment variable.
Read here on setting environment variables. If this still confuses you and this is just a test wallet, feel free to just replace PRIVATE_KEY in the code with your private key, and WEB3_INFURA_PROJECT_ID
.
3. Deploy your smart contract
In our scripts folder, we have a script called 01_deploy_price_consumer_v3.py
, this will deploy our Smart Contract that reads in the price of Ethereum in USD. If you want to see an easier walkthrough of what this contract does and how to deploy it, feel free to check out the Chainlink tutorial on deploying a price feed contract.
brownie run is the command we can use to run a script. If you run just brownie you can see a list of all commands.
brownie run scripts/price_feed_scripts/01_deploy_price_consumer_v3.py --network kovan
--network kovan
allows us to set the network we want to work with. We are working with the kovan testnet for this demo. You’ll need Kovan ETH to do this! You’ll get a big output, but eventually will settle with something like:
Running 'scripts/price_feed_scripts/01_deploy_price_consumer_v3.py::main'...
Transaction sent: 0x23d1dfa3937e0cfbab58f8d5ecabe2bfffc28bbe2349527dabe9289e747bac56
Gas price: 20.0 gwei Gas limit: 145600 Nonce: 1339
PriceFeed.constructor confirmed - Block: 22721813 Gas used: 132364 (90.91%)
PriceFeed deployed at: 0x6B2305935DbC77662811ff817cF3Aa54fc585816
If this worked properly, we can go to kovan etherscan and find the contract we deployed. The link above shows the contract deployed in this example.
4. Read off your smart contract
Now that we have deployed a smart contract, we can read the price of ETH from the contract we just deployed. We will be using another script that we have:
brownie run scripts/price_feed_scripts/02_read_price_feed.py --network kovan
And we will get an output like:
Brownie v1.12.2 - Python development framework for Ethereum
ChainlinkProject is the active project.
Running 'scripts/price_feed_scripts/read_price_feed.py::main'...
Reading data from 0x6B2305935DbC77662811ff817cF3Aa54fc585816
62322000000
Where 62322000000 is the current price of ETH in USD! Solidity doesn’t understand decimals, and we know that this example has 8 decimals, so the price is $623.22 .
And you’ve just deployed your first smart contract using python with Brownie!
Using web3.py
Brownie uses a tool called web3.py to make your life easier, but if you’re savvy you can always work with the contracts directly without a framework. Web3.py is a raw package that we can use to work more directly with contracts.
For this, we will just need our Kovan infura project id as above.
Remember, to interact with any smart contract, you need two things:
- The contract ABI
- The contract address
Brownie takes care of a lot of these pieces behind the scenes, but we can do it manually as well. Here is what reading from that contract on-chain looks like with web3.py.
First, we need to install web3.py.
pip install web3
Then we can run the following in a file.
web3 = Web3(Web3.HTTPProvider('https://kovan.infura.io/v3/<infura_project_id>'))
abi = '[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]'
addr = '0x9326BFA02ADD2366b30bacB125260Af641031331'
contract = web3.eth.contract(address=addr, abi=abi)
latestData = contract.functions.latestRoundData().call() print(latestData)
Running the above will print the latest price of ETH in USD to our console. If you have an issue, be sure to check the Chainlink documentation to see if something is off.
Conclusion
You can learn more about Web3.py and Brownie from their documentation. Both of these projects are open-sourced so anyone can contribute!
Feel free to check out this video which explains it some more!
Top comments (1)
I can't seem to get the web3 code to work. It fails on 'latestData = contract.functions.latestRoundData().call()'. If I call your contract address with my Infura Project Id with works. Could there be an issue with my deployed contract? The 'read_price_feed.py' script works ok from brownie though.