# Project export: MediLedger

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: A privacy-preserving drug availability search powered by EigenDA and ZK-PTLS
- Devpost: https://devpost.com/software/mediledger
- GitHub: https://github.com/shayaansultan/treehacks-final
- Demo: https://app.flutterflow.io/project/exchange-crypto-app-template-u-i-kit-jlnp6p
- Video: https://www.youtube.com/embed/N1Zv7cgQSQ0?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Web3 Prize: Best AI Agents ($8k Cash + 8k EIGEN [1st] & $7k Cash + 7k EIGEN [2nd]))
- Team: 1 GitHub contributor(s) — Shayaan Sultan (1 commits)

## Devpost submission (written by the team)

### Inspiration

Hospital stockouts—the unavailability of essential medications and medical supplies—present a major crisis in healthcare. Patients suffer delays in critical treatments, emergency rooms lack life-saving drugs, and hospitals are forced to ration care. Despite medical advancements, hospitals still rely on outdated, fragmented inventory systems, leading to supply shortages that compromise patient outcomes. The biggest challenge? Hospitals don’t want to share their entire inventory with competitors. Right now, checking stock requires manual calls between hospitals, which is slow, inefficient, and unscalable. The result? Wasted time that directly exacerbates preventable patient harm. Our project is uniquely ambitious in the way we are applying Web3 to a deeply centralized system like healthcare, but we want to reinvent how cybersecurity works in this important industry. We care about using blockchain for social good, so a detailed problem statement highlighting the inspiration for our product is linked below.

### What it does

MediLedger is all about collaboration: providing a decentralized platform on which hospitals can more securely and confidently transmit sensitive data. Our system enables hospitals to securely check drug availability at other hospitals without exposing full inventory details on both ends. Instead of storing a simple list of drugs, each hospital stores its own inventory in a Merkle tree, where each leaf node contains a cryptographic hash of a drug name and quantity. The Merkle root (a compact summary of the entire inventory) is then submitted to EigenDA, a decentralized data availability layer. EigenDA allows hospitals to store inventory commitments on-chain without revealing individual stock details and ensures data integrity—any change in inventory changes the Merkle root, which prevents tampering. When a hospital needs a drug, the AI agent first checks the local hospital’s inventory. If the drug is out of stock or the supply is trending low, the AI agent fetches Merkle roots of nearby hospitals from EigenDA using Zero-Knowledge Proofs (zkProof) and the provided Merkle trees. The hospitals that have the requested drug generate a Merkle proof, which extracts the leaf node (hashed drug and quantity) from the Merkle tree, includes all necessary sibling hashes to allow recomputation of the Merkle root, and ensures the proof matches the stored Merkle root on EigenDA. The system then outputs a list of hospitals whose Merkle proofs have been validated, thus proving they have the drug in stock.

### How we built it

The backend server was built with Express.js, Node.js, and TypeScript. For the hashing algorithm, we used SHA256. While configuring the adapter for EigenDA, we used EigenLayer’s extensible agent framework for verifiable AI capabilities and data availability logging. The frontend was built with FlutterFlow, with an initial POC frontend built using Next.js.

### Challenges we ran into

The biggest challenge was integration—the backend deployment was not Much of what we worked on also involved repositories and tools with limited documentation, where we underwent educated debugging in a field we had little prior experience in. We initially had two ideas (this one and another more fintech focused on price manipulation) that we pivoted twice between on Friday, so defining the initial feature set for both with a blockchain focus took some time, especially with the crunch time of 36 hours. The biggest challenge was integration as well with FlutterFlow.

### Accomplishments we're proud of

For most of the team, TreeHacks 2025 is our first hackathon ever. We could have played it safe using familiar Web2 tech stacks from classes, we decided to challenge ourselves by diving headfirst into Web3 and cryptography. In a span of 36 hours, we overcame the steep learning curve of understanding and implementing Merkle proofs and trees, which are notoriously complex but incredibly powerful for verification. Despite occasional roadblocks, we were determined to not give up on our ambitious ideas (where else has blockchain been integrated with healthcare?). Our team wore multiple hats—from testing backend deployment to graphic design and marketing. In the end, we are proud of how we built a fully decentralized inventory system for hospitals to use.

