By using this site, you agree to our use of cookies, which we use to analyse our traffic in accordance with our Privacy Policy. We also share information about your use of our site with our analytics partners.

Developers

How to Send Money Using Python: A Web3.py Tutorial

A technical walkthrough of an Ethereum blockchain library for Python developers.
by Coogan BrennanMarch 19, 2020
Python Tutorial Featured

Trigger warning: The following tutorial contains elements of explicit cryptography, peer-to-peer financial services, and other transgressive behaviors. The examples are meant only to illustrate the power and ease of blockchain for Python.

Hey all yall Pythoners out there!

I鈥檓 really into the Python community. Like so many, Python was my first programming language. The Python hackathon and meet-ups are awesome. And, as a bonus point,聽I love Monty Python!

I thought I鈥檇 join all these loves together with my current job: showing developers how powerful blockchain programming can be, and how easy the skills are to pick up.

This will be a tutorial walking Python developers through the basics of聽Web3.py, a blockchain (Ethereum) library. We鈥檒l do a lot of this from the Python interpreter. (In the next tutorial, we鈥檒l set up a proper directory but we鈥檙e keeping things easy for now!)

Note: For security reasons, we鈥檒l be sending our money over a test network. All of these same techniques can be used on the main Ethereum network.

Table of Contents

  • Installation
  • Setting up Connection
  • Initialization
  • Create an Account
  • ENS accounts
  • Send Money

Installation

We鈥檙e gonna use聽pip to install聽web3.py聽from our command line:

$ pip3 install web3
Code language: Python (python)

For people with both Python 2 and 3 installed, you should check to see which version聽pip聽command invokes. Some default to 2.7:

$ pip -V pip 10.0.1 from /Library/Python/2.7/site-packages/pip-10.0.1-py2.7.egg/pip (python 2.7) $ pip3 -V pip 20.0.2 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
Code language: JavaScript (javascript)

Also, if you鈥檙e using virtualenv,聽here鈥檚 some documentation聽about setting up a clean environment for Web3.py

Great! We鈥檙e on our way.

Setting Up Our Connection

Blockchain systems are decentralized networks made up of people running individual dedicated P2P software or 鈥渘odes鈥. It鈥檚 similar to a torrent network: To interact with the network, you either have to host a node or use a service that hosts one for you.

Since this is a basic tutorial, we鈥檒l use a service. The most popular one is聽Infura.聽You can setup your own free account (instructions here) or you can use the Product ID below. It鈥檚 crucial you get a Project ID and API endpoint 鈥 it will be our API endpoint to the blockchain and the analytics dashboard is helpful.

infura rinkeby

Copy the Endpoint and聽be sure to prepend https:// to the address.

Once you have that, you鈥檙e ready to connect to the blockchain using Python!

Initialization

Let鈥檚 fire up our Python interpreter. This can vary depending on your Python installation, but is usually accomplished with running whatever keyword you typically put before a python file. For me on my Mac iTerm with both Pyton 2 and 3 installed, it鈥檚:

$ python3
Python 3.8.2 [Clang 6.0 (clang-600.0.57)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>>
Code language: JavaScript (javascript)

To check that all our setup is correct, please run your python interpreter and the following commands:

>>> from web3 import Web3, HTTPProvider >>> import json
Code language: JavaScript (javascript)

The above command imports some of the main methods from聽web3.py聽we鈥檙e going to use to connect to the blockchain as well as the ever-faithful, native聽json聽library.

Next, we鈥檒l create an object,聽w3, which we initialize with our Infura API endpoint ( prepended with https:// ). It will become the main way聽web3.py聽works with the blockchain throughout the rest of the tutorial.

>>> w3 = Web3(Web3.HTTPProvider("https://rinkeby.infura.io/v3/8e4cd4b220fa42d3ac2acca966fd07fa"))
Code language: JavaScript (javascript)

Note: You need to add聽HTTPS://聽in front of your Infura API address, otherwise you鈥檒l get an error!

We also need to add in some middleware to help us work with Infura and the Rinkeby testnet:

>>> from web3.middleware import geth_poa_middleware >>> w3.middleware_onion.inject(geth_poa_middleware, layer=0)
Code language: JavaScript (javascript)

Now, to see if all鈥檚 gone well, we run:

>>> w3.isConnected()
Code language: CSS (css)

If you get聽True, congratulations! You鈥檙e connected to the blockchain!

If you get聽False, a few things you can check:

  1. If you鈥檝e had to restart the interpreter, you have to re-import the libraries and re-initialize the variables
  2. Have you copied the Infura API key correctly?
  3. Have you installed聽web3.py聽and installed and imported Web3 and HTTPProvider library?
  4. Have you prepended the API key with https:// ?

Create an Account

If we鈥檇 like to send money on the blockchain, we鈥檒l need an Ethereum account. Ethereum accounts are the main unit of identity on the Ethereum blockchain 鈥 an account鈥檚 address is how the user is identified on the network. Underpinning the account system is a decentralized identity protocol based on聽public key cryptography.聽Essentially, identity on a blockchain network is confirmed by authentication of digital signatures from a single private key (held in secret by a single user) by its public address counterpart (held by the entire network). While it has significant user experience hurdles, it does provide a fast, peer-to-peer authentication protocol.

Generating an account to use on the Ethereum network is super easy with聽web3.py.

Note: In the next few steps, I鈥檓 going to break a few rules of cryptography and security. 1) I鈥檓 going to generate a private key with inadequate entropy (randomness) and 2) I鈥檓 going to post a private key online. I鈥檓 not going to use this key beyond this tutorial鈥攊t鈥檚 just for educational purposes. You should always use proper private key management, like聽Geth聽or聽MetaMask,聽and never share your private key publicly.

