Skip to content

Commit c985c74

Browse files
authored
Merge pull request #147 from ton-studio/feature/stonfi_discovery
Add Airflow DAG for ston.fi routers discovery
2 parents 41fa611 + 951e3c0 commit c985c74

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import ast
2+
import json
3+
import logging
4+
import requests
5+
from datetime import datetime
6+
7+
from airflow.decorators import dag
8+
from airflow.operators.python import PythonOperator
9+
from airflow.providers.telegram.hooks.telegram import TelegramHook
10+
11+
12+
STONFI_ROUTER_V1 = "EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt"
13+
STONFI_PARSER_V2_URL = "https://raw.githubusercontent.com/ton-studio/ton-etl/refs/heads/main/parser/parsers/message/stonfi_swap_v2.py"
14+
STONFI_POOLS_API_URL = "https://api.ston.fi/v1/pools?dex_v2=true"
15+
16+
17+
@dag(
18+
schedule_interval="30 8 * * *",
19+
start_date=datetime(2025, 1, 25),
20+
catchup=False,
21+
concurrency=1,
22+
max_active_runs=1,
23+
tags=["ton", "dex"],
24+
)
25+
def stonfi_new_routers_checker():
26+
"""
27+
DAG to monitor Ston.fi routers. This DAG checks for new routers by comparing
28+
router addresses from the parser code and the API. If new routers are found,
29+
an alert is sent via Telegram.
30+
"""
31+
32+
def get_data(url):
33+
"""
34+
Fetches data from the given URL using an HTTP GET request.
35+
36+
Args:
37+
url (str): The URL to fetch data from.
38+
39+
Returns:
40+
str: The response text from the URL.
41+
42+
Raises:
43+
Exception: If the HTTP status code is not 200 or another error occurs.
44+
"""
45+
try:
46+
response = requests.get(url, timeout=10)
47+
if response.status_code != 200:
48+
raise Exception(f"Response status code = {response.status_code}")
49+
return response.text
50+
except Exception as e:
51+
telegram_hook = TelegramHook(telegram_conn_id="telegram_watchdog_conn")
52+
logging.error(f"Unable to get data from {url}: {e}")
53+
telegram_hook.send_message({"text": f"📛 Unable to get data from {url}: {e}"})
54+
raise e
55+
56+
def extract_routers_from_code(code: str):
57+
"""
58+
Extracts router addresses from the provided Python code.
59+
60+
Args:
61+
code (str): The Python code containing router definitions.
62+
63+
Returns:
64+
set: A set of router addresses extracted from the code.
65+
"""
66+
tree = ast.parse(code)
67+
for node in tree.body:
68+
if isinstance(node, ast.Assign):
69+
for target in node.targets:
70+
if (
71+
isinstance(target, ast.Name)
72+
and target.id == "ROUTERS"
73+
and isinstance(node.value, ast.Call)
74+
and isinstance(node.value.func, ast.Name)
75+
and node.value.func.id == "set"
76+
):
77+
map_call = node.value.args[0]
78+
if (
79+
isinstance(map_call, ast.Call)
80+
and isinstance(map_call.func, ast.Name)
81+
and map_call.func.id == "map"
82+
):
83+
list_node = map_call.args[1]
84+
if isinstance(list_node, ast.List):
85+
return {ast.literal_eval(el) for el in list_node.elts}
86+
87+
def check_routers():
88+
"""
89+
Compares the router addresses from the parser code and the Ston.fi API.
90+
If new router addresses are found, sends an alert via Telegram.
91+
"""
92+
code = get_data(STONFI_PARSER_V2_URL)
93+
routers_from_parser = extract_routers_from_code(code) | {STONFI_ROUTER_V1}
94+
95+
stonfi_api_data = get_data(STONFI_POOLS_API_URL)
96+
pool_list = json.loads(stonfi_api_data).get("pool_list")
97+
routers_from_api = {pool.get("router_address") for pool in pool_list}
98+
99+
new_routers = routers_from_api - routers_from_parser
100+
101+
if new_routers:
102+
telegram_hook = TelegramHook(telegram_conn_id="telegram_watchdog_conn")
103+
logging.info(f"New Ston.fi routers have been found: {', '.join(new_routers)}")
104+
telegram_hook.send_message({"text": f"⚠️ New Ston.fi routers have been found: {', '.join(new_routers)}"})
105+
106+
PythonOperator(
107+
task_id="check_routers",
108+
python_callable=check_routers,
109+
)
110+
111+
112+
stonfi_checker_dag = stonfi_new_routers_checker()

0 commit comments

Comments
 (0)