Introduction
Uniswap is a decentralized cryptocurrency exchange that allows for the permissionless trading of Ethereum based ERC-20 tokens. Through their grants program, the protocol’s governance has funded initiatives that support the creation of community infrastructure that lower the barriers of entry for those seeking to interact with the platform from within alternative development environments. Consistent with that mandate, through our grant we created an open source software library for the R language that allows developers, traders, researchers and enthusiasts the ability to query, analyze, export and model data generated from the Uniswap platform. Our uniswappeR R package includes the functionality to trade and query prices from the Uniswap platform. To interact with the Uniswap through this language we’ll first need to configure the environment and then use package defined functions to perform swaps and collect data. This blog post provides an overview of the motivation behind the creation of the package, who we expect our primary users to be, the steps needed to configure the environment, and a survey of the functionality contained within the package.
Motivation
The growth of the Uniswap platform has come with both a need for tools that help enable interaction with the platform, as well as higher-level analytics about the growth of the platform itself. We’ve built uniswappeR
to target both of these key areas.
uniswappeR
maintains a broad set of trading and utility functions. Because these have been abstracted into simple R-based functions, the possibilities are significant in terms of automation. For example, perhaps a trader on the Uniswap platform wishes to execute a particular trade given a sit of criteria, or at a specific time. The automation afforded to us by the R language and this package means that workflows such as this are now easy and seamless. Perhaps another trader wishes to perform an analysis of their current positions in order to help decide on a trade. The suite of visualization and data analysis tools in R makes the process of doing so significantly more seamless. These could even be abstracted into an API – imagine a simple web-based REST API which allows the inputting of a list of tokens and quickly retrieves statistics on pool liquidity with respect to these tokens.At a platform level,
uniswappeR
allows for a visualization and statistical assessment of the growth and trends in growth of users and usage. This has implications both from the perspective of a Uniswap user and for the Uniswap community / development team as a whole. For the user, the easy to access metrics about platform usage help lend confidence in the platform itself, and to help indicate potential trends in when trades may be most lucrative. For the broader Uniswap community, our package provides a simple way of accessing growth metrics that can help with the governance and direction of the platform, and can be chained to down-stream analysis in order to make more informed and data-driven decisions.
Setup
Full use of uniswappeR
requires a Python environment available. Fortunately, thanks to the reticulate
package, we can easily interact with Python code within an R session. To setup your session, perform the following steps:
- Install the reticulate package using
library(reticulate)
- Install python to use as backend using
install_python("3.8.7")
- Create a Virtual Environment to keep the backend sandboxed using:
virtualenv_create("uniswappeR-env", version = "3.8.7")
- Install uniswap-python package using
virtualenv_install(envname="uniswappeR-env",packages=c("uniswap-python==0.4.6"))
- Use the Virtual Environment using
use_virtualenv("uniswappeR-env",required=TRUE)
- Extract checksum function from Web3 module using
Web3_checksum <- import("web3",convert=FALSE)$Web3$toChecksumAddress
All together, the code looks as follow:
library(reticulate)
install_python("3.8.7")
virtualenv_create("uniswappeR-env", version = "3.8.7")
virtualenv_install(envname="uniswappeR-env",packages=c("uniswap-python==0.4.6"))
use_virtualenv("uniswappeR-env", required = TRUE)
Web3_checksum <- import("web3",convert=FALSE)$Web3$toChecksumAddress
Now, we need to simply activate the virtual environment:
library(reticulate)
library(uniswappeR)
Web3_checksum <- import("web3",convert=FALSE)$Web3$toChecksumAddress
To configure your Infura node, you use the following:
## [1] TRUE
set_infura_node("https://mainnet.infura.io/v3/XXXXXXXXXXXXXXXXXXX")
At this stage, we need to set our address and private key:
u_w <- uniswap_session(user_add="<your_address>",pvt_key="<your_private_key>")
Now, we are ready to begin!
Helper Functions
The package includes a variety of helper functions which perform queries against the uniswap API. For instance, with the following we can check our balance in ETH:
check_eth_balance(u_w)
## [1] 18.32611
We can use the Uniswap Token Address, along with the number of decimals, to check our token balance:
t_a <- Web3_checksum("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984")
t_d <- 18
check_tok_balance(t_a,t_d,u_w)
## [1] 17.81118
Suppose we wish to determine how much ETH we would need in order to get 2 UNI tokens. We can do that simple check easily:
t_q <- 2
check_eth.to.tok_tok.fix(t_a,t_d,t_q,u_w)
## [1] 2.451879
A class of similar functions are available:
check_eth.to.tok_tok.fix
: How much ETH you would get for 2 UNI Tokens, When you Swap UNI for ETHcheck_tok.to.eth_tok.fix
: How much UNI Token you need to get .5 ETH Tokens, When you Swap UNI for ETHcheck_tok.to.eth_eth.fix
: How much DAI Token you would get for 2 UNI, When you Swap UNI for DAI (Uses UNI->ETH->DAI Route)check_tok1.to.tok2_tok1.fix
: How much UNI Token you would need to get 50 DAI, When you Swap UNI for DAI (Uses UNI->ETH->DAI Route)
Making Trades
One of the fundamental components of the package is the ability to make trades. The basic trading functionality is encapsulated in a set of trade_*
functions:
trade_eth.to.tok_eth.fix
: Swap ETH for UNI Tokenstrade_eth.to.tok_tok.fix
: Swap as much ETH required to get N UNI Tokenstrade_tok.to.eth_tok.fix
: Swap UNI Tokens for ETHtrade_tok.to.eth_eth.fix
: Swap as much UNI Tokens required to get N ETHtrade_tok1.to.tok2_tok1.fix
: Swap UNI Tokens for other Tokens
For example, suppose I wanted to swap .5 ETH for UNI tokens. I would simply execute:
e_q <- .5
trade_eth.to.tok_eth.fix(t_a,t_d,e_q,u_w)
Pulling Historical Data
In addition to lower-level functions for checking balances and performing trades, uniswappeR
has rich functionality to pull historical data to perform a down-stream analysis or produce visualizations. We provide the factory_stats_v2()
function which gives statistics across all pairs generated by the Uniswap Factory. There is also a corresponding uniswap_stats_hist_v2()
function which has the historical data.
factory_stats_v2()
id | pairCount | totalLiquidityETH | totalLiquidityUSD | totalVolumeETH | totalVolumeUSD | txCount |
---|---|---|---|---|---|---|
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f | 52135 | 1421653 | 4310303951 | 279881002 | 3.47819e+11 | 61602306 |
uniswap_stats_hist_v2()
dailyVolumeETH | dailyVolumeUSD | dailyVolumeUntracked | date | totalLiquidityETH | totalLiquidityUSD | totalVolumeETH | totalVolumeUSD | txCount |
---|---|---|---|---|---|---|---|---|
179300.5 | 558029103 | 2721549429 | 1632096000 | 1421653 | 4310303951 | 0 | 0 | 61602306 |
121586.4 | 410670904 | 1823649220 | 1632009600 | 1403587 | 4654001671 | 0 | 0 | 61532178 |
103734.4 | 358961182 | 949614434 | 1631923200 | 1390544 | 4766547009 | 0 | 0 | 61460138 |
122927.6 | 426859530 | 1016250380 | 1631836800 | 1382503 | 4694619054 | 0 | 0 | 61385296 |
130864.5 | 470205526 | 1779515409 | 1631750400 | 1369737 | 4887351102 | 0 | 0 | 61319618 |
134899.2 | 467252991 | 69839801636 | 1631664000 | 1371344 | 4939990712 | 0 | 0 | 61246300 |
We can look at the stats for a specific token by calling token_stats_v2
or token_stats_hist_v2
and passing in the corresponding address. For example:
token_stats_v2(token_address = "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984")
Variable | Value |
---|---|
decimals | 18 |
derivedETH | 0.006996118649801181784999804392223752 |
id | 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 |
name | Uniswap |
symbol | UNI |
totalLiquidity | 2144853.7738 |
tradeVolume | 781380949.3267 |
tradeVolumeUSD | 7099187441.8701 |
txCount | 773068 |
untrackedVolumeUSD | 7104532201.7246 |
Similarly, we can get statistics for pairs following the same convention as previous:
pair_stats_v2(pair_address = "0xf00e80f0de9aea0b33aa229a4014572777e422ee")
Variable | Value |
---|---|
createdAtBlockNumber | 10881896 |
createdAtTimestamp | 1600374417 |
id | 0xf00e80f0de9aea0b33aa229a4014572777e422ee |
liquidityProviderCount | 351 |
reserve0 | 940.901506190946009069 |
reserve1 | 19916.661133515222782723 |
reserveETH | 13.05078217841610887592210387849377 |
reserveUSD | 40053.79913499987141134046709123516 |
token0Price | 0.04724192975335721350901947925432114 |
token1Price | 21.16763657244411622590365979032975 |
totalSupply | 3882.86847708394650094 |
trackedReserveETH | 13.05078217841610887592210387849378 |
txCount | 15617 |
untrackedVolumeUSD | 2917996.449041687701456679972555839 |
volumeToken0 | 350124.063511150688205857 |
volumeToken1 | 2922904.154447062735234883 |
volumeUSD | 2917102.314312241474338116627477814 |
tok0 | UNI |
tok1 | DAI |
Likewise for the liquidity positions:
pair_liq_positions_v2(pair_address = "0xf00e80f0de9aea0b33aa229a4014572777e422ee")
id | liquidityTokenBalance | pair | user |
---|---|---|---|
0xf00e80f0de9aea0b33aa229a4014572777e422ee-0x000e0079afa2a5aad5ff05b145abf4777b1707ac | 380.308033046897392563 | 0xf00e80f0de9aea0b33aa229a4014572777e422ee | 0x000e0079afa2a5aad5ff05b145abf4777b1707ac |
0xf00e80f0de9aea0b33aa229a4014572777e422ee-0x01ebedf403c19bf4f1f6317ef64bf80a2a704701 | 2.477708698357644268 | 0xf00e80f0de9aea0b33aa229a4014572777e422ee | 0x01ebedf403c19bf4f1f6317ef64bf80a2a704701 |
0xf00e80f0de9aea0b33aa229a4014572777e422ee-0x0220b3a3c7f41566a89c28d8fa763276038ebeb4 | 0 | 0xf00e80f0de9aea0b33aa229a4014572777e422ee | 0x0220b3a3c7f41566a89c28d8fa763276038ebeb4 |
0xf00e80f0de9aea0b33aa229a4014572777e422ee-0x03b8941448adfd421b7617455c314aa1abdf5b18 | 0 | 0xf00e80f0de9aea0b33aa229a4014572777e422ee | 0x03b8941448adfd421b7617455c314aa1abdf5b18 |
0xf00e80f0de9aea0b33aa229a4014572777e422ee-0x04c689d31d3e3e64f871330d412e35fe66553507 | 0 | 0xf00e80f0de9aea0b33aa229a4014572777e422ee | 0x04c689d31d3e3e64f871330d412e35fe66553507 |
0xf00e80f0de9aea0b33aa229a4014572777e422ee-0x0552c166c914837d138741893d9820e3de2b9995 | 0 | 0xf00e80f0de9aea0b33aa229a4014572777e422ee | 0x0552c166c914837d138741893d9820e3de2b9995 |
All of these datasets can be exported into a standard CSV file with a general data_export function, like this:
export_data(uniswap_stats_hist_v2(), "my_file.csv")
Data Exploration
Last but not least, we turn to an exploration of growth metrics on the platform, using data visualization.
There are a suite of functions designed to illustrate the growth of the platform as a whole, at a high level. These functions produce visualizations. Here is a quick survey. The first provides a view of the growth in terms of volume, liquidity, and number of transactions of the platform. This can be shown both in terms of the Uniswap platform, and with respect to particular tokens by passing in an address. At the platform level, we can see some very obvious patterns. The total liquidity in ETH peaked during the late 2020 time frame, while the total liquidity when measured in USD peaked along with the overall crypto market in spring 2021. Transactions on the platform have steadily rose as a function of time, though over the past couple of months have tended to level off some compared to the more rapid growth prior. As illustrated, these metrics can be obtained at the per-token level, in this case UNI, which allows for an even more fine-grained assessment of how use of the platform is evolving over time.
There is also a set of visualization functions that are designed to operate on pairs, for visualization of the number of pairs, the growth in terms of the aforementioned metrics of volume with respect to the pairs, and in terms of the liquidity token distribution. Here we look at these stats for the UNI/DAI pair. The first plot illustrates UNI as both the first and the second token in the pair has seen a large increase, particularly in the late 2020 time frame. The latter two plots tell a consistent story: While the overall number of daily transactions with this pair has declined, there are still several daily spikes that indicate the pair is seeing significant volume on the platform. This is just one example of the type of visualizations that uniswappeR
can produce, which illustrates how this can be used to derive insights and metrics across the platform, including determining which pairs have seen the most recent growth, and so much more.
Finally, there is a function swap_visualizations
which creates a pre-set panel of visualizations designed to illustrate the amount of swaps over time, and by token. Here we pass in a set of addresses and the corresponding swap data is pulled. This allows a per-user view of the performance, so we can quickly at-a-glance see how the usage of the platform has changed at the per-user, per-pair level. You can imagine this could be used to produce per-user dashboards with respect to trades / swaps done on the Uniswap platform, so users can quickly use analytics to assess their own trading behavior.
Video Tutorials
We have encapsulated much of the content of this walkthrough into two video tutorials. The first covers the setup of the package itself, and the second covers the ability to make trades with the package. Those videos can be seen below:
Conclusion
As you can see, the uniswappeR
package provides a large host of functionality, ranging from smaller helper utilities, to functions for initializing trades, to aggregation and visualization functions. This package operates on both a micro / account level, but also provides a statistical overview that is useful from the perspective of the platform itself. While still early in its infancy, we do hope that this package serves as a useful tool for those familiar with the R language and opens up the Uniswap platform to more use.