>>> my_account = w3.eth.account.create('Nobody expects the Spanish Inquisition!') >>> my_account._address '0x5b580eB23Fca4f0936127335a92f722905286738' >>> my_account._private_key HexBytes('0x265434629c3d2e652550d62225adcb2813d3ac32c6e07c8c39b5cc1efbca18b3')
Code language: JavaScript (javascript)

The above command uses the string input to generate the聽my_account聽object, which contains a private key (my_account._private_key) and its associated Ethereum address (my_account._address) . However, since this has been posted publicly, someone could generate and use the same private key. (Luckily, I鈥檓 just using it for this tutorial and only on a test blockchain network.)

For this reason, users typically delegate private key creation and management to software called clients (like聽Geth) or wallets (like聽MetaMask). These projects provide an incredibly secure way to generate and handle private keys for blockchain interactions.

ENS Accounts

Ethereum addresses are long hexadecimal numbers. They鈥檙e nearly impossible to type or remember, so the Ethereum community created the聽Ethereum Name System (ENS).聽It serves the same benefit of the Domain Name System, which replaces website server numbers (216.58.194.46) to human-readable names (google.com). Rather than a聽.com聽domain, most ENS names use a聽.eth聽domain.

For example, I have an Ethererum account at聽0x4d3dd8471a289E820Aa9E2Dc5f437C1b2E22F598聽but I鈥檝e used ENS to map the more readable name聽coogan.eth聽to the address. If you type those into apps or projects that support ENS (such as聽web3.py!), it will substitute in the Ethereum hexadecimal address. Unfortunately, we won鈥檛 be able to use it for this tutorial as聽.eth聽domain names only work on mainnet鈥ut maybe in the next tutorial!

Send Money

For this last section, we鈥檙e going to send some money from the account we just created to another Ethereum account. All from the python interpreter!

A reasonable response to sending money is, 鈥淚sn鈥檛 this just internet funny money that鈥檚 always fluctuating with a downward trajectory?鈥 And, yes, cryptocurrency USD prices are extremely volatile. This has caused hesitation from businesses 鈥 why would you accept a currency with an uncertain price?

However, there鈥檚 a novel workaround built within the Ethereum ecosystem. Ethereum is called 鈥渢he World Computer鈥 because it鈥檚 a distributed system that allows developers to upload and execute their own code. Code uploaded to Ethereum in this way is called a聽smart contract.聽Once it鈥檚 uploaded to the network, it becomes a standalone entity, with its own address, memory storage and network access. Smart contracts have given rise to a rich Ethereum developer community that is both creative and tireless in addressing challenges, like the inherent price volatility of blockchains.

In an attempt to create a stable source of value for Ethereum, a group of developers wrote and uploaded code to Ethereum called聽Dai.聽It鈥檚 a digital token that is always worth about $1 USD. (The technical details to achieve this stability are fascinating but beyond the scope of this tutorial 鈥 if you鈥檇 like to read more about the mechanics聽you can find more here). Once we hold the Dai token, we can exchange it with other users for the same rate as US dollars. We鈥檒l do that now!

(Note: to get Dai to an account different from the one in the tutorial, you can mint your own on the testnet using聽this contract address,聽Metamask and your own account.聽Follow the steps in the tutorial here)

Contract Instantiation

First, to interface with code that鈥檚 been uploaded by a developer to Ethereum, we need to know what methods the uploaded code exposes.聽Web3.py聽natively knows how to interface with the core Ethereum software, but needs guidance to interact with third-party code. We provide that guidance by providing Web3.py an聽Application Binary Interface (ABI). Similar to an聽Application Programming Interface (API),聽the ABI allows our machine to know what functions are available to us and what parameters those functions expect. ABIs are not available on the blockchain and are supplied by the developer on sites like Github or Etherscan.

Here鈥檚 the testnet Dai ABI we鈥檒l be using, please click and copy the entire code snippet:

Note: this code is聽really聽long, take special care to copy it fully!

abi = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"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"},{"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"}]'
Code language: JavaScript (javascript)

We need to parse it using聽json:

>>> abi = json.loads(abi)

We also need to tell聽web3.py聽where to find this code on the Ethereum network. We do so with the following code:

>>> address = '0xc3dbf84Abb494ce5199D5d4D815b10EC29529ff8'
Code language: JavaScript (javascript)

