class: center, middle, inverse, title-slide .title[ # Predicting Growth in L2 Chains
] .author[ ### Omni Analytics Group ] --- ## Introduction A Layer 2 chain, or L2 chain, helps to keep in check the growth of L1 chains such as Ethereum. ![](l2.png) --- ## Goals As L2 chains continue to grow, it is worth devoting some statistical expertise to attempting to forecast their growth. In this case study, we will focus on two: `Polygon` and `Optimism`, and use a Brownian Motion based forecasting technique in order to forecast the growth. Let's get started! --- ## The Data First, let's begin by loading in our two sources of data. We'll start with `Polygon`: ```r pol <- read_csv("data/polygon.csv") pol %>% head %>% kable ``` <table> <thead> <tr> <th style="text-align:left;"> Date(UTC) </th> <th style="text-align:right;"> UnixTimeStamp </th> <th style="text-align:right;"> No. of Verified Contracts </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> 2021-05-12 </td> <td style="text-align:right;"> 1620777600 </td> <td style="text-align:right;"> 2 </td> </tr> <tr> <td style="text-align:left;"> 2021-05-17 </td> <td style="text-align:right;"> 1621209600 </td> <td style="text-align:right;"> 1 </td> </tr> <tr> <td style="text-align:left;"> 2021-05-18 </td> <td style="text-align:right;"> 1621296000 </td> <td style="text-align:right;"> 6 </td> </tr> <tr> <td style="text-align:left;"> 2021-05-28 </td> <td style="text-align:right;"> 1622160000 </td> <td style="text-align:right;"> 1 </td> </tr> <tr> <td style="text-align:left;"> 2021-06-09 </td> <td style="text-align:right;"> 1623196800 </td> <td style="text-align:right;"> 468 </td> </tr> <tr> <td style="text-align:left;"> 2021-06-10 </td> <td style="text-align:right;"> 1623283200 </td> <td style="text-align:right;"> 228 </td> </tr> </tbody> </table> --- ## Optimism Data Next, the `Optimism` data: ```r optim <- read_csv("data/optimistic.csv") optim %>% head %>% kable ``` <table> <thead> <tr> <th style="text-align:left;"> Date(UTC) </th> <th style="text-align:right;"> UnixTimeStamp </th> <th style="text-align:right;"> No. of Verified Contracts </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> 2021-01-14 </td> <td style="text-align:right;"> 1610582400 </td> <td style="text-align:right;"> 0 </td> </tr> <tr> <td style="text-align:left;"> 2021-01-15 </td> <td style="text-align:right;"> 1610668800 </td> <td style="text-align:right;"> 0 </td> </tr> <tr> <td style="text-align:left;"> 2021-01-16 </td> <td style="text-align:right;"> 1610755200 </td> <td style="text-align:right;"> 0 </td> </tr> <tr> <td style="text-align:left;"> 2021-01-17 </td> <td style="text-align:right;"> 1610841600 </td> <td style="text-align:right;"> 0 </td> </tr> <tr> <td style="text-align:left;"> 2021-01-18 </td> <td style="text-align:right;"> 1610928000 </td> <td style="text-align:right;"> 0 </td> </tr> <tr> <td style="text-align:left;"> 2021-01-19 </td> <td style="text-align:right;"> 1611014400 </td> <td style="text-align:right;"> 0 </td> </tr> </tbody> </table> --- ## A First Glance at the Polygon Data We do some minor data cleaning below: ```r ## Day of week pol$Day <- weekdays(pol$`Date(UTC)`) pol$Day <- factor(pol$Day, levels=weekdays(pol$`Date(UTC)`[2]+0:6)) pol$Contracts <- pol$`No. of Verified Contracts` optim$Day <- weekdays(optim$`Date(UTC)`) optim$Day <- factor(optim$Day, levels=weekdays(pol$`Date(UTC)`[2]+0:6)) optim$Contracts <- optim$`No. of Verified Contracts` ## Weekday wise Contracts Deployed pol_pld <- pol[pol$Contracts>0,] pol_plot <- pol_pld %>% ggplot(aes(x=Day, y=Contracts, fill=Day)) + geom_boxplot() + scale_fill_viridis(discrete = TRUE, alpha=0.6) + geom_jitter(color="black", size=0.4, alpha=0.9) + theme(legend.position="none",plot.title = element_text(size=11)) + ggtitle("Contracts Verified Weekday Wise (Polygon Chain)") + xlab("Weekday")+ scale_y_continuous(limits = quantile(pol_pld$Contracts, c(0.01, 0.99))) ``` --- We immediately see a strong weekend effect in the number of contracts verified for Polygon <img src="growth_files/figure-html/unnamed-chunk-4-1.png" style="display: block; margin: auto;" /> --- ## A First Glance at the Optimism Data We perform a similar routine for the optimism data ```r optim_pld <- optim[optim$Contracts>0,] optim_plot <- optim_pld %>% ggplot(aes(x=Day, y=Contracts, fill=Day)) + geom_boxplot() + scale_fill_viridis(discrete = TRUE, alpha=0.6) + geom_jitter(color="black", size=0.4, alpha=0.9) + theme(legend.position="none",plot.title = element_text(size=11)) + ggtitle("Contracts Verified Weekday Wise (Optimistic Chain)") + xlab("Weekday")+ scale_y_continuous(limits = quantile(optim_pld$Contracts, c(0.01, 0.99))) ``` --- The weekend effect is much less pronounced for the Optimism chain, though there still remains a noticeable dropoff in verified contracts on Sundays. <img src="growth_files/figure-html/unnamed-chunk-6-1.png" style="display: block; margin: auto;" /> --- ## Polygon Calendar (2022) We can visualize a calendar of the 2022 Polygon data using the following code: ```r pol22d <- data.frame(Date = seq(ymd('2022-01-01'),ymd('2022-12-31'), by = 'days'), Contracts = 0) pol22d$Contracts <- pol$Contracts[match(pol22d$Date,pol$`Date(UTC)`)] pol22d$ContractsCuts <- as.character(cut(pol22d$Contracts, c(0:6*100),right=FALSE)) pol22d$ContractsCuts <- fct_reorder(pol22d$ContractsCuts, pol22d$Contracts) pol22 <- calendR( title = "Polygon 2022",title.size = 20,mbg.col = 2,weeknames = c("M","T","W","T","F","S","S"),subtitle = "Contracts Verified",subtitle.size = 10,months.col = "white", year = 2022,special.days = as.character(pol22d$ContractsCuts),special.col = brewer.pal(length(levels(pol22d$ContractsCuts)), "Oranges")[order(levels(pol22d$ContractsCuts))],legend.pos = "bottom" ) ``` --- <img src="growth_files/figure-html/unnamed-chunk-8-1.png" style="display: block; margin: auto;" /> --- ## Polygon Calendar (2021 + 2022) And for 2021 + 2022 combined: ```r pol21d <- data.frame(Date = seq(ymd('2021-01-01'),ymd('2021-12-31'), by = 'days'), Contracts = 0) pol21d$Contracts <- pol$Contracts[match(pol21d$Date,pol$`Date(UTC)`)] pol21d$ContractsCuts <- as.character(cut(pol21d$Contracts, c(0:5*100,3900,4000),right=FALSE,dig.lab=10)) pol21d$ContractsCuts <- fct_reorder(pol21d$ContractsCuts, pol21d$Contracts) pol21 <- calendR( title = "Polygon 2021",,title.size = 20,mbg.col = 2,weeknames = c("M","T","W","T","F","S","S"),subtitle = "Contracts Verified",subtitle.size = 10,months.col = "white", year = 2021,special.days = as.character(pol21d$ContractsCuts),special.col = brewer.pal(length(levels(pol21d$ContractsCuts)), "Oranges")[order(levels(pol21d$ContractsCuts))],legend.pos = "bottom" ) pol_calplot <- pol21+pol22 ``` --- <img src="growth_files/figure-html/unnamed-chunk-10-1.png" style="display: block; margin: auto;" /> --- ## Optimism Calendar (2022) A similar routine allows us to compute the calendar for Optimism: ```r opt22d <- data.frame(Date = seq(ymd('2022-01-01'),ymd('2022-12-31'), by = 'days'), Contracts = 0) opt22d$Contracts <- optim$Contracts[match(opt22d$Date,optim$`Date(UTC)`)] opt22d$ContractsCuts <- as.character(cut(opt22d$Contracts, c(0,1,1:4*20),right=FALSE)) opt22d$ContractsCuts <- fct_reorder(opt22d$ContractsCuts, opt22d$Contracts) opt22 <- calendR( title = "Optimistic 2022",,title.size = 20,mbg.col = 2,weeknames = c("M","T","W","T","F","S","S"),subtitle = "Contracts Verified",subtitle.size = 10,months.col = "white", year = 2022,special.days = as.character(opt22d$ContractsCuts),special.col = brewer.pal(length(levels(opt22d$ContractsCuts)), "Oranges")[order(levels(opt22d$ContractsCuts))],legend.pos = "bottom" ) ``` --- <img src="growth_files/figure-html/unnamed-chunk-12-1.png" style="display: block; margin: auto;" /> --- ## Optimism Calendar (2021 + 2022) And for 2021 + 2022 combined: ```r opt21d <- data.frame(Date = seq(ymd('2021-01-01'),ymd('2021-12-31'), by = 'days'), Contracts = 0) opt21d$Contracts <- optim$Contracts[match(opt21d$Date,optim$`Date(UTC)`)] opt21d$ContractsCuts <- as.character(cut(opt21d$Contracts, c(0,1,1:4*10),right=FALSE)) opt21d$ContractsCuts <- fct_reorder(opt21d$ContractsCuts, opt21d$Contracts) opt21 <- calendR( title = "Optimistic 2021",,title.size = 20,mbg.col = 2,weeknames = c("M","T","W","T","F","S","S"),subtitle = "Contracts Verified",subtitle.size = 10,months.col = "white", year = 2021,special.days = as.character(opt21d$ContractsCuts),special.col = brewer.pal(length(levels(opt21d$ContractsCuts)), "Oranges")[order(levels(opt21d$ContractsCuts))],legend.pos = "bottom" ) optim_calplot <- opt21+opt22 ``` --- <img src="growth_files/figure-html/unnamed-chunk-14-1.png" style="display: block; margin: auto;" /> --- ## Brownian Motion Lastly, we use brownian motion in order to forecast each ```r ## Brownian Motion polbd <- pol[5:nrow(pol),] polbd$Date <- polbd$`Date(UTC)` polbd <- polbd[,c("Date","Contracts")] optimbd <- optim optimbd$Date <- optimbd$`Date(UTC)` optimbd <- optimbd[,c("Date","Contracts")] polbd_n <- data.frame(Date = max(polbd$Date)+1:30) pol_forecast <- brownian_forecast(polbd,polbd_n) optimbd_n <- data.frame(Date = max(optimbd$Date)+1:30) optim_forecast <- brownian_forecast(optimbd,optimbd_n) ``` --- ## Polygon Forecast ```r pol_forecast %>% head %>% kable ``` <table> <thead> <tr> <th style="text-align:left;"> Date </th> <th style="text-align:right;"> Prediction </th> <th style="text-align:right;"> Upper </th> <th style="text-align:right;"> Lower </th> <th style="text-align:left;"> Method </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> 2022-06-09 </td> <td style="text-align:right;"> 350.0542 </td> <td style="text-align:right;"> 722.9660 </td> <td style="text-align:right;"> 159.82879 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-10 </td> <td style="text-align:right;"> 373.3307 </td> <td style="text-align:right;"> 935.5918 </td> <td style="text-align:right;"> 131.22131 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-11 </td> <td style="text-align:right;"> 394.7485 </td> <td style="text-align:right;"> 1109.2994 </td> <td style="text-align:right;"> 112.10611 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-12 </td> <td style="text-align:right;"> 422.6976 </td> <td style="text-align:right;"> 1444.3612 </td> <td style="text-align:right;"> 89.16038 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-13 </td> <td style="text-align:right;"> 451.6471 </td> <td style="text-align:right;"> 1709.9363 </td> <td style="text-align:right;"> 71.10531 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-14 </td> <td style="text-align:right;"> 485.0554 </td> <td style="text-align:right;"> 2157.0793 </td> <td style="text-align:right;"> 62.26108 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> </tbody> </table> --- ## Optimism Forecast ```r optim_forecast %>% head %>% kable ``` <table> <thead> <tr> <th style="text-align:left;"> Date </th> <th style="text-align:right;"> Prediction </th> <th style="text-align:right;"> Upper </th> <th style="text-align:right;"> Lower </th> <th style="text-align:left;"> Method </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;"> 2022-06-08 </td> <td style="text-align:right;"> 73.08083 </td> <td style="text-align:right;"> 300.000 </td> <td style="text-align:right;"> 3.0000000 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-09 </td> <td style="text-align:right;"> 190.82595 </td> <td style="text-align:right;"> 1801.875 </td> <td style="text-align:right;"> 0.4272321 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-10 </td> <td style="text-align:right;"> 503.55897 </td> <td style="text-align:right;"> 4259.135 </td> <td style="text-align:right;"> 0.1996875 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-11 </td> <td style="text-align:right;"> 1301.39791 </td> <td style="text-align:right;"> 7035.469 </td> <td style="text-align:right;"> 0.1049063 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-12 </td> <td style="text-align:right;"> 2740.34046 </td> <td style="text-align:right;"> 20576.374 </td> <td style="text-align:right;"> 0.0644000 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> <tr> <td style="text-align:left;"> 2022-06-13 </td> <td style="text-align:right;"> 5409.50414 </td> <td style="text-align:right;"> 32827.902 </td> <td style="text-align:right;"> 0.0419925 </td> <td style="text-align:left;"> Brownian Motion </td> </tr> </tbody> </table>