Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
320 changes: 135 additions & 185 deletions packages/ui/README.md
Original file line number Diff line number Diff line change
@@ -1,255 +1,205 @@
# Stellar Explain – Core API
# Stellar Explain — Frontend

The `core` package contains the Rust backend API for Stellar Explain.

It exposes HTTP endpoints that fetch data from Horizon and return structured explanations of Stellar transactions.
The Next.js frontend for [Stellar Explain](https://github.com/StellarCommons/Stellar-Explain) — a tool that explains Stellar blockchain transactions and accounts in plain English.

---

## 🚀 Getting Started

### Prerequisites

- Rust (latest stable recommended)
Install via: https://rustup.rs
- Cargo (installed with Rust)
- Access to a Horizon instance (defaults to public/testnet)
- Optional: Docker (if running via container)
## Prerequisites

Check your Rust version:
- **Node.js** v18 or later
- **npm** v9 or later
- A running instance of the [Stellar Explain backend](../core/README.md) (Rust)

Check your versions:
```bash
rustc --version
node --version
npm --version
```

---

## ⚙️ Environment Variables
## Setup

The API reads configuration from environment variables:
```bash
# 1. Clone the repo (if you haven't already)
git clone https://github.com/StellarCommons/Stellar-Explain.git
cd Stellar-Explain/packages/ui

| Variable | Description | Default |
| ----------------- | --------------------------- | ----------------------- |
| `STELLAR_NETWORK` | `public` or `testnet` | `public` |
| `HORIZON_URL` | Custom Horizon URL override | Network default |
| `CORS_ORIGIN` | Allowed CORS origin | `http://localhost:3000` |
| `RUST_LOG` | Logging level | `info` |
# 2. Install dependencies
npm install

Example:
# 3. Configure environment variables
cp .env.local.example .env.local
# Edit .env.local and set API_URL to your running backend

```bash
export STELLAR_NETWORK=testnet
export RUST_LOG=info
# 4. Start the dev server
npm run dev
```

---
The app will be available at `http://localhost:3000`.

## 🛠 Build & Run
---

From the repository root:
## Environment Variables

```bash
cargo build --release -p core
```
| Variable | Required | Description | Example |
|----------|----------|-------------|---------|
| `API_URL` | ✅ Yes | URL of the Stellar Explain Rust backend | `http://localhost:4000` |
| `NEXT_PUBLIC_STELLAR_NETWORK` | No | Network label shown in the UI | `testnet` |

Run locally:
> **Note:** `API_URL` is read server-side only and is never exposed to the browser. All requests to the backend are proxied through Next.js API routes.

```bash
cargo run -p core
```
---

The server starts on:
## Scripts

```
http://localhost:4000
```
| Command | Description |
|---------|-------------|
| `npm run dev` | Start development server with Turbopack |
| `npm run build` | Build for production |
| `npm run start` | Start production server |
| `npm run lint` | Run ESLint + TypeScript type check |
| `npm run format` | Format all source files with Prettier |
| `npm run format:check` | Check formatting without writing changes |

---

## 🧪 Running Tests
## Project Structure

Run all tests:

```bash
cargo test -p core
```

Run with logs:

```bash
RUST_LOG=debug cargo test -p core
packages/ui/
├── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── page.tsx # Landing page (/)
│ │ ├── app/page.tsx # Search page (/app)
│ │ ├── tx/[hash]/page.tsx # Transaction result (/tx/:hash)
│ │ ├── account/[address]/ # Account result (/account/:address)
│ │ └── api/ # Next.js proxy routes (server-side only)
│ │ ├── tx/[hash]/route.ts
│ │ ├── account/[address]/route.ts
│ │ └── health/route.ts
│ │
│ ├── components/ # Shared UI components
│ │ ├── AppShell.tsx # Shared page wrapper with header and context
│ │ ├── AppShellContext.ts # React context + useAppShell() hook
│ │ ├── TransactionResult.tsx # Transaction explanation display
│ │ ├── AccountResult.tsx # Account explanation display
│ │ ├── ErrorDisplay.tsx # Typed API error display
│ │ ├── Toast.tsx # Copy-to-clipboard notification
│ │ ├── Card.tsx # Base card wrapper
│ │ ├── Pill.tsx # Status pill (success/fail/warning)
│ │ ├── Label.tsx # Section label
│ │ ├── AddressChip.tsx # Truncated address with copy
│ │ ├── TabSwitcher.tsx # Transaction / Account tab toggle
│ │ ├── SearchBar.tsx # Search input + submit
│ │ ├── landing/ # Landing page sections
│ │ ├── history/ # Search history panel (UI #21)
│ │ └── addressbook/ # Address book panel (UI #22)
│ │
│ ├── hooks/ # Custom React hooks
│ │ ├── useSearchHistory.ts # localStorage search history
│ │ ├── useAddressBook.ts # localStorage address book
│ │ ├── useCopyToClipboard.ts # Clipboard with reset state
│ │ └── usePersonalMode.ts # Personalised explanation mode
│ │
│ ├── lib/ # Utilities and API client
│ │ ├── api.ts # Typed fetch functions
│ │ ├── errors.ts # Error type guards and helpers
│ │ └── utils.ts # Formatting helpers
│ │
│ └── types/
│ └── index.ts # TypeScript types mirroring backend shapes
├── .env.local.example # Environment variable template
├── next.config.ts # Next.js configuration (standalone output)
├── tailwind.config.ts # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration (strict mode)
├── .prettierrc # Prettier formatting rules
├── eslint.config.mjs # ESLint configuration
└── Dockerfile # Multi-stage Docker build
```

---

## 🐳 Running with Docker
## Connecting to the Backend

From the project root:
### Option A — Run the Rust backend locally

```bash
docker-compose up --build
# From the monorepo root
cargo run -p core
# Backend starts at http://localhost:4000
```

The API will be available at:

Then set in your `.env.local`:
```
http://localhost:4000
API_URL=http://localhost:4000
```

Health check:
### Option B — Docker Compose (recommended)

```bash
curl http://localhost:4000/health
```

---

## 📡 API Endpoints

---

### GET `/health`

Returns service status and Horizon connectivity.

#### Example
Runs both frontend and backend together:

```bash
curl http://localhost:4000/health
```

#### Response

```json
{
"status": "ok",
"network": "testnet",
"horizon_reachable": true,
"version": "0.1.0"
}
# From the monorepo root
docker compose up --build
```

If Horizon is unreachable:
- Frontend: `http://localhost:3000`
- Backend: `http://localhost:4000`

- HTTP 503
- `"status": "degraded"`
The frontend container connects to the backend via Docker's internal network using `http://backend:4000` — this is configured automatically in `docker-compose.yml`.

---

### GET `/tx/:hash`
## Architecture

Returns a structured explanation of a Stellar transaction.
### Proxy Pattern

#### Example
The frontend never calls the Stellar Explain backend directly from the browser. All API calls go through Next.js server-side proxy routes:

```bash
curl http://localhost:4000/tx/3b7e9b6f...
```

#### Example Response

```json
{
"hash": "3b7e9b6f...",
"successful": true,
"fee_charged": 100,
"memo": "Payment for services",
"operations": [
{
"type": "payment",
"from": "GABC...",
"to": "GXYZ...",
"asset": "XLM",
"amount": "100.0000000"
}
],
"explanation": "This transaction transferred 100 XLM from GABC... to GXYZ..."
}
Browser → /api/tx/[hash] (Next.js server) → http://localhost:4000/tx/:hash (Rust backend)
```

---

### GET `/account/:address` (Planned)
This means `API_URL` is only ever read on the server — it is never bundled into the client JavaScript or exposed in network requests. This is intentional.

Will return paginated transactions for an account.
### AppShell + Context

#### Example
All app pages (`/app`, `/tx/:hash`, `/account/:address`) are wrapped in `AppShell`, which provides shared state via React context. Child pages access shared state via `useAppShell()`:

```bash
curl http://localhost:4000/account/GABC...
```

#### Expected Response Structure
```tsx
function TxPageInner() {
const { addEntry } = useAppShell();
...
}

```json
{
"account": "GABC...",
"transactions": [],
"next_cursor": "...",
"prev_cursor": "..."
export default function TxPage() {
return (
<AppShell>
<TxPageInner />
</AppShell>
);
}
```

---

## 🔐 Rate Limiting

The API applies global per-IP rate limiting:

- 60 requests per minute
- Returns HTTP 429 if exceeded
- Includes `Retry-After` header

---

## 🏗 Project Structure
### Route Structure

```
packages/core
├── src/
│ ├── routes/
│ ├── services/
│ ├── models/
│ ├── config/
│ └── main.rs
├── Cargo.toml
└── README.md
```
| URL | Description |
|-----|-------------|
| `/` | Landing page |
| `/app` | Search page — enter a hash or address |
| `/tx/:hash` | Transaction explanation result |
| `/account/:address` | Account explanation result |

---

## 🤝 Contributing
## Contributing

We welcome contributions.

### How to Contribute

1. Check open issues
2. Create a new branch from `main`
3. Follow conventional commit format
4. Ensure:
- Code compiles
- Tests pass
- Formatting is clean (`cargo fmt`)
- Linting passes (`cargo clippy`)
5. Open a Pull Request referencing the issue number

Example:

```
feat(health): add horizon connectivity check
```

### Running Checks Before PR
See the root [CONTRIBUTING.md](../../CONTRIBUTING.md) for contribution guidelines.

Before opening a PR, make sure:
```bash
cargo fmt
cargo clippy
cargo test
```

---

## 📜 License

MIT (or project-specific license)
npm run lint # No TypeScript or ESLint errors
npm run format:check # Code is formatted correctly
```
Loading
Loading