Dex Swaps With Python
Writing a Command Line Tool to Perform Dex Swaps
Oh the wonderful world of web3! When in Wonderland, it’s easy to loose track of time, as I have lately. I must warn you that if you start writing web3 compatible applications, there is a grave risk of addiction. Okay, I have covered myself. Moving on.
The reason for this post and much of what you see on this blog is that there is a tremendous amount of resources available for anyone that wants to learn how to interact with Web3 via JavaScript or NodeJs, but it is a little harder to find good tools or tutorials for Python. That is of course simply because Solidity was clearly written by people that were primarily JavaScript programmers. That said, despite the fact that Python is a little less well supported in web3 than JavaScript or Node, there are definitely adequate resources to build off.
Today, I will show you how to perform swaps on various DEX’s (decentralized exchanges) with Python. Initially I started working on this because a client of mine had an asset that they were unable to swap using the Uniswap web application. That is because in order to use Uniswap version 2, you now need to write some custom code, as the web interface now solely uses Uniswap version 3. Now, this is likely to be a drag for the uninitiated, because there are many different assets that do not have any liquidity on Uniswap version 3. Fortunately for us, we are initiated and thus able to solve this problem by writing a few lines of code. It will be software engineers that inherit the Earth, do you understand?
Before I started writing this blog, I had already written a command line tool for performing these swaps which is hosted here on Github. Then while writing this, I realized that uniswap-python actually comes with a command line interface, but it’s really basic and only provides limited functionality, like getting a quote. If I had realized that, I would have simply added onto their tool instead of writing an entire new one myself. Perhaps I will end up merging my tool with theirs.
Anyway, the first step in a situation like this is to ascertain whether or not there is an existing API or library that can be used to interface with the service in question, which in this case is the Uniswap V2 Router. A quick Google search tells us that there is a library called Uniswap Python which is exactly what we need for this task. Upon further research, this just gets better and better because we can use this library to interact with any DEX that is a fork of Uniswap! This means that our program will be compatible with other dex’s such as Sushiswap and Kyberswap — the only thing we would need to modify at that point is the contract address of the router. It is now time to code.
First, we need to install the uniswap-python library with pip. I would recommend creating a virtual enviroment because it keeps the namespace of this project isolated.
$ python3 -m virtualenv env $ source env/bin/activate $ pip install uniswap-python
The uniswap-python documentation contains this simple example for getting started. At the moment we are just testing this out, so we need to set our remote ethereum node endpoint, “xxxxxxx“ being your infura API key. If you don’t have a node or an infura account, create an account with infura.
$ export PROVIDER=https://mainnet.infura.of/xxxxxxxx
Now let’s open a python interactive shell and just get a feel for the uniswap-python library.
from uniswap import Uniswap
address = "YOUR ADDRESS" # or None if you're not going to make transactions
private_key = "YOUR PRIVATE KEY" # or None if you're not going to make transactions
version = 2 # specify which version of Uniswap to use
provider = "WEB3 PROVIDER URL" # can also be set through the environment variable `PROVIDER`
uniswap = Uniswap(address=address, private_key=private_key, version=version, provider=provider)
# Some token addresses we'll be using later in this guide
eth = "0x0000000000000000000000000000000000000000"
bat = "0x0D8775F648430679A709E98d2b0Cb6250d2887EF"
dai = "0x6B175474E89094C44Da98b954EedeAC495271d0F"
# Returns the amount of DAI you get for 1 ETH (10^18 wei)
uniswap.get_price_input(eth, dai, 10**18)
# Returns the amount of ETH you need to pay (in wei) to get 1000 DAI
uniswap.get_price_output(eth, dai, 1_000 * 10**18)
# Make a trade by specifying the quantity of the input token you wish to # sell
uniswap.make_trade(bat, eth, 1*10**18) # sell 1 BAT for ETHAs you can see, this is not complicated stuff. This example contains everything that you need in order to swap on Uniswap Version 2. That said, one thing that took me a little bit to wrap my head around was how Ethereum and token quantities work. As an example, this is what happens when I query the balance of one of my wallets.
» w3.eth.get_balance('chevis.eth')
81025742952312064
As much as I would like that to mean that I have 81 quantillion Ethereum in my wallet, it’s not the case. What you are looking at is a raw unsigned 256 bit integer (if I recall correctly). The language that smart contracts are written in, Solidity, does not know the concept of floating point numbers, so everything is denoted in integers. In order to see the actual, human-friendly balance, we need to convert it from Wei to Ether.
» w3.fromWei(81025742952312064, 'ether')
Decimal('0.081025742952312064')
By the way, a “Wei” is the smallest unit of measurement for quantifying Ethereum balances. If we divide 81025742952312064 by 10**18, we get the same result. Tokens work almost the same way, except that every token has a different number of “decimals”, which tell us what their most basic unit of measurement is. For example, USDT has six decimals, so in order to convert a raw integer Tether balance to human friendly form, we would divide it by 10**6.
If you understand that, then you understand how to get price quotes in US dollars using the Uniswap API — basically you can just use something like DAI as the base currency to get a “close” estimate of a tokens dollar value. Note that if you are writing a program in which the value of a particular token is of paramount importance, then this is probably not the best way to calculate value, because in the event that the liquidity pool that you are querying suffers a flash loan attack (or something similar), the dollar value could be way off at that moment, and thus one DAI may not be close to worth one dollar.
Another “gotcha” that is worth bringing up is please remember to verify the contract addresses of the assets that you are trying to swap into, especially when working from the command line. The shell simply does what you tell it do, and it will not warn you in the event that you get phished and pass a malicious imposter address instead of an intended address. Be careful.
At this point, you should have enough of an understanding to perform swaps on Uniswap with Python. If you also want to trade this way on another dex such as Sushiswap, you only need to specify Sushi’s router address when you initialize the uniswap.Uniswap class.
We have reached the end of the tutorial. Thanks for reading, and please consider upvoting this post if you found it useful.


