Skip to content

Commit cb51223

Browse files
committed
Add front end
1 parent 7ec6451 commit cb51223

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+5848
-1
lines changed

.DS_Store

0 Bytes
Binary file not shown.

app.py

+52
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import numpy as np
2020
import psutil
2121
import signal
22+
from quart_cors import cors
2223

2324
matplotlib.use('Agg')
2425

@@ -52,6 +53,8 @@ async def fetch_chain_data():
5253
return chain_data
5354

5455
app = Quart(__name__)
56+
# For development - restrict to specific origins in production
57+
app = cors(app, allow_origin="*")
5558

5659

5760
@app.route('/', methods=['GET', 'POST'])
@@ -83,6 +86,55 @@ async def index():
8386
return await render_template('index.html', networks=sorted_networks)
8487

8588

89+
@app.route('/api/networks', methods=['GET'])
90+
async def api_networks():
91+
global CHAIN_DATA
92+
if not CHAIN_DATA:
93+
CHAIN_DATA = await fetch_chain_data()
94+
sorted_networks = sorted(CHAIN_DATA.keys())
95+
return {"networks": sorted_networks}
96+
97+
98+
@app.route('/api/data', methods=['POST'])
99+
async def api_data():
100+
global CHAIN_DATA
101+
if not CHAIN_DATA:
102+
CHAIN_DATA = await fetch_chain_data()
103+
104+
# Get form data from request body as JSON
105+
json_data = await request.get_json()
106+
address = json_data['address'].lower()
107+
request_type = json_data['type']
108+
selected_network = json_data['network']
109+
network_url = CHAIN_DATA.get(selected_network, {}).get(
110+
'url', "https://eth.hypersync.xyz")
111+
112+
try:
113+
directory, total_blocks, total_items, elapsed_time, start_block, is_cached = await fetch_data(address, selected_network, network_url, request_type)
114+
if total_items == 0:
115+
return {"error": f"No {request_type}s found for {address} on the {selected_network} network."}, 404
116+
117+
# Get the plot and stats
118+
img, stats = create_plot(
119+
directory, request_type, total_blocks, total_items, elapsed_time, start_block, is_cached)
120+
121+
# Return JSON response with base64 encoded image
122+
return {
123+
"plot_url": img,
124+
"stats": stats,
125+
"request_type": request_type,
126+
"address": address,
127+
"network": selected_network,
128+
"total_blocks": total_blocks,
129+
"total_items": total_items,
130+
"elapsed_time": elapsed_time
131+
}
132+
except Exception as e:
133+
error_message = str(e)
134+
print(f"Error: {error_message}")
135+
return {"error": f"An unexpected error occurred. Error: {error_message}"}, 500
136+
137+
86138
def create_query(address, start_block, request_type):
87139
if request_type == "event":
88140
query = hypersync.Query(

front-end/.eslintrc.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This configuration only applies to the package manager root.
2+
/** @type {import("eslint").Linter.Config} */
3+
module.exports = {
4+
ignorePatterns: ["apps/**", "packages/**"],
5+
extends: ["@workspace/eslint-config/library.js"],
6+
parser: "@typescript-eslint/parser",
7+
parserOptions: {
8+
project: true,
9+
},
10+
}

front-end/.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# Dependencies
4+
node_modules
5+
.pnp
6+
.pnp.js
7+
8+
# Local env files
9+
.env
10+
.env.local
11+
.env.development.local
12+
.env.test.local
13+
.env.production.local
14+
15+
# Testing
16+
coverage
17+
18+
# Turbo
19+
.turbo
20+
21+
# Vercel
22+
.vercel
23+
24+
# Build Outputs
25+
.next/
26+
out/
27+
build
28+
dist
29+
30+
31+
# Debug
32+
npm-debug.log*
33+
34+
# Misc
35+
.DS_Store
36+
*.pem

front-end/.npmrc

Whitespace-only changes.

front-end/README.md

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# ChainDensity Frontend
2+
3+
This is the Next.js frontend for the ChainDensity application, which visualizes event and transaction density for Ethereum and other EVM-compatible blockchains.
4+
5+
## Setup
6+
7+
### Prerequisites
8+
9+
- Node.js 18+ and npm/pnpm
10+
- Python 3.10+ (for the backend)
11+
12+
### Backend Setup
13+
14+
1. Set up the Python backend first:
15+
16+
```bash
17+
# From the root directory
18+
pip install -r requirements.txt
19+
python app.py
20+
```
21+
22+
The backend will run on http://localhost:5001.
23+
24+
### Frontend Setup
25+
26+
1. Install dependencies:
27+
28+
```bash
29+
# From the front-end directory
30+
pnpm install
31+
```
32+
33+
2. Run the development server:
34+
35+
```bash
36+
# From the front-end directory
37+
pnpm dev
38+
```
39+
40+
3. Open your browser and navigate to http://localhost:3000
41+
42+
## Architecture
43+
44+
This project uses a decoupled architecture:
45+
46+
- **Backend**: Python Quart application that handles data retrieval and processing using Hypersync
47+
- **Frontend**: Next.js application that provides a modern UI for interacting with the backend
48+
49+
## API Endpoints
50+
51+
The backend exposes the following API endpoints:
52+
53+
- `GET /api/networks` - Returns a list of available networks
54+
- `POST /api/data` - Accepts address, network, and request type to generate density visualization
55+
56+
## Development
57+
58+
This is a [Next.js](https://nextjs.org/) project bootstrapped with Turborepo.
59+
60+
## Acknowledgements
61+
62+
Made by envio and powered by [Hypersync](https://envio.dev).

front-end/apps/web/app/favicon.ico

25.3 KB
Binary file not shown.

front-end/apps/web/app/layout.tsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Geist, Geist_Mono } from "next/font/google"
2+
3+
import "@workspace/ui/globals.css"
4+
import { Providers } from "@/components/providers"
5+
6+
const fontSans = Geist({
7+
subsets: ["latin"],
8+
variable: "--font-sans",
9+
})
10+
11+
const fontMono = Geist_Mono({
12+
subsets: ["latin"],
13+
variable: "--font-mono",
14+
})
15+
16+
export default function RootLayout({
17+
children,
18+
}: Readonly<{
19+
children: React.ReactNode
20+
}>) {
21+
return (
22+
<html lang="en" suppressHydrationWarning>
23+
<body
24+
className={`${fontSans.variable} ${fontMono.variable} font-sans antialiased `}
25+
>
26+
<Providers>{children}</Providers>
27+
</body>
28+
</html>
29+
)
30+
}

0 commit comments

Comments
 (0)