Adds Topledger on-chain data (#5029)

* Adds Topledger on-chain data

* Update with Review Comments

* Updates API Documentation for Topledger API

* Removes Duplicate entry from trail_map

* Updates Topledger Org Slugs & API keys

* Fix Linting Error

* Fix Linting Error

* Fix Linting Error

* Fix pylint errors

* Fix onchain_controller test for help text

---------

Co-authored-by: Deepak Khatri <3785149+dkdocs@users.noreply.github.com>
This commit is contained in:
Harsh Vardhan Sharma
2023-06-30 21:46:59 +05:30
committed by GitHub
parent d2ed7c28ac
commit ecf7814ffd
11 changed files with 436 additions and 1 deletions

View File

@@ -258,6 +258,8 @@ class CryptoController(model.CryptoRoot):
`token_decimals`: Helper methods that gets token decimals number. [Source: Ethplorer]\n
`top`: Get top 50 tokens. [Source: Ethplorer]\n
`top_chart`: Display top ERC20 tokens [Source: Ethplorer]\n
`topledger`: Returns Topledger's Data for the given Organization's Slug[org_slug] based\n
`topledger_chart`: Display on-chain data from Topledger. [Source: Topledger]\n
`ttcp`: Get most traded crypto pairs on given decentralized exchange in chosen time period.\n
`ttcp_chart`: Prints table showing most traded crypto pairs on given decentralized exchange in chosen time period.\n
`tv`: Get token volume on different Decentralized Exchanges. [Source: https://graphql.bitquery.io/]\n

View File

@@ -463,6 +463,8 @@ class CryptoOnChain(Category):
`token_decimals`: Helper methods that gets token decimals number. [Source: Ethplorer]\n
`top`: Get top 50 tokens. [Source: Ethplorer]\n
`top_chart`: Display top ERC20 tokens [Source: Ethplorer]\n
`topledger`: Returns Topledger's Data for the given Organization's Slug[org_slug] based\n
`topledger_chart`: Display on-chain data from Topledger. [Source: Topledger]\n
`ttcp`: Get most traded crypto pairs on given decentralized exchange in chosen time period.\n
`ttcp_chart`: Prints table showing most traded crypto pairs on given decentralized exchange in chosen time period.\n
`tv`: Get token volume on different Decentralized Exchanges. [Source: https://graphql.bitquery.io/]\n
@@ -533,6 +535,8 @@ class CryptoOnChain(Category):
self.token_decimals = lib.crypto_onchain_ethplorer_model.get_token_decimals
self.top = lib.crypto_onchain_ethplorer_model.get_top_tokens
self.top_chart = lib.crypto_onchain_ethplorer_view.display_top_tokens
self.topledger = lib.crypto_onchain_topledger_model.get_topledger_data
self.topledger_chart = lib.crypto_onchain_topledger_view.display_topledger_data
self.ttcp = lib.crypto_onchain_bitquery_model.get_most_traded_pairs
self.ttcp_chart = lib.crypto_onchain_bitquery_view.display_most_traded_pairs
self.tv = lib.crypto_onchain_bitquery_model.get_token_volume_on_dexes

View File

@@ -258,6 +258,8 @@ from openbb_terminal.cryptocurrency.onchain import (
whale_alert_view as crypto_onchain_whale_alert_view,
shroom_model as crypto_onchain_shroom_model,
shroom_view as crypto_onchain_shroom_view,
topledger_model as crypto_onchain_topledger_model,
topledger_view as crypto_onchain_topledger_view,
)

View File

@@ -129,6 +129,7 @@ crypto.onchain.query_graph,crypto_onchain_bitquery_model.query_graph,
crypto.onchain.th,crypto_onchain_ethplorer_model.get_token_history,crypto_onchain_ethplorer_view.display_token_history
crypto.onchain.token_decimals,crypto_onchain_ethplorer_model.get_token_decimals,
crypto.onchain.top,crypto_onchain_ethplorer_model.get_top_tokens,crypto_onchain_ethplorer_view.display_top_tokens
crypto.onchain.topledger,crypto_onchain_topledger_model.get_topledger_data,crypto_onchain_topledger_view.display_topledger_data
crypto.onchain.ttcp,crypto_onchain_bitquery_model.get_most_traded_pairs,crypto_onchain_bitquery_view.display_most_traded_pairs
crypto.onchain.tv,crypto_onchain_bitquery_model.get_token_volume_on_dexes,crypto_onchain_bitquery_view.display_dex_volume_for_token
crypto.onchain.tx,crypto_onchain_ethplorer_model.get_tx_info,crypto_onchain_ethplorer_view.display_tx_info
1 trail model view
129 crypto.onchain.th crypto_onchain_ethplorer_model.get_token_history crypto_onchain_ethplorer_view.display_token_history
130 crypto.onchain.token_decimals crypto_onchain_ethplorer_model.get_token_decimals
131 crypto.onchain.top crypto_onchain_ethplorer_model.get_top_tokens crypto_onchain_ethplorer_view.display_top_tokens
132 crypto.onchain.topledger crypto_onchain_topledger_model.get_topledger_data crypto_onchain_topledger_view.display_topledger_data
133 crypto.onchain.ttcp crypto_onchain_bitquery_model.get_most_traded_pairs crypto_onchain_bitquery_view.display_most_traded_pairs
134 crypto.onchain.tv crypto_onchain_bitquery_model.get_token_volume_on_dexes crypto_onchain_bitquery_view.display_dex_volume_for_token
135 crypto.onchain.tx crypto_onchain_ethplorer_model.get_tx_info crypto_onchain_ethplorer_view.display_tx_info

View File

@@ -23,6 +23,8 @@ from openbb_terminal.cryptocurrency.onchain import (
ethplorer_model,
ethplorer_view,
shroom_view,
topledger_model,
topledger_view,
whale_alert_model,
whale_alert_view,
)
@@ -82,6 +84,7 @@ class OnchainController(BaseController):
"btcct",
"btcblockdata",
"query",
"topledger",
]
PATH = "/crypto/onchain/"
@@ -110,6 +113,7 @@ class OnchainController(BaseController):
mt.add_cmd("btcblockdata")
mt.add_cmd("gwei")
mt.add_cmd("whales")
mt.add_cmd("topledger")
mt.add_cmd("lt")
mt.add_cmd("dvcp")
mt.add_cmd("tv")
@@ -427,6 +431,55 @@ class OnchainController(BaseController):
else None,
)
@log_start_end(log=logger)
def call_topledger(self, other_args: List[str]):
"""Process topledger command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="topledger",
description="""
Display on-chain data from topledger.
[Source: Topledger]
""",
)
parser.add_argument(
"-o",
"--org",
dest="org_slug",
type=str,
help="Organization Slug",
choices=list(topledger_model.MAPPING.keys()),
default=None,
)
parser.add_argument(
"-q",
"--query",
dest="query_slug",
type=str,
help="Query Slug",
choices=[
query.get("slug")
for section in topledger_model.MAPPING.values()
for query in section.get("queries", [])
if query.get("slug")
],
default=None,
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
topledger_view.display_topledger_data(
org_slug=ns_parser.org_slug,
query_slug=ns_parser.query_slug,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_address(self, other_args: List[str]):
"""Process address command"""

View File

@@ -0,0 +1,309 @@
"""Topledger model"""
__docformat__ = "numpy"
import logging
from typing import Any, Dict, Optional, Tuple
import pandas as pd
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import request
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
MAPPING: Dict[str, Any] = {
"tensor": {
"api_key": "8eSjJnSuOC7uLWkrTQoWADStkNS2ZIy94pH5CsNn",
"tl_org_slug": "tensor",
"queries": [
{"id": 846, "slug": "daily-transactions"},
{"id": 847, "slug": "weekly-transactions"},
{"id": 848, "slug": "monthly-transactions"},
{"id": 849, "slug": "daily-users"},
{"id": 852, "slug": "monthly-users"},
{"id": 851, "slug": "weekly-users"},
{"id": 1092, "slug": "daily-gmv-sol"},
{"id": 1104, "slug": "weekly-gmv"},
{"id": 1105, "slug": "monthly-gmv"},
{"id": 1110, "slug": "daily-tvl"},
{"id": 1111, "slug": "weekly-tvl"},
{"id": 1112, "slug": "monthly-tvl"},
{"id": 1115, "slug": "top-traders-by-gmv"},
],
},
"genopets": {
"api_key": "67F9IFzXma0iusqMAb2VynopTfHqzri2zTeacQnh",
"tl_org_slug": "genopets",
"queries": [
{"id": 1206, "slug": "daily-users"},
{"id": 1207, "slug": "weekly-users"},
{"id": 1208, "slug": "monthly-users"},
{"id": 1209, "slug": "weekly-retention"},
{"id": 1210, "slug": "monthly-retention"},
{"id": 1214, "slug": "daily-transactions"},
{"id": 1215, "slug": "weekly-transactions"},
{"id": 1216, "slug": "monthly-transactions"},
{"id": 1221, "slug": "daily-$ki-volume"},
{"id": 1222, "slug": "weekly-$ki-volume"},
{"id": 1223, "slug": "monthly-$ki-volume"},
{"id": 1224, "slug": "daily-$gene-volume"},
{"id": 1225, "slug": "weekly-$gene-volume"},
{"id": 1226, "slug": "monthly-$gene-volume"},
{"id": 1229, "slug": "ghmp-daily-instruction-type-split"},
{"id": 1230, "slug": "ghmp-weekly-instruction-type-split"},
{"id": 1231, "slug": "ghmp-monthly-instruction-type-split"},
{"id": 1234, "slug": "gcmp-daily-instruction-type-split"},
{"id": 1235, "slug": "gcmp-weekly-instruction-type-split"},
{"id": 1236, "slug": "gcmp-monthly-instruction-type-split"},
{"id": 1238, "slug": "gsp-daily-instruction-type-split"},
{"id": 1239, "slug": "gsp-weekly-instruction-type-split"},
{"id": 1242, "slug": "harvestki-daily"},
{"id": 1245, "slug": "withdrawki-daily"},
],
},
"staratlas": {
"api_key": "l8Q9sOseZRAoFVqvTExxWJ8W9j4ETySHiDFGIHg3",
"tl_org_slug": "staratlas",
"queries": [
{"id": 1033, "slug": "daily-users"},
{"id": 1026, "slug": "weekly-users"},
{"id": 1040, "slug": "monthly-users"},
{"id": 1048, "slug": "weekly-retention"},
{"id": 1051, "slug": "monthly-retention"},
{"id": 1049, "slug": "daily-transactions"},
{"id": 1034, "slug": "weekly-transactions"},
{"id": 1043, "slug": "monthly-transactions"},
{"id": 1056, "slug": "ssp-daily-instruction-type-split"},
{"id": 1058, "slug": "ssp-weekly-instruction-type-split"},
{"id": 1037, "slug": "ssp-monthly-instruction-type-split"},
{"id": 1059, "slug": "daily-process-harvest-atlas-volume"},
{"id": 1036, "slug": "weekly-process-harvest-atlas-volume"},
{"id": 1039, "slug": "monthly-process-harvest-atlas-volume"},
{"id": 1038, "slug": "daily-gas-fee"},
{"id": 1046, "slug": "weekly-gas-fee"},
{"id": 1027, "slug": "monthly-gas-fee"},
],
},
"aurory": {
"api_key": "LzJODEsQnkySsqbCoRxPqj8KuN5x8SGK3AESatgm",
"tl_org_slug": "aurory",
"queries": [
{"id": 1147, "slug": "daily-users"},
{"id": 1132, "slug": "weekly-users"},
{"id": 1127, "slug": "monthly-users"},
{"id": 1166, "slug": "weekly-retention"},
{"id": 1135, "slug": "monthly-retention"},
{"id": 1143, "slug": "daily-transactions"},
{"id": 1153, "slug": "weekly-transactions"},
{"id": 1160, "slug": "monthly-transactions"},
{"id": 1149, "slug": "daily-aury-volume"},
{"id": 1165, "slug": "weekly-aury-volume"},
{"id": 1152, "slug": "monthly-aury-volume"},
{"id": 1151, "slug": "daily-xaury-volume"},
{"id": 1150, "slug": "weekly-xaury-volume"},
{"id": 1152, "slug": "monthly-xaury-volume"},
],
},
"sol_casino": {
"api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP",
"tl_org_slug": "tl",
"queries": [
{"id": 1468, "slug": "daily-transactions"},
{"id": 1471, "slug": "weekly-transactions"},
{"id": 1473, "slug": "monthly-transactions"},
{"id": 1481, "slug": "daily-active-wallets"},
{"id": 1482, "slug": "weekly-active-wallets"},
{"id": 1483, "slug": "monthly-active-wallets"},
{"id": 1505, "slug": "daily-volume"},
{"id": 1485, "slug": "daily-sol-deposits"},
{"id": 1486, "slug": "daily-usdc-usdt-deposits"},
{"id": 1488, "slug": "daily-bonk-deposits"},
],
},
"exchange_art": {
"api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP",
"tl_org_slug": "tl",
"queries": [
{"id": 1331, "slug": "daily-transactions"},
{"id": 1332, "slug": "weekly-transactions"},
{"id": 1333, "slug": "monthly-transactions"},
{"id": 1334, "slug": "daily-transaction-fee"},
{"id": 1335, "slug": "daily-users"},
{"id": 1336, "slug": "weekly-users"},
{"id": 1337, "slug": "monthly-users"},
{"id": 1338, "slug": "daily-transactions-by-programs"},
{"id": 1339, "slug": "weekly-transactions-by-programs"},
{"id": 1340, "slug": "monthly-transactions-by-programs"},
{"id": 1349, "slug": "daily-new-users"},
{"id": 1358, "slug": "weekly-new-users"},
{"id": 1359, "slug": "monthly-new-users"},
{"id": 1367, "slug": "daily-bids-placed"},
{"id": 1371, "slug": "daily-finearts-listed"},
{"id": 1418, "slug": "daily-primary-gmv"},
{"id": 1422, "slug": "weekly-primary-gmv"},
{"id": 1423, "slug": "monthly-primary-gmv"},
{"id": 1427, "slug": "daily-secondary-gmv"},
{"id": 1428, "slug": "weekly-secondary-gmv"},
{"id": 1429, "slug": "monthly-secondary-gmv"},
],
},
"xNFT_Backpack": {
"api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP",
"tl_org_slug": "tl",
"queries": [
{"id": 2318, "slug": "xnft-installed"},
{"id": 1738, "slug": "daily-xnft-installed"},
{"id": 1736, "slug": "daily-xnft-users"},
{"id": 1760, "slug": "xnft-by-tag"},
{"id": 1762, "slug": "xnft-by-rating"},
{"id": 2320, "slug": "top20-users-by-nft-installs"},
{"id": 1764, "slug": "mad-lad-nft"},
{"id": 1757, "slug": "users-by-xnft"},
{"id": 1731, "slug": "cumulative-xnft-installed"},
],
},
"bonk": {
"api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP",
"tl_org_slug": "tl",
"queries": [
{"id": 486, "slug": "bonk-transaction-fee"},
{"id": 506, "slug": "daily-bonk-burn"},
{"id": 507, "slug": "top-100-accounts-burning-bonk"},
{"id": 477, "slug": "daily-active-users"},
{"id": 488, "slug": "user-split-by-pools"},
{"id": 479, "slug": "bonk-transactions"},
{"id": 487, "slug": "transactions-split-by-pools"},
{"id": 514, "slug": "bonk-volume-usd"},
{"id": 483, "slug": "bonk-volume"},
{"id": 509, "slug": "bonk-holders"},
{"id": 484, "slug": "top-100-bonk-holders"},
{"id": 485, "slug": "top-wallets-by-transactions"},
{"id": 492, "slug": "daily-users-split-by-dex"},
],
},
"light_protocol": {
"api_key": "De2qJPBJKt19Yh24Z2n6kezIlaEBA4UYkBBjdbXn",
"tl_org_slug": "luminouslabs",
"queries": [
{"id": 1846, "slug": "hourly-transactions"},
{"id": 1506, "slug": "daily-transactions"},
{"id": 1690, "slug": "weekly-transactions"},
{"id": 1691, "slug": "monthly-transaction"},
{"id": 1692, "slug": "daily-fee"},
{"id": 1697, "slug": "daily-shielded-and-unshielded-volume"},
{"id": 1699, "slug": "weekly-shielded-and-unshielded-volume"},
{"id": 1700, "slug": "monthly-shielded-and-unshielded-volume"},
{"id": 1701, "slug": "shielded-and-unshielded-transactions-by-wallets"},
],
},
}
@log_start_end(log=logger)
def make_request(org_slug="", query_slug="") -> Tuple[Optional[int], Any]:
"""Helper methods for requests to topledger's query results [Source: Topledger]
Parameters
----------
org_slug: str
Organization Slug
query_slug: str
Query Slug
Returns
-------
Tuple[Optional[int], Any]
status code, response from api request
"""
org = MAPPING[org_slug]
if not org:
console.print(f"[red]Organization Slug[{org_slug}] is invalid[/red]\n")
logger.error("Organization Slug[%s] is invalid", org_slug)
return None, None
query_items = [x for x in org["queries"] if x["slug"] == query_slug]
if len(query_items) == 0:
console.print(
f"[red]Query Slug[{query_slug}] is invalid for Organization[{org_slug}][/red]\n"
)
logger.error(
"Query Slug[%s] is invalid for Organization[%s]", query_slug, org_slug
)
return None, None
query = query_items[0]
query_id = query["id"]
api_key = org["api_key"]
tl_org_slug = org["tl_org_slug"]
# """
# API Documentation
# ----------
# :param string tl_org_slug: Slug of Topledger Organization
# :param string query_id: the ID of the query to fetch result of
# :param string api_key: Authentication Key to fetch result
#
# API Format: HOST/<tl_org_slug>/api/queries/<query_id>/results.json?api_key=<api_key>
# """
url = f"https://analytics.topledger.xyz/{tl_org_slug}/api/queries/{query_id}/results.json?api_key={api_key}"
try:
response = request(url)
except Exception:
return None, None
result = {}
if response.status_code == 200:
result = response.json()
elif response.status_code == 401:
console.print("[red]Invalid API Key[/red]\n")
logger.error("Invalid Authentication: %s", response.text)
elif response.status_code == 401:
console.print("[red]API Key not authorized[/red]\n")
logger.error("Insufficient Authorization: %s", response.text)
elif response.status_code == 429:
console.print("[red]Exceeded number of calls per minute[/red]\n")
logger.error("Calls limit exceeded: %s", response.text)
else:
console.print(response.json()["message"])
logger.error("Error in request: %s", response.text)
return response.status_code, result
@log_start_end(log=logger)
def get_topledger_data(org_slug: str, query_slug: str) -> pd.DataFrame:
"""Returns Topledger's Data for the given Organization's Slug[org_slug] based
on Query Slug[query_slug] [Source: Topledger]
Parameters
----------
org_slug: str
Organization Slug
query_slug: str
Query Slug
Returns
-------
pd.DataFrame
Topledger Data
"""
if not org_slug:
console.print("[red]Org is blank[/red]\n")
logger.error("Org is blank")
return pd.DataFrame()
if not query_slug:
console.print("[red]Query is blank[/red]\n")
logger.error("Query is blank")
return pd.DataFrame()
status_code, response = make_request(org_slug, query_slug)
if status_code != 200:
return pd.DataFrame()
data = pd.json_normalize(response["query_result"]["data"]["rows"])
return pd.DataFrame(data)

View File

@@ -0,0 +1,57 @@
"""Topledger Data view"""
__docformat__ = "numpy"
import logging
import os
from typing import Optional
from openbb_terminal.cryptocurrency.onchain import topledger_model
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
print_rich_table,
)
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def display_topledger_data(
org_slug: str,
query_slug: str,
export: str = "",
sheet_name: Optional[str] = None,
) -> None:
"""Display on-chain data from Topledger. [Source: Topledger]
Parameters
----------
org_slug: str
Organization Slug
query_slug: str
Query Slug
export: str
Export dataframe data to csv,json,xlsx file
"""
df = topledger_model.get_topledger_data(org_slug, query_slug)
if df.empty:
console.print("Failed to retrieve data.")
return
df_data = df.copy()
print_rich_table(
df,
headers=list(df.columns),
show_index=False,
title=f"Topledger Query Result {query_slug} for {org_slug}",
export=bool(export),
)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
f"{query_slug} for {org_slug}",
df_data,
sheet_name,
)