### What we learned

One of our largest takeaways from this weekend is how accessible hacking can be—even to students with limited technical exposure. With the right background and a willingness to learn new tools, we became a determined team that collaborated well on an interesting idea. Blockchain can be used for security and social good, and we hope we proved that through intersecting these technologies with healthcare. For a more technical side, we learned how hash-based data structures allow for selective proof generation. Storing data off-chain in EigenDA was a major shift from Web2 thinking, and now we understand that different decentralization techniques come with trade-offs in performance.

### What's next

If we had more time, we would love to implement more features related to Web3: Right now, our proof verification happens off-chain in our backend. A great addition would be to store Merkle roots & zkProofs on Ethereum or a Layer 2 (e.g., Polygon, Arbitrum). This would allow on-chain smart contracts to verify zkProofs without a centralized backend. Currently, hospitals only react when a drug is out of stock or if supply has a diminishing trend. We would love to use our AI agent to predict drug shortages before they happen: analyzing historical hospital inventory data to forecast demand surges and sending alerts to hospitals before a stockout occurs. Currently, hospitals authenticate via API keys. Instead, we could use Web3 wallets (e.g., MetaMask) for hospital authentication, where hospitals could digitally sign zkProofs before submitting them. This would prevent unauthorized hospitals from accessing inventory data.

## README (from the GitHub repository)

# Meta-Ledger
MediLedger is all about collaboration: providing a decentralized platform on which hospitals can more securely and confidently transmit sensitive data. Our system enables hospitals to securely check drug availability at other hospitals without exposing full inventory details on both ends.

Instead of storing a simple list of drugs, each hospital stores its own inventory in a Merkle tree, where each leaf node contains a cryptographic hash of a drug name and quantity. The Merkle root (a compact summary of the entire inventory) is then submitted to EigenDA, a decentralized data availability layer. EigenDA allows hospitals to store inventory commitments on-chain without revealing individual stock details and ensures data integrity—any change in inventory changes the Merkle root, which prevents tampering.

When a hospital needs a drug, the AI agent first checks the local hospital’s inventory. If the drug is out of stock or the supply is trending low, the AI agent fetches Merkle roots of nearby hospitals from EigenDA using Zero-Knowledge Proofs (zkProof) and the provided Merkle trees. The hospitals that have the requested drug generate a Merkle proof, which extracts the leaf node (hashed drug and quantity) from the Merkle tree, includes all necessary sibling hashes to allow recomputation of the Merkle root, and ensures the proof matches the stored Merkle root on EigenDA. The system then outputs a list of hospitals whose Merkle proofs have been validated, thus proving they have the drug in stock.

Backend Dev (Express, Node, TypeScript): Shayaan and Cynthia <br>
Frontend (FlutterFlow): Taarush <br>
Biotech Technicals & Slides: Bernardo


## Detected evidence (automated analysis)

