Spaces:
Runtime error
Runtime error
gauravlochab
commited on
Commit
·
e2243e5
1
Parent(s):
5fbddca
feat: Add tab for transaction visualizations in the dashboard
Browse files- app.py +4 -1
- app_trans_new.py +267 -0
app.py
CHANGED
@@ -5,6 +5,7 @@ import plotly.express as px
|
|
5 |
from datetime import datetime, timedelta
|
6 |
import json
|
7 |
from web3 import Web3
|
|
|
8 |
|
9 |
OPTIMISM_RPC_URL = 'https://opt-mainnet.g.alchemy.com/v2/U5gnXPYxeyH43MJ9tP8ONBQHEDRav7H0'
|
10 |
|
@@ -309,7 +310,9 @@ def create_visualizations():
|
|
309 |
def dashboard():
|
310 |
with gr.Blocks() as demo:
|
311 |
gr.Markdown("# Valory Transactions Dashboard")
|
312 |
-
|
|
|
|
|
313 |
# Fetch and display visualizations
|
314 |
with gr.Tab("Swaps"):
|
315 |
fig_swaps_chain, fig_bridges_chain, fig_agents_daily, fig_agents_with_transactions_daily = create_visualizations()
|
|
|
5 |
from datetime import datetime, timedelta
|
6 |
import json
|
7 |
from web3 import Web3
|
8 |
+
from app_trans_new import create_transcation_visualizations
|
9 |
|
10 |
OPTIMISM_RPC_URL = 'https://opt-mainnet.g.alchemy.com/v2/U5gnXPYxeyH43MJ9tP8ONBQHEDRav7H0'
|
11 |
|
|
|
310 |
def dashboard():
|
311 |
with gr.Blocks() as demo:
|
312 |
gr.Markdown("# Valory Transactions Dashboard")
|
313 |
+
with gr.Tab("Transactions"):
|
314 |
+
fig_tx_chain = create_transcation_visualizations()
|
315 |
+
gr.Plot(fig_tx_chain)
|
316 |
# Fetch and display visualizations
|
317 |
with gr.Tab("Swaps"):
|
318 |
fig_swaps_chain, fig_bridges_chain, fig_agents_daily, fig_agents_with_transactions_daily = create_visualizations()
|
app_trans_new.py
ADDED
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
import pandas as pd
|
3 |
+
import gradio as gr
|
4 |
+
import plotly.express as px
|
5 |
+
from datetime import datetime, timedelta
|
6 |
+
import json
|
7 |
+
from web3 import Web3
|
8 |
+
import time
|
9 |
+
# RPC URLs
|
10 |
+
OPTIMISM_RPC_URL = 'https://opt-mainnet.g.alchemy.com/v2/U5gnXPYxeyH43MJ9tP8ONBQHEDRav7H0'
|
11 |
+
BASE_RPC_URL = 'https://base-mainnet.g.alchemy.com/v2/U5gnXPYxeyH43MJ9tP8ONBQHEDRav7H0'
|
12 |
+
ETH_RPC_URL = 'https://eth-mainnet.g.alchemy.com/v2/U5gnXPYxeyH43MJ9tP8ONBQHEDRav7H0'
|
13 |
+
|
14 |
+
# Initialize Web3 instances
|
15 |
+
print("Initializing Web3 instances...")
|
16 |
+
web3_optimism = Web3(Web3.HTTPProvider(OPTIMISM_RPC_URL))
|
17 |
+
web3_base = Web3(Web3.HTTPProvider(BASE_RPC_URL))
|
18 |
+
web3_eth = Web3(Web3.HTTPProvider(ETH_RPC_URL))
|
19 |
+
|
20 |
+
# Contract addresses for service registries
|
21 |
+
contract_address_optimism = '0x3d77596beb0f130a4415df3D2D8232B3d3D31e44'
|
22 |
+
contract_address_base = '0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE'
|
23 |
+
contract_address_eth = '0x48b6af7B12C71f09e2fC8aF4855De4Ff54e775cA'
|
24 |
+
|
25 |
+
# Load the ABI from a local JSON file
|
26 |
+
with open('service_registry_abi.json', 'r') as abi_file:
|
27 |
+
contract_abi = json.load(abi_file)
|
28 |
+
|
29 |
+
# Create the contract instances
|
30 |
+
service_registry_optimism = web3_optimism.eth.contract(address=contract_address_optimism, abi=contract_abi)
|
31 |
+
service_registry_base = web3_base.eth.contract(address=contract_address_base, abi=contract_abi)
|
32 |
+
service_registry_eth = web3_eth.eth.contract(address=contract_address_eth, abi=contract_abi)
|
33 |
+
print("Service registry contracts loaded.")
|
34 |
+
|
35 |
+
# Check if connection is successful
|
36 |
+
if not web3_optimism.is_connected():
|
37 |
+
raise Exception("Failed to connect to the Optimism network.")
|
38 |
+
if not web3_base.is_connected():
|
39 |
+
raise Exception("Failed to connect to the Base network.")
|
40 |
+
if not web3_eth.is_connected():
|
41 |
+
raise Exception("Failed to connect to the ETH network.")
|
42 |
+
print("Successfully connected to Ethereum, Optimism, and Base networks.")
|
43 |
+
|
44 |
+
|
45 |
+
def fetch_service_safes(web3, registry_contract):
|
46 |
+
print("\nFetching service safes...")
|
47 |
+
total_services = registry_contract.functions.totalSupply().call()
|
48 |
+
print(f"Total services: {total_services}")
|
49 |
+
service_safes = set()
|
50 |
+
|
51 |
+
for service_id in range(1, total_services + 1):
|
52 |
+
print(f"Processing service ID: {service_id}")
|
53 |
+
service = registry_contract.functions.getService(service_id).call()
|
54 |
+
agent_ids = service[-1] # Assuming the last element is the list of agent IDs
|
55 |
+
print(f"Agent IDs: {agent_ids}")
|
56 |
+
|
57 |
+
if 25 in agent_ids:
|
58 |
+
agent_address = registry_contract.functions.getAgentInstances(service_id).call()
|
59 |
+
service_safe = service[1]
|
60 |
+
print(f"Found agent_address: {agent_address}")
|
61 |
+
print(f"Found service safe: {service_safe}")
|
62 |
+
service_safes.add(service_safe)
|
63 |
+
|
64 |
+
print(f"Total service safes found: {len(service_safes)}")
|
65 |
+
return service_safes
|
66 |
+
|
67 |
+
# Fetch service safes for each network
|
68 |
+
service_safes_optimism = fetch_service_safes(web3_optimism, service_registry_optimism)
|
69 |
+
service_safes_base = fetch_service_safes(web3_base, service_registry_base)
|
70 |
+
service_safes_eth = fetch_service_safes(web3_eth, service_registry_eth)
|
71 |
+
service_safes_eth = {safe for safe in service_safes_eth if safe.lower() != '0x0000000000000000000000000000000000000000'}
|
72 |
+
|
73 |
+
def get_block_range_for_date(chain_id, date_str, api_key, base_url):
|
74 |
+
"""Get the block range for a specific date."""
|
75 |
+
target_date = datetime.strptime(date_str, "%Y-%m-%d")
|
76 |
+
start_of_day = datetime.combine(target_date, datetime.min.time())
|
77 |
+
end_of_day = datetime.combine(target_date, datetime.max.time())
|
78 |
+
|
79 |
+
start_timestamp = int(start_of_day.timestamp())
|
80 |
+
end_timestamp = int(end_of_day.timestamp())
|
81 |
+
|
82 |
+
# Get start block
|
83 |
+
|
84 |
+
start_response = requests.get(
|
85 |
+
f"{base_url}?module=block&action=getblocknobytime×tamp={start_timestamp}&closest=before&apikey={api_key}"
|
86 |
+
)
|
87 |
+
if start_response.status_code == 200:
|
88 |
+
start_data = start_response.json()
|
89 |
+
start_block = start_data.get('result')
|
90 |
+
else:
|
91 |
+
print(f"Error fetching start block for {date_str} on chain {chain_id}")
|
92 |
+
return None, None
|
93 |
+
|
94 |
+
if start_block is None:
|
95 |
+
print(f"No start block found for chain {chain_id} on {date_str}")
|
96 |
+
return None, None
|
97 |
+
print(f"Start block for chain {chain_id} on {date_str}: {start_block}")
|
98 |
+
|
99 |
+
# Get end block
|
100 |
+
time.sleep(1)
|
101 |
+
end_response = requests.get(
|
102 |
+
f"{base_url}?module=block&action=getblocknobytime×tamp={end_timestamp}&closest=before&apikey={api_key}"
|
103 |
+
)
|
104 |
+
if end_response.status_code == 200:
|
105 |
+
end_data = end_response.json()
|
106 |
+
end_block = end_data.get('result')
|
107 |
+
else:
|
108 |
+
print(f"Error fetching end block for {date_str} on chain {chain_id}")
|
109 |
+
return None, None
|
110 |
+
|
111 |
+
if end_block is None:
|
112 |
+
print(f"No end block found for chain {chain_id} on {date_str}")
|
113 |
+
return None, None
|
114 |
+
print(f"End block for chain {chain_id} on {date_str}: {end_block}")
|
115 |
+
|
116 |
+
return start_block, end_block
|
117 |
+
|
118 |
+
def get_transactions(api_keys, wallet_address, chain_name, start_block, end_block):
|
119 |
+
"""Retrieve transactions for the given wallet address, chain, and block range using the Etherscan or similar API."""
|
120 |
+
base_url = {
|
121 |
+
'optimism': "https://api-optimistic.etherscan.io/api",
|
122 |
+
'base': "https://api.basescan.org/api",
|
123 |
+
'ethereum': "https://api.etherscan.io/api"
|
124 |
+
}.get(chain_name)
|
125 |
+
|
126 |
+
if not base_url:
|
127 |
+
print(f"Invalid chain name: {chain_name}")
|
128 |
+
return []
|
129 |
+
|
130 |
+
params = {
|
131 |
+
'module': 'account',
|
132 |
+
'action': 'txlist',
|
133 |
+
'address': wallet_address,
|
134 |
+
'startblock': start_block,
|
135 |
+
'endblock': end_block,
|
136 |
+
'sort': 'asc',
|
137 |
+
'apikey': api_keys.get(chain_name)
|
138 |
+
}
|
139 |
+
|
140 |
+
response = requests.get(base_url, params=params)
|
141 |
+
data = response.json()
|
142 |
+
|
143 |
+
time.sleep(1)
|
144 |
+
|
145 |
+
if data['status'] != '1':
|
146 |
+
print(f"Error: {data['message']}")
|
147 |
+
return []
|
148 |
+
|
149 |
+
valid_transactions = [tx for tx in data['result'] if tx['isError'] == '0']
|
150 |
+
|
151 |
+
return valid_transactions
|
152 |
+
|
153 |
+
def date_range(start_date, end_date):
|
154 |
+
"""Generates a range of dates from start_date to end_date inclusive."""
|
155 |
+
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
|
156 |
+
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
|
157 |
+
delta = timedelta(days=1)
|
158 |
+
current_dt = start_dt
|
159 |
+
while current_dt <= end_dt:
|
160 |
+
yield current_dt.strftime("%Y-%m-%d")
|
161 |
+
current_dt += delta
|
162 |
+
|
163 |
+
def fetch_transactions():
|
164 |
+
# User inputs
|
165 |
+
api_keys = {
|
166 |
+
'optimism': 'XQ72JA5XZ51QC7TG1W295AAIF4KTV92K1K',
|
167 |
+
'base': '4BFQMVW1QUKEPVDA4VW711CF4462682CY8',
|
168 |
+
'ethereum': '3GRYJGX55W3QWCEKGREF4H53AFHCAIVVR7'
|
169 |
+
}
|
170 |
+
|
171 |
+
base_urls = {
|
172 |
+
10: "https://api-optimistic.etherscan.io/api",
|
173 |
+
8453: "https://api.basescan.org/api",
|
174 |
+
1: "https://api.etherscan.io/api"
|
175 |
+
}
|
176 |
+
|
177 |
+
start_date = '2024-09-19' # Starting date
|
178 |
+
current_date = datetime.now().strftime('%Y-%m-%d') # Till present date
|
179 |
+
|
180 |
+
chains = {
|
181 |
+
10: ('optimism', service_safes_optimism), # Optimism chain ID and service safes
|
182 |
+
8453: ('base', service_safes_base), # Base chain ID and service safes
|
183 |
+
1: ('ethereum', service_safes_eth) # Ethereum mainnet chain ID and service safes
|
184 |
+
}
|
185 |
+
|
186 |
+
all_transactions = [] # List to hold all transactions
|
187 |
+
|
188 |
+
for chain_id, (chain_name, service_safes) in chains.items():
|
189 |
+
base_url = base_urls[chain_id]
|
190 |
+
api_key = api_keys[chain_name]
|
191 |
+
|
192 |
+
for safe_address in service_safes:
|
193 |
+
print(f"\nProcessing {chain_name.capitalize()} for safe address {safe_address}...")
|
194 |
+
for single_date in date_range(start_date, current_date):
|
195 |
+
start_block, end_block = get_block_range_for_date(chain_id, single_date, api_key, base_url)
|
196 |
+
if start_block is None or end_block is None:
|
197 |
+
print(f"Skipping date {single_date} for chain {chain_name} due to missing block data.")
|
198 |
+
continue
|
199 |
+
|
200 |
+
print(f"Start Block: {start_block}, End Block: {end_block} for date {single_date}")
|
201 |
+
|
202 |
+
transactions = get_transactions(api_keys, safe_address, chain_name, start_block, end_block)
|
203 |
+
|
204 |
+
if transactions:
|
205 |
+
print(f"Found {len(transactions)} transactions on {single_date} for {chain_name.capitalize()} safe address {safe_address}:")
|
206 |
+
for tx in transactions:
|
207 |
+
tx_time = datetime.fromtimestamp(int(tx['timeStamp']))
|
208 |
+
all_transactions.append({
|
209 |
+
'chain': chain_name,
|
210 |
+
'safe_address': safe_address,
|
211 |
+
'date': single_date,
|
212 |
+
'transaction_hash': tx['hash'],
|
213 |
+
'timestamp': tx_time,
|
214 |
+
'from': tx['from'],
|
215 |
+
'to': tx['to'],
|
216 |
+
'value_eth': int(tx['value']) / 1e18 # Convert value to ETH
|
217 |
+
})
|
218 |
+
else:
|
219 |
+
print(f"No transactions found for safe address {safe_address} on {single_date} on {chain_name.capitalize()}.")
|
220 |
+
|
221 |
+
# Convert the collected transactions into a DataFrame
|
222 |
+
df_transactions_new = pd.DataFrame(all_transactions)
|
223 |
+
return df_transactions_new
|
224 |
+
|
225 |
+
def create_transcation_visualizations():
|
226 |
+
df_transactions_new = fetch_transactions()
|
227 |
+
df_transactions_new.to_csv('daily_transactions_new.csv', index=False)
|
228 |
+
|
229 |
+
df_transactions_new['timestamp'] = pd.to_datetime(df_transactions_new['timestamp'])
|
230 |
+
|
231 |
+
# Group by date and chain, count transactions
|
232 |
+
daily_counts = df_transactions_new.groupby([df_transactions_new['timestamp'].dt.date, 'chain']).size().unstack(fill_value=0)
|
233 |
+
|
234 |
+
# Set up the plot
|
235 |
+
fig_tx_chain = px.bar(
|
236 |
+
daily_counts,
|
237 |
+
barmode="stack",
|
238 |
+
title="Chain Daily Activity: Transactions"
|
239 |
+
)
|
240 |
+
fig_tx_chain.update_layout(
|
241 |
+
xaxis_title="Date",
|
242 |
+
yaxis_title="Daily Transaction Nr",
|
243 |
+
legend_title="Transaction Chain",
|
244 |
+
xaxis_tickformat="%Y-%m-%d",
|
245 |
+
height=700,
|
246 |
+
)
|
247 |
+
|
248 |
+
return fig_tx_chain
|
249 |
+
|
250 |
+
|
251 |
+
# Gradio interface
|
252 |
+
def dashboard():
|
253 |
+
with gr.Blocks() as demo:
|
254 |
+
gr.Markdown("# Valory Transactions Dashboard")
|
255 |
+
|
256 |
+
# Fetch and display visualizations
|
257 |
+
with gr.Tab("Transactions"):
|
258 |
+
fig_tx_chain = create_transcation_visualizations()
|
259 |
+
gr.Plot(fig_tx_chain)
|
260 |
+
|
261 |
+
# Add more tabs as needed...
|
262 |
+
|
263 |
+
return demo
|
264 |
+
|
265 |
+
# Launch the dashboard
|
266 |
+
if __name__ == "__main__":
|
267 |
+
dashboard().launch()
|