Skip to content

Commit

Permalink
ERC20 Token Subgraph and Analytics APP (#546)
Browse files Browse the repository at this point in the history
* check in README

* ERC20 Token Subgraph

* ERC20 Token Analytics APP

* new token analytics image

* small changes on schema format

* modified based on PR review feedbacks

* update totalSupply when needed

* add protocols folder for CI/CD

* refactor schema

* change based on Harsh's review feedback

* change to deal with some transfer function also emit burn/mint events

* ERC721 subgraph schema

* remove ERC721 subgraph schema to create a clean PR

* ERC721 subgraph schema

* remove ERC721 subgraph schema to create a clean ERC20 PR
  • Loading branch information
corerouter authored Jul 30, 2022
1 parent b404106 commit 6bd81ac
Show file tree
Hide file tree
Showing 20 changed files with 1,688 additions and 317 deletions.
29 changes: 29 additions & 0 deletions apps/erc20-analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# ERC20 Token Analytics

![Uniswap Analytics](../../docs/images/apps/erc20-analytics.png "ERC20 Token Analytics")

## Development

Install venv

```
python3 -m venv venv
```

Activate venv

```
source venv/bin/activate
```

Install dependencies

```
pip3 install -r requirements.txt
```

Run locally

```
streamlit run app.py
```
138 changes: 138 additions & 0 deletions apps/erc20-analytics/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import streamlit as st
from streamlit_autorefresh import st_autorefresh
import altair as alt
import pandas as pd
from subgrounds.subgrounds import Subgrounds

# Refresh every 30 seconds
REFRESH_INTERVAL_SEC = 30

sg = Subgrounds()
erc20Subgraph = sg.load_subgraph("https://api.thegraph.com/subgraphs/name/corerouter/erc20")

def fetch_tokens(subgraph):
tokens = subgraph.Query.tokens(
orderBy='id',
orderDirection='desc',
first=10
)

df = sg.query_df(
[
tokens.id,
tokens.symbol,
tokens.totalSupply,
tokens.holderCount,
tokens.transferCount
]
)

df = df.rename(
columns=lambda x: x[len("tokens_") :]
)

df["address"] = df["id"]
df["totalSupply"] = df["totalSupply"].map("{:,.0f}".format)
df["holderCount"] = df["holderCount"].map("{:,.0f}".format)
df["transferCount"] = df["transferCount"].map("{:,.0f}".format)

return df[["symbol", "totalSupply", "holderCount", "transferCount", "address"]]

def fetch_account_token(subgraph, tokenAddress):
accountBalances = subgraph.Query.accountBalances(
orderBy='amount',
orderDirection='desc',
first=10,
where={
'token' : tokenAddress
}
)
account_df = sg.query_df(
[
accountBalances.account,
accountBalances.amount
]
)

tokenDailySnapshots = subgraph.Query.tokenDailySnapshots(
orderBy='id',
orderDirection='desc',
first=100,
where={
'token' : tokenAddress
}
)

token_df = sg.query_df(
[
tokenDailySnapshots.id,
tokenDailySnapshots.dailyTransferCount,
tokenDailySnapshots.dailyTransferAmount
]
)

if account_df.empty | token_df.empty:
return account_df,token_df

account_df = account_df.rename(
columns=lambda x: x[len("accountBalances_") :]
)
account_df["amount"] = account_df["amount"].map("{:,.2f}".format)

token_df = token_df.rename(
columns=lambda x: x[len("tokenDailySnapshots_") :]
)
token_df["Date"] = pd.to_datetime(token_df["id"].str.split("-").apply(lambda x: x[1]), unit="d").dt.date
token_df = token_df.drop(columns="id")

return account_df,token_df


st.set_page_config(layout="wide")
ticker = st_autorefresh(interval=REFRESH_INTERVAL_SEC * 1000, key="ticker")
st.title("ERC20 Token Analytics")

data_loading = st.text(f"[Every {REFRESH_INTERVAL_SEC} seconds] Loading data...")
df = fetch_tokens(erc20Subgraph)
data_loading.text(f"[Every {REFRESH_INTERVAL_SEC} seconds] Loading data... done!")

st.header("Token list")
st.markdown(df.to_markdown())

st.header("Specific Token Metrics")
symbol = st.selectbox(
"Select token",
df["symbol"]
)

tokenAddress = df.loc[df['symbol'] == symbol, 'address'].iloc[0]
data_loading = st.text(f"[Every {REFRESH_INTERVAL_SEC} seconds] Loading data...")
account_df,token_df = fetch_account_token(erc20Subgraph, tokenAddress)
data_loading.text(f"[Every {REFRESH_INTERVAL_SEC} seconds] Loading data... done!")

if(account_df.empty | token_df.empty):
st.write("No data yet.")
else:
st.header("Top Account")
st.markdown(account_df.to_markdown())

st.header("Token Daily Snapshot")
token_snapshot_dailyTransferCount_line_chart = (
alt.Chart(token_df)
.mark_line()
.encode(
x="Date:T",
y="dailyTransferCount:Q"
)
)
st.altair_chart(token_snapshot_dailyTransferCount_line_chart, use_container_width=True)

token_snapshot_dailyTransferAmount_line_chart = (
alt.Chart(token_df)
.mark_line()
.encode(
x="Date:T",
y="dailyTransferAmount:Q"
)
)
st.altair_chart(token_snapshot_dailyTransferAmount_line_chart, use_container_width=True)
5 changes: 5 additions & 0 deletions apps/erc20-analytics/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
streamlit
streamlit-autorefresh
subgrounds
altair
tabulate
Binary file added docs/images/apps/erc20-analytics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 29 additions & 1 deletion subgraphs/erc20/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
# ERC20-Subgraph
# ERC20 Token Subgraph

The subgraph read erc20 token list from IPFS, monitor the tokens' event and aggregate the results accordingly.

## Calculation Methodology v1.0.0

### Token's Current Holder Count

Count of Unique Addresses which are currently holding the token

### Token's Cumulative Holder Count

Count of Unique Addresses which have held or are holding the token

### Token's Transfer Count

Count of Transfer Event which have interacted with the protocol via transfer transaction

`Transfers`

### Token's Daily Transfer Amount

Total number of tokens transfered between accounts during a day via transfer transaction

`Transfers`

ERC20 Token Standard

- https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
52 changes: 52 additions & 0 deletions subgraphs/erc20/abis/Burnable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[
{
"constant": false,
"inputs": [
{
"name": "value",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "burner",
"type": "address"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "burner",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Burn",
"type": "event",
"_alias": "Burn"
}
]
Loading

0 comments on commit 6bd81ac

Please sign in to comment.