Indexed codebase: 106 recognized source files, 969 KB.
- C (language) — detected in the code
- CSS (language) — detected in the code
- Dart (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Kotlin (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Swift (language) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 149)

```
.DS_Store
.env.test
.gitignore
frontend/.gitignore
frontend/eslint.config.mjs
frontend/next.config.ts
frontend/package.json
frontend/postcss.config.mjs
frontend/README.md
frontend/src/app/globals.css
frontend/src/app/layout.tsx
frontend/src/app/page.tsx
frontend/src/components/VerificationProcess.tsx
frontend/src/lib/api.ts
frontend/src/types/index.ts
frontend/tailwind.config.ts
frontend/tsconfig.json
helix-newfinal-0q7hnh/.gitignore
helix-newfinal-0q7hnh/.metadata
helix-newfinal-0q7hnh/.vscode/ff_metadata.json
helix-newfinal-0q7hnh/.vscode/file_map.json
helix-newfinal-0q7hnh/.vscode/settings.json
helix-newfinal-0q7hnh/analysis_options.yaml
helix-newfinal-0q7hnh/android/.gitignore
helix-newfinal-0q7hnh/android/app/build.gradle
helix-newfinal-0q7hnh/android/app/proguard-rules.pro
helix-newfinal-0q7hnh/android/app/src/debug/AndroidManifest.xml
helix-newfinal-0q7hnh/android/app/src/main/AndroidManifest.xml
helix-newfinal-0q7hnh/android/app/src/main/kotlin/com/example/my_project/MainActivity.kt
helix-newfinal-0q7hnh/android/app/src/main/res/drawable/launch_background.xml
helix-newfinal-0q7hnh/android/app/src/main/res/values-night-v31/styles.xml
helix-newfinal-0q7hnh/android/app/src/main/res/values-night/styles.xml
helix-newfinal-0q7hnh/android/app/src/main/res/values-v31/styles.xml
helix-newfinal-0q7hnh/android/app/src/main/res/values/strings.xml
helix-newfinal-0q7hnh/android/app/src/main/res/values/styles.xml
helix-newfinal-0q7hnh/android/app/src/profile/AndroidManifest.xml
helix-newfinal-0q7hnh/android/build.gradle
helix-newfinal-0q7hnh/android/gradle.properties
helix-newfinal-0q7hnh/android/gradle/wrapper/gradle-wrapper.properties
helix-newfinal-0q7hnh/android/settings.gradle
helix-newfinal-0q7hnh/ios/.gitignore
helix-newfinal-0q7hnh/ios/Flutter/AppFrameworkInfo.plist
helix-newfinal-0q7hnh/ios/Flutter/Debug.xcconfig
helix-newfinal-0q7hnh/ios/Flutter/Release.xcconfig
helix-newfinal-0q7hnh/ios/ImageNotification/Info.plist
helix-newfinal-0q7hnh/ios/ImageNotification/NotificationService.swift
helix-newfinal-0q7hnh/ios/Podfile
helix-newfinal-0q7hnh/ios/Runner.xcodeproj/project.pbxproj
helix-newfinal-0q7hnh/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
helix-newfinal-0q7hnh/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
helix-newfinal-0q7hnh/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
helix-newfinal-0q7hnh/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
helix-newfinal-0q7hnh/ios/Runner.xcworkspace/contents.xcworkspacedata
helix-newfinal-0q7hnh/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
helix-newfinal-0q7hnh/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
helix-newfinal-0q7hnh/ios/Runner/AppDelegate.swift
helix-newfinal-0q7hnh/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
helix-newfinal-0q7hnh/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
helix-newfinal-0q7hnh/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
helix-newfinal-0q7hnh/ios/Runner/Base.lproj/LaunchScreen.storyboard
helix-newfinal-0q7hnh/ios/Runner/Base.lproj/Main.storyboard
helix-newfinal-0q7hnh/ios/Runner/Info.plist
helix-newfinal-0q7hnh/ios/Runner/PrivacyInfo.xcprivacy
helix-newfinal-0q7hnh/ios/Runner/Runner-Bridging-Header.h
helix-newfinal-0q7hnh/ios/Runner/Runner.entitlements
helix-newfinal-0q7hnh/lib/components/app_bar_model.dart
helix-newfinal-0q7hnh/lib/components/app_bar_widget.dart
helix-newfinal-0q7hnh/lib/components/metamask_model.dart
helix-newfinal-0q7hnh/lib/components/metamask_widget.dart
helix-newfinal-0q7hnh/lib/components/navbar_model.dart
helix-newfinal-0q7hnh/lib/components/navbar_widget.dart
helix-newfinal-0q7hnh/lib/custom_code/actions/api_service.dart
helix-newfinal-0q7hnh/lib/custom_code/actions/index.dart
helix-newfinal-0q7hnh/lib/custom_code/widgets/index.dart
helix-newfinal-0q7hnh/lib/custom_code/widgets/verification_process.dart
helix-newfinal-0q7hnh/lib/flutter_flow/custom_functions.dart
helix-newfinal-0q7hnh/lib/flutter_flow/custom_icons.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_animations.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_charts.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_choice_chips.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_icon_button.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_model.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_theme.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_util.dart
helix-newfinal-0q7hnh/lib/flutter_flow/flutter_flow_widgets.dart
helix-newfinal-0q7hnh/lib/flutter_flow/form_field_controller.dart
helix-newfinal-0q7hnh/lib/flutter_flow/lat_lng.dart
helix-newfinal-0q7hnh/lib/flutter_flow/nav/nav.dart
helix-newfinal-0q7hnh/lib/flutter_flow/nav/serialization_util.dart
helix-newfinal-0q7hnh/lib/flutter_flow/place.dart
helix-newfinal-0q7hnh/lib/flutter_flow/random_data_util.dart
helix-newfinal-0q7hnh/lib/flutter_flow/uploaded_file.dart
helix-newfinal-0q7hnh/lib/index.dart
helix-newfinal-0q7hnh/lib/loading/loading_model.dart
helix-newfinal-0q7hnh/lib/loading/loading_widget.dart
helix-newfinal-0q7hnh/lib/main.dart
helix-newfinal-0q7hnh/lib/pages/available_locations/available_locations_model.dart
helix-newfinal-0q7hnh/lib/pages/available_locations/available_locations_widget.dart
helix-newfinal-0q7hnh/lib/pages/confirm_flourouracil/confirm_flourouracil_model.dart
helix-newfinal-0q7hnh/lib/pages/confirm_flourouracil/confirm_flourouracil_widget.dart
helix-newfinal-0q7hnh/lib/pages/fluorouracil_stock/fluorouracil_stock_model.dart
helix-newfinal-0q7hnh/lib/pages/fluorouracil_stock/fluorouracil_stock_widget.dart
helix-newfinal-0q7hnh/lib/pages/gabapentin_stock/gabapentin_stock_model.dart
helix-newfinal-0q7hnh/lib/pages/gabapentin_stock/gabapentin_stock_widget.dart
helix-newfinal-0q7hnh/lib/pages/get_started02/get_started02_model.dart
helix-newfinal-0q7hnh/lib/pages/get_started02/get_started02_widget.dart
helix-newfinal-0q7hnh/lib/pages/get_started03/get_started03_model.dart
helix-newfinal-0q7hnh/lib/pages/get_started03/get_started03_widget.dart
helix-newfinal-0q7hnh/lib/pages/homepage/homepage_model.dart
helix-newfinal-0q7hnh/lib/pages/homepage/homepage_widget.dart
helix-newfinal-0q7hnh/lib/pages/inventory/inventory_model.dart
helix-newfinal-0q7hnh/lib/pages/inventory/inventory_widget.dart
helix-newfinal-0q7hnh/lib/pages/login/login_model.dart
helix-newfinal-0q7hnh/lib/pages/login/login_widget.dart
helix-newfinal-0q7hnh/lib/pages/pending/pending_model.dart
helix-newfinal-0q7hnh/lib/pages/pending/pending_widget.dart
helix-newfinal-0q7hnh/lib/pages/register/register_model.dart
helix-newfinal-0q7hnh/lib/pages/register/register_widget.dart
helix-newfinal-0q7hnh/lib/pages/request_flouroracil/request_flouroracil_model.dart
helix-newfinal-0q7hnh/lib/pages/request_flouroracil/request_flouroracil_widget.dart
[29 more files omitted for size]
```

### Dependencies

- frontend/package.json: @eslint/eslintrc@^3, @headlessui/react@^2.2.0, @heroicons/react@^2.2.0, @types/node@^20, @types/react@^19, @types/react-dom@^19, axios@^1.7.9, clsx@^2.1.1, eslint@^9, eslint-config-next@15.1.7, framer-motion@^12.4.3, next@15.1.7, postcss@^8, react@^19.0.0, react-dom@^19.0.0, tailwindcss@^3.4.1, typescript@^5
- package.json: @layr-labs/agentkit@^0.1.1, @types/cors@^2.8.17, @types/crypto-js@^4.2.2, @types/dotenv@^6.1.1, @types/express@^4.17.21, @types/jest@^29.5.14, @types/node@^22.13.4, cors@^2.8.5, crypto-js@^4.2.0, dotenv@^16.4.7, eigenda-sdk@^0.1.0, express@^4.21.2, merkletreejs@^0.4.1, nodemon@^3.1.9, ts-node@^10.9.2, typescript@^5.7.3

### Recent commits (newest first)

- Update README.md
- Update README.md
- bruh
- update
- add basic frontend
- it works?
- stuff
- bruh
- something
- maybe working
- cursor cooked;
- works?
- setup
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "treehacks-backend-2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "nodemon src/index.ts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@layr-labs/agentkit": "^0.1.1",
    "cors": "^2.8.5",
    "crypto-js": "^4.2.0",
    "dotenv": "^16.4.7",
    "eigenda-sdk": "^0.1.0",
    "express": "^4.21.2",
    "merkletreejs": "^0.4.1"
  },
  "devDependencies": {
    "@types/cors": "^2.8.17",
    "@types/crypto-js": "^4.2.2",
    "@types/dotenv": "^6.1.1",
    "@types/express": "^4.17.21",
    "@types/jest": "^29.5.14",
    "@types/node": "^22.13.4",
    "nodemon": "^3.1.9",
    "ts-node": "^10.9.2",
    "typescript": "^5.7.3"
  }
}

