class: center, middle, inverse, title-slide # Tornado.Cash Airdrop Analysis
### Omni Analytics Group --- ## Tornado Cash Tornado.cash is a fully autonomous, decentralized transaction mixer that provides private value transfers on the Ethereum blockchain. On December 17th, a Decentralized Autonomous Organization was formed around the Tornado.Cash protocol, and its native token `$vTORN` was distributed to early adopters and users of the service. As it stands, the `$vTRON` tokens have a 45 day lock up where transfers have been disabled until governance decides on the best way forward. Having said that, it is quite common for tokens to be "airdropped" to users, which have some fair market trade value. --- ## Motivation This case study will be speculative in nature and will involve the procurement of the data, the transformation of variables using a few common themes we've learned from other cryptocurrency analysis. The final step will be to calculate set of estimates of the average expected dollar value of the tokens a recipient will gain. We will be using the following libraries: ```r library(knitr) library(tidyverse) library(kableExtra) library(formattable) library(scales) ``` <p align="center"> <img src="Cut_outs/Cut_out_17.png" width="200px" height="150px"> </p> --- ## The Data For data, we'll use the open sourced table of wallet addresses and the raw amounts supplied by the team within their announcement: https://github.com/tornadocash/airdrop/blob/master/airdrop.csv. We named the columns `Wallet Address` and `Raw`. Below are the first few columns of the data: ```r torn <- read_csv("airdrop.csv") torn %>% head() %>% kable() %>% kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) ``` <table class="table table-striped table-hover" style="width: auto !important; margin-left: auto; margin-right: auto;"> <thead> <tr> <th style="text-align:left;"> Wallet Address </th> <th style="text-align:right;"> Raw </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> 0x0039F22efB07A647557C7C5d17854CFD6D489eF3 </td> <td style="text-align:right;"> 6.167693e+20 </td> </tr> <tr> <td style="text-align:left;"> 0xbB1332e692E701bFC0e3C19FfD4Dd619C599ea2a </td> <td style="text-align:right;"> 1.014204e+20 </td> </tr> <tr> <td style="text-align:left;"> 0x9305D3b084FA269Afe5A1a8Ca414715D39041eb9 </td> <td style="text-align:right;"> 4.786841e+20 </td> </tr> <tr> <td style="text-align:left;"> 0x7f7DC314fC75658D474DBBfe598EbB058CA6aAcd </td> <td style="text-align:right;"> 3.285185e+20 </td> </tr> <tr> <td style="text-align:left;"> 0x3ac2483105a248b77BDe927cA41A5bbDA968603D </td> <td style="text-align:right;"> 1.215414e+20 </td> </tr> <tr> <td style="text-align:left;"> 0xA61A8CeC64C404B0050EB3fA3Dd2B4A9C9aE53e7 </td> <td style="text-align:right;"> 2.009907e+20 </td> </tr> </tbody> </table> --- ## Number Of Tokens First, we can convert the `Raw` column to the number of TORN TOkens by simply dividing it by `\(10^{18}\)`. ```r torn <- torn %>% mutate(TORN = Raw/10^(18)) torn %>% summarise( sum = sum(TORN)) ``` ``` ## # A tibble: 1 x 1 ## sum ## <dbl> ## 1 500000 ``` We have a total of `\(500,000\)` TORN Tokens. <p align="right"> <img src="Cut_outs/Cut_out_14.png" width="150" height="150px"> </p> --- ## Summary Statistics Now let us look a the summary statistics of these TORN Tokens. ```r summary(torn$TORN) ``` ``` ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 0.0062 9.5943 21.2435 66.5425 58.3479 2539.9524 ``` From the summary, we can see that the mean is slightly higher than the third quartile and the maximum is very large compared to the others. --- ## Distribution of TORN Tokens Let's take a look at the distribution of the TORN Tokens using `ggplot2` and `scales` packages. ```r torn %>% ggplot(aes(x=TORN))+ geom_histogram(color="darkblue", fill="lightblue", boundary=0)+ scale_y_continuous(breaks = scales::pretty_breaks(n=10))+ scale_x_continuous(breaks = scales::pretty_breaks(n=20))+ labs(title = "Distribution of TORN Tokens", x = "TORN Tokens") ``` <p align="left"> <img src="Cut_outs/Cut_out_08.png" width="200px" height="200px"> </p> --- <img src="tornado_files/figure-html/unnamed-chunk-6-1.png" style="display: block; margin: auto;" /> --- ## Log transformation Since there are many large values (outliers), we can log transform the x-axis to have a better sense of the distribution via `scale_x_log10()`. Also, note that we created `scaleFUN` to make sure it displays with 2 decimals. ```r scaleFUN <- function(x) sprintf("%.2f", x) x_breaks = c(0.01,0.03,0.1,0.3, 1, 3, 10,30,100,300,1000) p1 <- torn %>% ggplot(aes(x=TORN))+ geom_histogram(color="darkblue", fill="lightblue", boundary=0)+ scale_y_continuous(breaks = scales::pretty_breaks(n=10))+ scale_x_log10(breaks = x_breaks, labels = scaleFUN)+ labs(title = "Distribution of TORN Tokens", x = "TORN Tokens")+ theme(axis.text.x = element_text(angle = 45, vjust=0.9)) ``` --- <img src="tornado_files/figure-html/unnamed-chunk-8-1.png" style="display: block; margin: auto;" /> --- ## Market Capitalization Estimates <img src="stickers/calc1.png" width="100px" height="100px"> The market capitalization of a token is simply: Number of Tokens * The price of the token. We will assume that the `\(500000\)` tokens are the only tokens in existence. We chose the following market capitalizations: <b> Val1MCap, Val2MCap, Val5MCap, Val10MCap, Val25MCap, Val50MCap, Val100MCap, Val250MCap, Val500MCap, Val1BCap </b> Along with some other recent air drops: <b> `$Badger2MCap`, `$MIR17MCap`, `$1Inch164MCap`, `$UNI734MCap` </b>. For example, to get a market capitalization of `$1,000,000` (<b> Val1MCap </b>), we will need the price of the token to be `$2` as we have `\(500,000\)` tokens. --- Let's create a table where we have the dollar value of each `Wallet Address` based on the market capitalization. We can simply multiply by the appropriate price of the token with the number of tokens for each row as follows: ```r cap <- torn %>% select(TORN) %>% mutate(Val1MCap = TORN*2, '$Badger2MCap' = TORN*4, Val5MCap = TORN*10, Val10MCap = TORN*20, 'MIR17MCap' = TORN*34, Val25MCap = TORN*50, Val50MCap = TORN*100, Val100MCap = TORN*200, '$Inch164MCap' = TORN*328, Val250MCap = TORN*500, Val500MCap = TORN*1000, '$UNI734MCap' = TORN*1468, Val1BCap = TORN*2000) ``` --- ## The Market Capitalization Table Below shows the first few rows and columns of the table: ```r cap %>% select(1:8) %>% head() %>% kable() %>% kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) ``` <table class="table table-striped table-hover" style="width: auto !important; margin-left: auto; margin-right: auto;"> <thead> <tr> <th style="text-align:right;"> TORN </th> <th style="text-align:right;"> Val1MCap </th> <th style="text-align:right;"> $Badger2MCap </th> <th style="text-align:right;"> Val5MCap </th> <th style="text-align:right;"> Val10MCap </th> <th style="text-align:right;"> MIR17MCap </th> <th style="text-align:right;"> Val25MCap </th> <th style="text-align:right;"> Val50MCap </th> </tr> </thead> <tbody> <tr> <td style="text-align:right;"> 616.7693 </td> <td style="text-align:right;"> 1233.5386 </td> <td style="text-align:right;"> 2467.0773 </td> <td style="text-align:right;"> 6167.693 </td> <td style="text-align:right;"> 12335.386 </td> <td style="text-align:right;"> 20970.157 </td> <td style="text-align:right;"> 30838.466 </td> <td style="text-align:right;"> 61676.93 </td> </tr> <tr> <td style="text-align:right;"> 101.4204 </td> <td style="text-align:right;"> 202.8408 </td> <td style="text-align:right;"> 405.6816 </td> <td style="text-align:right;"> 1014.204 </td> <td style="text-align:right;"> 2028.408 </td> <td style="text-align:right;"> 3448.294 </td> <td style="text-align:right;"> 5071.020 </td> <td style="text-align:right;"> 10142.04 </td> </tr> <tr> <td style="text-align:right;"> 478.6841 </td> <td style="text-align:right;"> 957.3682 </td> <td style="text-align:right;"> 1914.7364 </td> <td style="text-align:right;"> 4786.841 </td> <td style="text-align:right;"> 9573.682 </td> <td style="text-align:right;"> 16275.259 </td> <td style="text-align:right;"> 23934.205 </td> <td style="text-align:right;"> 47868.41 </td> </tr> <tr> <td style="text-align:right;"> 328.5185 </td> <td style="text-align:right;"> 657.0369 </td> <td style="text-align:right;"> 1314.0738 </td> <td style="text-align:right;"> 3285.185 </td> <td style="text-align:right;"> 6570.369 </td> <td style="text-align:right;"> 11169.627 </td> <td style="text-align:right;"> 16425.923 </td> <td style="text-align:right;"> 32851.85 </td> </tr> <tr> <td style="text-align:right;"> 121.5414 </td> <td style="text-align:right;"> 243.0827 </td> <td style="text-align:right;"> 486.1655 </td> <td style="text-align:right;"> 1215.414 </td> <td style="text-align:right;"> 2430.827 </td> <td style="text-align:right;"> 4132.406 </td> <td style="text-align:right;"> 6077.068 </td> <td style="text-align:right;"> 12154.14 </td> </tr> <tr> <td style="text-align:right;"> 200.9907 </td> <td style="text-align:right;"> 401.9814 </td> <td style="text-align:right;"> 803.9629 </td> <td style="text-align:right;"> 2009.907 </td> <td style="text-align:right;"> 4019.814 </td> <td style="text-align:right;"> 6833.684 </td> <td style="text-align:right;"> 10049.536 </td> <td style="text-align:right;"> 20099.07 </td> </tr> </tbody> </table> --- ## Summary of Market Capitalization For simplicity, we can use the following summary statistics to give an overview of what people can expect: <b> Maximum, 90th percentile, 75th percentile, Median, Mean, 25th percentile, 10th percentile, Minimum </b> We will use `gather()` to pivot the table into a long table so we can `group_by()` market capitalization. ```r summary <- cap %>% gather() %>% group_by(key) %>% summarise(Max = max(value), '90%-tile' = quantile(value, 0.9), '70%-tile' = quantile(value, 0.75), Median = mean(value), '25%-tile' = quantile(value, 0.25), '10%-tile' = quantile(value,0.1), Min = min(value)) %>% column_to_rownames(var="key") ``` --- ```r summary ``` ``` ## Max 90%-tile 70%-tile Median 25%-tile ## $Badger2MCap 10159.810 589.7860 233.39164 266.16982 38.37708 ## $Inch164MCap 833104.398 48362.4549 19138.11425 21825.92494 3146.92065 ## $UNI734MCap 3728650.171 216451.4748 85654.73085 97684.32260 14084.38877 ## MIR17MCap 86358.383 5013.1813 1983.82892 2262.44344 326.20519 ## TORN 2539.952 147.4465 58.34791 66.54245 9.59427 ## Val100MCap 507990.486 29489.3017 11669.58186 13308.49082 1918.85406 ## Val10MCap 50799.049 2948.9302 1166.95819 1330.84908 191.88541 ## Val1BCap 5079904.864 294893.0174 116695.81860 133084.90817 19188.54057 ## Val1MCap 5079.905 294.8930 116.69582 133.08491 19.18854 ## Val250MCap 1269976.216 73723.2543 29173.95465 33271.22704 4797.13514 ## Val25MCap 126997.622 7372.3254 2917.39547 3327.12270 479.71351 ## Val500MCap 2539952.432 147446.5087 58347.90930 66542.45409 9594.27028 ## Val50MCap 253995.243 14744.6509 5834.79093 6654.24541 959.42703 ## Val5MCap 25399.524 1474.4651 583.47909 665.42454 95.94270 ## 10%-tile Min ## $Badger2MCap 13.337580 0.024883902 ## $Inch164MCap 1093.681570 2.040479979 ## $UNI734MCap 4894.891904 9.132392099 ## MIR17MCap 113.369431 0.211513169 ## TORN 3.334395 0.006220976 ## Val100MCap 666.879006 1.244195109 ## Val10MCap 66.687901 0.124419511 ## Val1BCap 6668.790060 12.441951089 ## Val1MCap 6.668790 0.012441951 ## Val250MCap 1667.197515 3.110487772 ## Val25MCap 166.719752 0.311048777 ## Val500MCap 3334.395030 6.220975544 ## Val50MCap 333.439503 0.622097554 ## Val5MCap 33.343950 0.062209755 ``` ## Readability For readability, let's transpose the table, round the values to 2 decimals, and reorder the market capitalization as follows: ```r summary_t <- t(summary) summary_t <- as.data.frame(summary_t) %>% rownames_to_column(., var = "rowname") %>% mutate_if(is.numeric, round, 2) %>% column_to_rownames(var="rowname") %>% select(TORN, Val1MCap, '$Badger2MCap', Val5MCap, Val10MCap, 'MIR17MCap', Val25MCap, Val50MCap, Val100MCap, '$Inch164MCap', Val250MCap, Val500MCap, '$UNI734MCap', Val1BCap) ``` --- ```r summary_t ``` ``` ## TORN Val1MCap $Badger2MCap Val5MCap Val10MCap MIR17MCap Val25MCap ## Max 2539.95 5079.90 10159.81 25399.52 50799.05 86358.38 126997.62 ## 90%-tile 147.45 294.89 589.79 1474.47 2948.93 5013.18 7372.33 ## 70%-tile 58.35 116.70 233.39 583.48 1166.96 1983.83 2917.40 ## Median 66.54 133.08 266.17 665.42 1330.85 2262.44 3327.12 ## 25%-tile 9.59 19.19 38.38 95.94 191.89 326.21 479.71 ## 10%-tile 3.33 6.67 13.34 33.34 66.69 113.37 166.72 ## Min 0.01 0.01 0.02 0.06 0.12 0.21 0.31 ## Val50MCap Val100MCap $Inch164MCap Val250MCap Val500MCap $UNI734MCap ## Max 253995.24 507990.49 833104.40 1269976.22 2539952.43 3728650.17 ## 90%-tile 14744.65 29489.30 48362.45 73723.25 147446.51 216451.47 ## 70%-tile 5834.79 11669.58 19138.11 29173.95 58347.91 85654.73 ## Median 6654.25 13308.49 21825.92 33271.23 66542.45 97684.32 ## 25%-tile 959.43 1918.85 3146.92 4797.14 9594.27 14084.39 ## 10%-tile 333.44 666.88 1093.68 1667.20 3334.40 4894.89 ## Min 0.62 1.24 2.04 3.11 6.22 9.13 ## Val1BCap ## Max 5079904.86 ## 90%-tile 294893.02 ## 70%-tile 116695.82 ## Median 133084.91 ## 25%-tile 19188.54 ## 10%-tile 6668.79 ## Min 12.44 ``` --- ## Formattable To make the plot more attractive, we can use `formattable` to create colors based on the values as follows: ```r colortile <- function(min.color = "lightpink", max.color = "lightgreen", fun = "currency") { fun <- match.fun(fun) formatter("span", x ~ fun(x), style = function(y) style( display = "block", direction = "rtl", "border-radius" = "4px", "padding-right" = "2px", "background-color" = csscolor(gradient(as.numeric(y),min.color='lightpink', max.color = 'lightgreen')) ) ) } formattable(summary_t, lapply(1:nrow(summary_t), function(row) { area(row, col = -1) ~ colortile() } ) ) ``` --- <p align="center"> <img src="summary.png"> </p> --- ## Conclusion Speculating on the value of future `\(TORN\)` tokens, the median airdrop recipient would get `$13,300.56` from their 66.54 tokens if the market capitalization of the protocol reaches a "modest" `$100 Million`. That's quite a few cups of coffee! <p align="right"> <img src="Cut_outs/Cut_out_07.png" width="200px" height="200px"> </p>