33import requests
44from cachetools import cached , TTLCache
55import logging
6+ from typing import Tuple , Optional
67
78from langchain_core .tools import BaseTool , tool
89from langgraph .graph .graph import RunnableConfig
@@ -41,15 +42,20 @@ def get_top_token_holders(
4142 chain = chain .lower ()
4243
4344 try :
44- holders = get_top_token_holders_from_coingecko (address , chain )
45+ holders , error = get_top_token_holders_from_coingecko (address , chain )
46+ if error :
47+ return error
48+
4549 return f"""Top holders of { address } on { chain } : { holders } ."""
4650 except Exception as e :
4751 logging .error (f"Error in get_top_token_holders with input { token_id } : { e } " )
4852 return f"ERROR: Failed to get top holders for { token_id } : { e } "
4953
5054
5155@cached (cache = TTLCache (maxsize = 10_000 , ttl = 60 * 10 ))
52- def get_top_token_holders_from_coingecko (token_address : str , chain : str ) -> List :
56+ def get_top_token_holders_from_coingecko (
57+ token_address : str , chain : str
58+ ) -> Tuple [List , Optional [str ]]:
5359 """Get the top holders of a token on the given chain."""
5460 headers = {
5561 "accept" : "application/json" ,
@@ -67,7 +73,7 @@ def get_top_token_holders_from_coingecko(token_address: str, chain: str) -> List
6773 )
6874 if response .status_code == 404 :
6975 logging .warning (f"Token top holders not found: { token_address } on { chain } " )
70- return "Top holders for this token are not available."
76+ return [], "Top holders for this token are not available."
7177 if response .status_code != 200 :
7278 raise Exception (
7379 f"Failed to fetch token holders: { response .status_code } { response .text } "
@@ -87,7 +93,7 @@ def get_top_token_holders_from_coingecko(token_address: str, chain: str) -> List
8793 }
8894 formatted_holders .append (holder_info )
8995
90- return formatted_holders
96+ return formatted_holders , None
9197
9298
9399@tool
@@ -120,9 +126,11 @@ def evaluate_token_risk(
120126 chain , address = token_id .split (":" , 1 )
121127 chain = chain .lower ()
122128
123- token_info = get_token_info_from_coingecko (address , chain )
124- attributes = token_info ["attributes" ]
129+ token_info , error = get_token_info_from_coingecko (address , chain )
130+ if error :
131+ return error
125132
133+ attributes = token_info ["attributes" ]
126134 risk_analysis = {
127135 "trust_score" : {
128136 "overall_score" : attributes .get ("gt_score" , 0 ),
@@ -176,7 +184,9 @@ def evaluate_token_risk(
176184
177185
178186@cached (cache = TTLCache (maxsize = 1000 , ttl = 60 * 10 ))
179- def get_token_info_from_coingecko (token_address : str , chain : str ) -> TokenMetadata :
187+ def get_token_info_from_coingecko (
188+ token_address : str , chain : str
189+ ) -> Tuple [TokenMetadata , Optional [str ]]:
180190 """Get token info from CoinGecko's token info endpoint for the chain."""
181191 headers = {
182192 "accept" : "application/json" ,
@@ -191,13 +201,16 @@ def get_token_info_from_coingecko(token_address: str, chain: str) -> TokenMetada
191201 response = requests .get (
192202 TOKEN_INFO_URL % (coingecko_chain , token_address ), headers = headers
193203 )
204+ if response .status_code == 404 :
205+ logging .warning (f"Token info not found: { token_address } on { chain } " )
206+ return None , "Token metadata not available."
194207 if response .status_code != 200 :
195208 raise Exception (
196209 f"Failed to fetch token info: { response .status_code } { response .text } "
197210 )
198211
199212 data = response .json ()
200- return data ["data" ]
213+ return data ["data" ], None
201214
202215
203216@cached (cache = TTLCache (maxsize = 100 , ttl = 60 * 10 ))
0 commit comments