```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@headlessui/react": "^2.2.0",
    "@heroicons/react": "^2.2.0",
    "axios": "^1.7.9",
    "clsx": "^2.1.1",
    "framer-motion": "^12.4.3",
    "next": "15.1.7",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.1.7",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### src/index.ts

```typescript
import dotenv from 'dotenv';
dotenv.config();

import express, { Express } from 'express';
import inventoryRoutes from './routes/inventory';

const app: Express = express();
const port = process.env.PORT || 3000;

app.use(express.json());

// Use inventory routes
app.use('/api/inventory', inventoryRoutes);

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
}); 
```

### frontend/src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Hospital Drug Availability",
  description: "Search for drug availability across our network of hospitals",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

```

### frontend/src/types/index.ts

```typescript
export interface Hospital {
  hospitalId: string;
  name: string;
  location: string;
}

export interface DrugAvailability {
  success: boolean;
  available: boolean;
}

export interface HospitalSearchResults {
  success: boolean;
  results: Hospital[];
}

export type VerificationStep = {
  id: number;
  title: string;
  description: string;
  status: 'pending' | 'processing' | 'completed' | 'error';
};

export type ProcessState = {
  currentStep: number;
  steps: VerificationStep[];
  isComplete: boolean;
  error?: string;
}; 
```