We then use the ABI and the address to instantiate a聽smart contract object.聽This will give us access to the functions exposed by the code:

>>> dai = w3.eth.contract(address=address, abi=abi)

To test we鈥檝e properly instantiated the contract, we鈥檒l call a function that tells us how much Dai is held by the contract:

>>> dai.functions.totalSupply().call() 10100000000000101001376883458034812485564519
Code language: CSS (css)

(The balance may be different by the time you run this)

Building the Transaction

To transfer the Dai from our Spanish Inquisition account (my_account), we鈥檒l use the聽transfer聽function from the Dai smart contract, shown below:

transfer function

We can see we need to pass in two parameters to the contract:聽to, which will be a hexadecimal Ethereum聽address聽and聽value, which is聽uint256. Dealing with unsigned integers of 256 bits (uint256) can be challenging for even seasoned developers. It鈥檚 a testament to unusual programming that has to be done at the smart contract level and frequently trips me up.聽Web3.py聽has a method we can use to cast values from integer to the format required for this smart contract,聽toHex. Let鈥檚 send 10 Dai, and since the amount we鈥檙e sending is below 16, we鈥檒l just put a聽0x聽on the front of it. For聽address, put the address to whom you鈥檇 like to send the Dai.

So our transaction currently looks like this:

transaction = dai.functions.transfer(TO_ADDRESS, 0x10)

These parameters are good for the Dai contract (we won鈥檛 get an error there), but we need more parameters for our transaction to run on the Ethereum network. Those values are聽chainId,聽gas聽and聽nonce.

.buildTransaction({'chainId': 4, 'gas':70000, 'nonce': w3.eth.getTransactionCount(my_account._address)})
Code language: JavaScript (javascript)
  • ChainId聽helps聽web3.py聽know to which network the transaction is being sent. Different networks have different quirks (as we saw when we installed the聽middleware聽at the beginning for Rinkeby) and this helps聽web3.py聽bundle the transaction correctly. Rinkeby鈥檚 network ID is聽4,聽here鈥檚 a complete list of network IDs..
  • Gas聽is the small payment you make to miners on the network to run your transaction. Many people are surprised by this, but the amount is small (our transaction will cost 0.00007000 ETH, for example,聽but is demarcated in a particular denomination called Gwei).聽Gas聽helps run the network in a decentralized and secure way.
  • Nonce聽is a global variable particular to each Ethereum account. It serves the same purpose as the number on the bottom of the check: it allows for proper ordering of payments from different accounts. It increments up one after each transaction sent.聽Web3.py聽has a method for finding the current聽nonce聽of the address:聽w3.eth.getTransactionCount(ETHEREUM_ADDRESS).

We鈥檒l use the聽web3.py聽method聽.buildTransaction聽to incorporate these three variables into our transaction. I鈥檓 also adding in my friend鈥檚 Ethereum address and will send him聽10聽Dai:

>>> transaction = dai.functions.transfer('0xafC2F2bBD4173311BE60A7f5d4103b098D2703e8', 0x10).buildTransaction({'chainId': 4, 'gas':70000, 'nonce': w3.eth.getTransactionCount('0x5b580eB23Fca4f0936127335a92f722905286738')})
Code language: JavaScript (javascript)

Signing and Sending Transaction

Now that we have our聽transaction, we need to sign it with our private key. This is how the peer-to-peer protocol of Ethereum will know that it is this account that wants to send the money. To sign, we put the聽transaction聽object and our聽my_account._private_key聽into the following function:

>>> signed_txn = w3.eth.account.signTransaction(transaction, '0x265434629c3d2e652550d62225adcb2813d3ac32c6e07c8c39b5cc1efbca18b3')
Code language: JavaScript (javascript)

Note: You should never post your real private key online! This is being done for educational purposes only. Double Note: Until this account is drained of test ether or test dai, the above command will be valid for the Rinkeby network

With our signed transaction, all we need to do now is send it to the network through our Infura API endpoint. We do this through our聽w3聽object with the following command:

>>> txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction)

If that goes through, congratulations!聽You鈥檝e just sent money using Python!

To find your transaction, you can print聽txn_hash聽and take the string value to聽Etherscan for Rinkeby.聽Here鈥檚 the hash I have (yours will be different!):

>>> txn_hash HexBytes('0xc5f98cbe6f1eaef16916b148e6c4ae926b11ab9dde750e188362745da39d560e')
Code language: JavaScript (javascript)

You can see it on the Ethereum testnet here.

As you can see, using聽web3.py聽opens up all kinds of possibilities with your applications. In the next tutorial, I鈥檓 hoping to do something more built out with proper files and directories. For now, I just wanted to show you some of the incredible options blockchain can provide. I hope you found something interesting! The community is really eager to engage with new folks, be sure to reach out.

Thank you to Daniel Ellison for his feedback and comments, sometimes copied verbatim!

Want to check out more Ethereum developer tutorials?
Subscribe to our newsletter to get dev tools, tutorials, pro tips, and more straight to your inbox.
Subscribe