Basic operations via Web3Py

While starting with web3, I haven't managed to find simple examples for doing basic stuff in chains via web3py
like: sending transactions, interacting with contract (read and write), mint NFTs and so on. That's why I decided to write this post showing some all of these examples in one place.
We will cover 3 topics:
- Native currency: Ethereum (ETH), Binance Coin (BNB), Polygon (MATIC), ...
- ERC20 token: USDT, USDC, UNI, ...
- NFT
But before we start, one needs to say how to connect to blockchains.
Connecting to blockchain
To connect to blockchain means to connect to a node of blockchain. Some networks have public RPC URL
to connect to node and some not (more here). Let's consider Binance Smart Chain (BSC), it's from the first group. In our example we will use testnet.
from web3 import Web3
binance_testnet_rpc_url = "https://data-seed-prebsc-1-s1.binance.org:8545/"
w3 = Web3(Web3.HTTPProvider(binance_testnet_rpc_url))
print(f"Is connected: {w3.isConnected()}")
print(f"gas price: {w3.eth.gas_price} BNB") # in Wei
If you execute last line to get gas price
, you'll see that the number has a lot of digits. It's because in crypto world there is no floats, there is only integer numbers as if all prices in real world were in cents, play here.
w3.fromWei(10 ** 18, 'ether') # Decimal('1')
w3.toWei(1, 'ether') # 1000000000000000000
We are connected to blockchain, let's go to 👇
Native currency
In Binance Smart Chain native currency is BNB.  How to know balance of any address? It's very simple 🔽
Balance info
some_address = '0x2A647559a6c5dcB76ce1751101449ebbC039b157'
w3.eth.get_balance(some_address) # returns number in Wei
Sending native currency (BNB) to someone 💸
Actually, sending transaction consists of 3 steps: 1) building, 2) signing, 3) sending. Ensure you have enough money!
my_address = '0x2A647559a6c5dcB76ce1751101449ebbC039b157'
someone_address = '0x3CCb0F1f6Cc0709f491A2D25b4FBCa5Fe78870e8'
# get it from metamask
private_key = '0395e33f161d29df8221a0...' # KEEP IN SECRET
# build txn
transaction = {
'chainId': 97, # more https://chainlist.org/
'to': someone_address,
'from': my_address,
'value': Web3.toWei(0.1, 'ether'), # 0.1 BNB
'nonce': w3.eth.getTransactionCount(my_address), # just number of confirmed txns
'gasPrice': w3.eth.gas_price,
'gas': 2000000,
}
# sign
signed_txn = w3.eth.account.sign_transaction(transaction, private_key)
# send
txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
print(txn_hash.hex())
After you obtained txn_hash
, you can check details of your transaction on bscscan. It will be completed in a couple of seconds.
ERC20 tokens
ERC20 token is just a standard used for creating and issuing smart contracts on blockchain. Each contract has its own ABI. You can treat ABI like entity which describes interface of contract, i.e. functions of contract with corresponding parameters. All functions can be divided into 2 categories:
- Read operations
- Write operations, when you send some info to blockchain
Let's have a look at USDT token contract in BSC Testnet. You can get few USDT in BSC testnet here.
Tether (USDT) is a blockchain-based cryptocurrency whose tokens in circulation are backed by an equivalent amount of U.S. dollars, making it a stablecoin with a price pegged to USD $1.00.
Initialising contract instance
import json
# this ABI is single for all ERC20 tokens, you can reuse it everywhere
ERC20_ABI = json.loads('''[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"setupDecimals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]''')
usdt_contract_address = '0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c'
usdt_contract = w3.eth.contract(usdt_contract_address, abi=ERC20_ABI)
Calling functions of contract
all_functions = usdt_contract.all_functions()
print(f"All functions of ERC20 token:\n{all_functions}")
my_address = '0x2A647559a6c5dcB76ce1751101449ebbC039b157'
token_name = usdt_contract.functions.name().call()
balance_of_token = usdt_contract.functions.balanceOf(my_address).call() # in Wei
token_symbol = usdt_contract.functions.symbol().call()
print(f"Balance of {token_name} ({token_symbol}) on {my_address} is {balance_of_token}")
```python
my_address = '0x2A647559a6c5dcB76ce1751101449ebbC039b157'
someone_address = '0x3CCb0F1f6Cc0709f491A2D25b4FBCa5Fe78870e8'
# get it from metamask
private_key = '0395e33f161d29df8221a0...' # KEEP IN SECRET
# build txn
dict_transaction = {
'chainId': 97, # more https://chainlist.org/
'from': my_address,
'gasPrice': w3.eth.gas_price,
'nonce': w3.eth.getTransactionCount(my_address), # just number of confirmed txns
}
usdt_decimals = usdt_contract.functions.decimals().call()
one_usdt = 1 * 10 ** usdt_decimals
transaction = usdt_contract.functions.transfer(
someone_address, one_usdt
).buildTransaction(dict_transaction)
# sign
signed_txn = w3.eth.account.sign_transaction(transaction, private_key)
# send
txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
print(txn_hash.hex())
```
It might be useful in some cases allow your own contract spend ERC20 tokens of your user. For example, when you're doing subscription manager and would like to bill your user every month. For that case you have to request approve from user to spend his tokens via your contract 👇
user_address = '0x2A647559a6c5dcB76ce1751101449ebbC039b157'
user_private_key = '0395e33f161d29df8221a0...'
your_custom_contract_address = '0x...'
dict_transaction = {
'chainId': w3.eth.chain_id,
'gas': 210000,
'gasPrice': w3.eth.gas_price,
'nonce': w3.eth.getTransactionCount(user_address),
}
# max approve amount is 2 ** 256 - 1
approve_amount = 10 * 10 ** 6 # 10 USDT
transaction = usdt_contract.functions.approve(
your_custom_contract_address, approve_amount
).buildTransaction(dict_transaction)
signed_txn = w3.eth.account.signTransaction(transaction, user_private_key)
txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
print(txn_hash.hex())
NFT
ERC20 and NFT are almost equal in terms of Web3Py. The main difference is different ABI. There is no one standard for NFT's ABI. For instance, let's look at Battle Shroom NFT in Polygon Testnet. One can find ABI of the NFT here at the bottom of the page.
Initialising
import json
from web3 import Web3
polygon_testnet_rpc_url = "https://rpc-mumbai.matic.today"
w3 = Web3(Web3.HTTPProvider(polygon_testnet_rpc_url))
print(f"Is connected: {w3.isConnected()}")
NFT_ABI = json.loads("""[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_botHolders","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_presalePaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_whiteListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newHolder","type":"address"}],"name":"addBotHolder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_newBotHolders","type":"address[]"}],"name":"addBotHolderMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wl","type":"address"}],"name":"addWL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_wls","type":"address[]"}],"name":"addWLMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDiscount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"giveAway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"giveAwayMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"migrateMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"migrateOne","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintPresaleShroom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"mintShroom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"oldWalletOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"val","type":"bool"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"val","type":"bool"}],"name":"presalePause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wl","type":"address"}],"name":"removeWL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_wls","type":"address[]"}],"name":"removeWLMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newDiscount","type":"uint256"}],"name":"setDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newReserved","type":"uint256"}],"name":"setGiftReserved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxPerTx","type":"uint256"}],"name":"setMaxPerTx","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMax","type":"uint256"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newReserved","type":"uint256"}],"name":"setPresaleReserved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPrice","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"walletOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"payable","type":"function"}]""")
nft_address = '0x1640B6BF576Ece2e7467C009bd1705408766D976'
nft_contract = w3.eth.contract(nft_address, abi=NFT_ABI)
wei_price = nft_contract.functions.getPrice().call()
matic_price = float(Web3.fromWei(wei_price, 'ether'))
nft_name = nft_contract.functions.name().call()
print(f"Price of {nft_name} is {matic_price} MATIC")
2) Write operations. The most desired one with NFT is minting 👇
my_address = '0x2A647559a6c5dcB76ce1751101449ebbC039b157'
wei_nft_price = nft_contract.functions.getPrice().call()
dict_transaction = {
'chainId': 80001, # more https://chainlist.org/
'from': my_address,
'value': wei_nft_price,
'gasPrice': w3.eth.gas_price,
'nonce': w3.eth.getTransactionCount(my_address), # just number of confirmed txns
}
number_of_nfts_to_mint = 1
transaction = nft_contract.functions.mintShroom(
number_of_nfts_to_mint
).buildTransaction(dict_transaction)
# sign
signed_txn = w3.eth.account.sign_transaction(transaction, private_key)
# send
txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
print(txn_hash.hex())
Congratulations 🎉
Now you're a Web3Py
master. I'd say that we've covered about 90% of use cases of the package. And one of the most exciting point is that all of these functions and approaches are suitable for Web3.js
except for its syntax. All roads of Web3 are open for you!