View File

@@ -539,6 +539,7 @@ en:
crypto/onchain/btcblockdata: displays BTC single block data
crypto/onchain/gwei: check current eth gas fees
crypto/onchain/whales: check crypto wales transactions
crypto/onchain/topledger: get on-chain data from topledger
crypto/onchain/lt: last trades by dex or month
crypto/onchain/dvcp: daily volume for crypto pair
crypto/onchain/tv: token volume on DEXes

View File

@@ -16,6 +16,10 @@ whales --address # this is bugged
whales --min 5000000 --limit 3 --sort amount
whales --sort amount_usd -r
# topledger
topledger -o tensor -q daily-transactions
topledger -o tensor -q weekly-users
baas -c ETH -vs BTC -l 10 -s date -r

View File

@@ -411,7 +411,8 @@
"holders": ["Ethplorer"],
"th": ["Ethplorer"],
"prices": ["Ethplorer"],
"tx": ["Ethplorer"]
"tx": ["Ethplorer"],
"topledger": ["Topledger"]
},
"defi": {
"newsletter": ["Substack"],

View File

@@ -4,6 +4,7 @@
btcblockdata displays BTC single block data [Blockchain]
gwei check current eth gas fees [ETHGasStations]
whales check crypto wales transactions [WhaleAlert]
topledger get on-chain data from topledger [Topledger]
lt last trades by dex or month [BitQuery]
dvcp daily volume for crypto pair [BitQuery]
tv token volume on DEXes [BitQuery]