By the end of this lesson, you will be able to:
This module is targeted at helping Web2 developers transition into Web3 along with their established skill set to build Web3 applications with complete ease. In this lesson, we provide developers with a detailed guide on how to deploy and interact with smart contracts on the BNB Smart Chain using one of the most popular Web2 programming languages, Python by leveraging the Web3py framework.
Web3.py is a set of libraries that enable Python developers to interact with Ethereum nodes using HTTP, IPC, or WebSocket protocols. BSC has an Ethereum-like API available which is completely compatible with Ethereum-style JSON RPC invocations. Therefore, developers can leverage this compatibility and use the Web3.py library to interact with a BSC Network and leverage their python programming skills to develop blockchain-based applications.
For this lesson, following are the software pre-requisites. As this is a hands-on guide, we encourage our readers to perform each step for better understanding.
Before jumping into the development part, let's first make sure we have the necessary environment setup. We will be installing web3 within a virtual environment. Make sure you are running the command prompt as administrator.
$ pip install --upgrade virtualenv
$ virtualenv -p python3 ~/.venv-py3
$ virtualenv -p python3 venv-py3 (On Windows)
$ source ~/.venv-py3/bin/activate
$ venv-py3\Scripts\activate (On Windows)
$ pip install --upgrade pip setuptools
$ pip install --upgrade web3
$ pip install py-solc-x
$ mkdir bsc-python-tutorial
$ cd bsc-python-tutorial
$ pip3 install web3 py-solc-x
Make a new file with .py
extension, say python-tutorial.py
start adding the following code sequence-wise into it.
Web3.py can be configured to work with BSC Testnet/Mainnet. To configure your project for use with BSC, all you need is one the officially provided RPC endpoints for the network.
from web3 import Web3
web3 = Web3(Web3.HTTPProvider('RPC-ENDPOINT-HERE')) # Insert your RPC URL here
Since BSC is not the default provider and uses a different mining mechanism than Ethereum, middleware is used.
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
There are certain new features that are still to be confirmed, however, you can easily enable them for use in your program
w3.eth.account.enable_unaudited_hdwallet_features()
We can use our Metamask account with web3py by using its private key to generate a compatible account. The private key is required to sign the transaction. We will also be adding our newly created account into the middleware onion, also construct_sign_and_send_raw_middleware(account)
will make sure that middleware automatically captures transactions, signs them, and sends them as raw transactions. Furthermore, the gas_price_strategy
is also set to confirm the time it takes to mine a transaction.
Note: This is for example purposes only. Never store your private keys in a Python file.
pvkey = "Your_Metamask_Account_Private_Key"
account = w3.eth.account.from_key(pvkey)
w3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))
w3.eth.default_account = account.address
w3.eth.set_gas_price_strategy(fast_gas_price_strategy)
Now that we have our necessary account details setup, the next step is to compile our smart contract. In this example, we directly provide smart contract within the python program, you can also read it from a .sol
file.
We create a simple Greeter
program, that has one string variable greeting to store name to greet; a setName
function to set value of greeting; and a greet
function to return the greeting message.
The py-solc-x
is the solidity compielr version that is design to work with python. Remember that the smart contract below is written in the Solidity programming language.
compiled_sol = compile_source(
'''
pragma solidity >0.8.0;
contract Greeter {
string public greeting;
constructor() public {
greeting = 'Hello, World';
}
function setGreeting(string memory _greeting) public {
greeting = _greeting;
}
function greet() view public returns (string memory) {
return greeting;
}
}
''',
output_values=['abi', 'bin']
)
To retrieve the contract interface so that we can interact with it, we can use the popitem()
function that will return the contract interface.
contract_id, contract_interface = compiled_sol.popitem()
Smart contracts are compiled and stored in the form of bytecodes on the blockchain. In the above command, when we compiled our contract the bin
variable was used to store the bytecode. Therefore, to retrieve its bytecode, the statement contract_interface['bin']
will return the bytecode.
bytecode = contract_interface['bin']
A smart contract’s Application Binary Interface (ABI) is the standard way to interact with it in the blockchain ecosystem, both from outside the blockchain and for contract-to-contract interaction. Therefore, we need to retrieve the ABI of our contract. The ABI is stored in the abi
variable as an output of compilation.
abi = contract_interface['abi']
Create a contract instance using the web3.eth.contract
function and passing in the ABI and bytecode of the contract. This instance will then be used for accessing functions of the smart contract.
Greeter = w3.eth.contract(abi=abi, bytecode=bytecode)
buildTransaction
function to pass in the transaction information including the from address and the nonce for the sender. To get the nonce you can use the web3.eth.get_transaction_count
function. The from address is the account you wish to use for making transactions.web3.eth.account.sign_transaction
function and pass in the constructor transaction and the private_key
of the sender.web3.eth.send_raw_transaction
function and wait for the transaction receipt by using the web3.eth.wait_for_transaction_receipt
function.nonce = w3.eth.getTransactionCount(account.address)
deploy_tx = Greeter.constructor().buildTransaction({'gasPrice': w3.eth.gas_price, 'gas': 10000000,'from': account.address, "chainId": 97,'nonce': w3.eth.getTransactionCount(account.address) })
signed_tx = w3.eth.account.sign_transaction(deploy_tx, private_key=account.key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print("Contract Deployed at: ", tx_receipt.contractAddress)
The ouput of running the script at this point, should produce a similar output. Remember that, each time a smart contract is deployed onto a blockchain network, it is assigned a new address. Therefore, dont panic if your's is not the same as the one in the following output.
There will be essentially two types of methods, call and send methods.
Call methods are the type of interaction that don't modify the contract's storage (change variables), meaning no transaction needs to be sent. They simply read various storage variables of the deployed contract.
Create a contract instance using the web3.eth.contract
function and passing in the ABI and address of the deployed contract. Using the contract instance, you can then call the greeting
function
contract_instance = w3.eth.contract(
address=tx_receipt.contractAddress,
abi=abi
)
# Call contract function to print greet message
print(contract_instance.functions.greet().call())
web3.eth.contract
function and passing in the ABI and address of the deployed contractupdate_name_tx
transaction using the contract instance and passing in the value to increment by. You'll then use the buildTransaction
function to pass in the transaction information including the from address and the nonce for the sender. To get the nonce you can use the web3.eth.get_transaction_count
functionweb3.eth.account.sign_transaction
function and pass in the increment transaction and the private_key
of the senderweb3.eth.send_raw_transaction
function and wait for the transaction receipt by using the web3.eth.wait_for_transaction_receipt
function.transaction_index
is used to make sure nonce is increased.transaction_index = 1
update_name_tx =
contract_instance.functions.setGreeting("Hello, BNBChain").buildTransaction({ 'gasPrice': w3.eth.gas_price,
'gas': 200000,'from': account.address,
"chainId": 97, 'nonce': nonce + transaction_index})
transaction_index +=1
signed_tx = w3.eth.account.sign_transaction(update_name_tx,private_key=account.key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
# Call contract function to print greet message
print(contract_instance.functions.greet().call())
Upon running the complete script the output produced should look like as follows. Remember that, each time a smart contract is deployed onto a blockchain network, it is assigned a new address. Therefore, don't panic if yours is not the same as the one in the following output.
In this module, we provide a detailed guide on how web2 developers can use their development skills and transition to building web3 blockchain apps without much hustle. In this lesson, we provided our readers with a step-by-step guide on how Python developers can use the Web3py library to interface their python programs with the BNB Smart Chain network, empowering them to compile, deploy and interact with smart contracts on the BNB Smart Chain network.