### frontend/src/app/page.tsx

```typescript
'use client';

import { useState } from 'react';
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { checkStanfordDrugAvailability, searchOtherHospitals } from '@/lib/api';
import { Hospital, ProcessState, VerificationStep } from '@/types';
import VerificationProcess from '@/components/VerificationProcess';

const initialSteps: VerificationStep[] = [
  {
    id: 1,
    title: 'Initializing EigenDA Connection',
    description: 'Establishing secure connection to the EigenDA network...',
    status: 'pending'
  },
  {
    id: 2,
    title: 'Generating Zero-Knowledge Proof',
    description: 'Creating a ZK proof for secure drug availability verification...',
    status: 'pending'
  },
  {
    id: 3,
    title: 'Verifying Hospital Merkle Tree',
    description: 'Validating hospital inventory data integrity...',
    status: 'pending'
  },
  {
    id: 4,
    title: 'Processing Trust-Less Query',
    description: 'Executing privacy-preserving inventory search...',
    status: 'pending'
  }
];

export default function Home() {
  const [drugName, setDrugName] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [stanfordAvailability, setStanfordAvailability] = useState<boolean | null>(null);
  const [otherHospitals, setOtherHospitals] = useState<Hospital[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [processState, setProcessState] = useState<ProcessState>({
    currentStep: -1,
    steps: initialSteps,
    isComplete: false
  });

  const updateStep = (stepIndex: number, status: VerificationStep['status']) => {
    setProcessState(prev => ({
      ...prev,
      steps: prev.steps.map((step, idx) => 
        idx === stepIndex ? { ...step, status } : step
      )
    }));
  };

  const simulateVerificationProcess = async () => {
    // Step 1: EigenDA Connection
    setProcessState(prev => ({ ...prev, currentStep: 0 }));
    updateStep(0, 'processing');
    await new Promise(resolve => setTimeout(resolve, 2000));
    updateStep(0, 'completed');

    // Step 2: ZK Proof Generation
    setProcessState(prev => ({ ...prev, currentStep: 1 }));
    updateStep(1, 'processing');
    await new Promise(resolve => setTimeout(resolve, 3000));
    updateStep(1, 'completed');

    // Step 3: Merkle Tree Verification
    setProcessState(prev => ({ ...prev, currentStep: 2 }));
    updateStep(2, 'processing');
    await new Promise(resolve => setTimeout(resolve, 2500));
    updateStep(2, 'completed');

    // Step 4: Query Processing
    setProcessState(prev => ({ ...prev, currentStep: 3 }));
    updateStep(3, 'processing');
    await new Promise(resolve => setTimeout(resolve, 2000));
    updateStep(3, 'completed');
  };

  const handleSearch = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!drugName.trim()) return;

    setIsLoading(true);
    setError(null);
    setStanfordAvailability(null);
    setOtherHospitals([]);
    
    try {
      // Start verification process
      await simulateVerificationProcess();

      // Actual API calls
      const [stanfordResult, otherHospitalsResult] = await Promise.all([
        checkStanfordDrugAvailability(drugName),
        searchOtherHospitals(drugName)
      ]);

      setStanfordAvailability(stanfordResult.available);
      setOtherHospitals(otherHospitalsResult.results);
      setProcessState(prev => ({ ...prev, isComplete: true }));
    } catch (err) {
      setError('Failed to fetch drug availability. Please try again.');
      console.error(err);
      // Mark all remaining steps as error
      setProcessState(prev => ({
        ...prev,
        steps: prev.steps.map((step, idx) => ({
          ...step,
          status: idx <= prev.currentStep ? 'error' : 'pending'
        }))
      }));
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <main className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
        <div className="text-center">
          <h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
            Secure Hospital Drug Verification
          </h1>
          <p className="mt-3 text-lg text-gray-500">
            Privacy-preserving drug availability search powered by EigenDA and ZK-PTLS
          </p>
        </div>

        <form onSubmit={handleSearch} className="mt-10 max-w-xl mx-auto">
          <div className="flex shadow-sm rounded-md">
            <input
              type="text"
              value={drugName}
              onChange={(e) => setDrugName(e.target.value)}
              placeholder="Enter drug name..."
              className="block w-full rounded-l-md border-0 py-3 pl-4 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
              disabled={isLoading}
            />
            <button
              type="submit"
              disabled={isLoading}
              className="relative -ml-px inline-flex items-center gap-x-1.5 rounded-r-md px-4 py-3 text-sm font-semibold text-white bg-blue-600 hover:bg-blue-500 disabled:bg-blue-400"
            >
              <MagnifyingGlassIcon className="h-5 w-5" aria-hidden="true" />
              {isLoading ? 'Verifying...' : 'Search'}
            </button>
          </div>
        </form>

        {processState.currentStep >= 0 && (
          <VerificationProcess
            steps={processState.steps}
            currentStep={processState.currentStep}
          />
        )}

        {error && (
          <div className="mt-6 text-center text-red-600">
            {error}
          </div>
        )}

        {stanfordAvailability !== null && !error && processState.isComplete && (
          <div className="mt-10">
            <h2 className="text-2xl font-semibold text-gray-900 mb-4">Verified Results</h2>
            
            <div className="bg-white shadow rounded-lg p-6 mb-6 transform
[truncated — 1549 more characters]
```

