gauravlochab commited on
Commit
e2243e5
1 Parent(s): 5fbddca

feat: Add tab for transaction visualizations in the dashboard

Browse files
Files changed (2) hide show
  1. app.py +4 -1
  2. 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&timestamp={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&timestamp={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()