-
Notifications
You must be signed in to change notification settings - Fork 245
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ERC20 Token Subgraph and Analytics APP (#546)
* 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
1 parent
b404106
commit 6bd81ac
Showing
20 changed files
with
1,688 additions
and
317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# 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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
streamlit | ||
streamlit-autorefresh | ||
subgrounds | ||
altair | ||
tabulate |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
] |
Oops, something went wrong.