### frontend/next.config.ts

```typescript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

```

### frontend/tailwind.config.ts

```typescript
import type { Config } from "tailwindcss";

export default {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [],
} satisfies Config;

```

### helix-newfinal-0q7hnh/analysis_options.yaml

```yaml
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
  # The lint rules applied to this project can be customized in the
  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
  # included above or to enable additional rules. A list of all available lints
  # and their documentation is published at https://dart.dev/lints.
  #
  # Instead of disabling a lint rule for the entire project in the
  # section below, it can also be suppressed for a single line of code
  # or a specific dart file by using the `// ignore: name_of_lint` and
  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
  # producing the lint.
  rules:
    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
    unnecessary_string_escapes: false

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
```

### src/test.ts

```typescript
import { EigenDAClient } from 'eigenda-sdk';
import dotenv from 'dotenv';

// Define the actual API response type
interface ApiUploadResponse {
  job_id: string;
  message: string;
  request_id: string;
  status: string;
}

// Load environment variables from .env file
dotenv.config();

async function main() {
  // Initialize the client
  const client = new EigenDAClient({
    apiUrl: process.env.EIGENDA_API_URL, // Optional: defaults to mainnet
    rpcUrl: process.env.EIGENDA_BASE_RPC_URL, // Optional: defaults to mainnet
    privateKey: process.env.EIGENDA_PRIVATE_KEY as string,
  });

  try {
    let identifier;
    const existingIdentifiers = await client.getIdentifiers();
    if (existingIdentifiers.length > 0) {
      identifier = existingIdentifiers[0];
    } else {
      identifier = await client.createIdentifier();
    }

    const balance = await client.getBalance(identifier);
    console.log('Balance:', balance);

    if (balance < 0.001) {
      console.log('Balance is low. Topping up credits...');
      const topupResult = await client.topupCredits(identifier, 0.001); // 0.001 ETH
      console.log('Topup result:', topupResult);
      
      // Verify new balance
      const newBalance = await client.getBalance(identifier);
      console.log('New balance:', newBalance, 'ETH');
    }

    // Upload data with identifier
    // console.log('Uploading data...');
    // const content = JSON.stringify({
    //   message: 'Hello EigenDA!',
    //   timestamp: Date.now()
    // });
    // const uploadResult = (await client.upload(content, identifier)) as unknown as ApiUploadResponse;
    // console.log('Upload result:', uploadResult);

    // // Wait for the blob to be confirmed
    // console.log('Waiting for blob to be confirmed...');
    // let status;
    // let attempts = 0;
    // const maxAttempts = 60; // Increased max attempts
    // const waitTime = 30000; // Increased to 30 seconds

    // while (attempts < maxAttempts) {
    //   status = await client.getStatus(uploadResult.job_id);
    //   console.log('Current status:', status);
      
    //   if (status.status === 'CONFIRMED' || status.status === 'FINALIZED') {
    //     console.log('Data confirmed! Proceeding to retrieval...');
    //     break;
    //   }
      
    //   if (status.status === 'FAILED') {
    //     throw new Error('Data upload failed');
    //   }
      
    //   attempts++;
    //   console.log(`Waiting ${waitTime/1000} seconds before next check (attempt ${attempts}/${maxAttempts})...`);
    //   await new Promise(resolve => setTimeout(resolve, waitTime));
    // }

    // if (status?.status !== 'CONFIRMED' && status?.status !== 'FINALIZED') {
    //   throw new Error('Data upload did not confirm in time');
    // }

    // // Give a little extra time after confirmation before retrieval
    // console.log('Waiting an additional 30 seconds before retrieval...');
    // await new Promise(resolve => setTimeout(resolve, 30000));

    // // Retrieve data with different options
    // console.log('Retrieving by job ID...');
    // console.log('uploadResult.job_id:', uploadResult.job_id);
    const request_id = 'b6a40004608bf0e37cd31cddedbc30d64dab33d9afc11cb1a578f5128ae3d3aa-313733393639383638323130383237313836362f302f33332f312f33332fe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
    const dataByJobId = await client.retrieve({
      requestId: request_id,
      waitForCompletion: true
    });
    console.log('Retrieved data by job ID:', dataByJobId);

  } catch (error) {
    console.error('Error:', error);
  }
}

main().catch(console.error);
```

[87 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]