Basic operations via Web3Py

Basic operations via Web3Py
Basic operations via Web3Py with native currency, ERC20 tokens and NFTs

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:

  1. Native currency: Ethereum (ETH), Binance Coin (BNB), Polygon (MATIC), ...
  2. ERC20 token: USDT, USDC, UNI, ...
  3. 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
That's it, we've connected to Binance Smart Chain Testnet 🎉
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
From one system to another

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
Balance of address

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:

  1. Read operations
  2. 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)
Initialise USDT contract

Calling functions of contract

all_functions = usdt_contract.all_functions()
print(f"All functions of ERC20 token:\n{all_functions}")
List all functions of ERC20 token

1) Read operations

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}")
Examples of some of read operations with contract

2) Write operations

```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())
```
Transfer 1 USDT to <someone_address>

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())
Allow your custom contract spend USDT of user

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)
Init nft contract

1) Read operations

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")
Some basic read operations

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())
Minting NFT example

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!