A Next.js frontend for Stellar Stream Hive: employers create real-time payment streams to employees on Stellar, and recipients watch their balance accrue by the second and withdraw whenever they want. Built as part of an open-source Stellar Development Foundation grant application.
| Repo | Purpose |
|---|---|
stellar-stream-hive-contract |
Soroban smart contracts (Stream, Vault, Registry) |
stellar-stream-hive-api |
Indexer/registry API used for stats and stream listings |
stellar-stream-hive-frontend (this repo) |
Next.js web app |
The core of the product is watching a recipient's withdrawable balance tick upward in real
time. hooks/useLiveBalance.ts mirrors the on-chain streaming formula client-side using BigInt
math (never floating point, always floored — the UI must never show more than the contract
would actually let you withdraw):
elapsed = min(current_ledger, end_ledger) - start_ledger
total_duration = end_ledger - start_ledger
streamed = floor(total_amount * elapsed / total_duration)
withdrawable = streamed - withdrawn
It estimates the current ledger between chain reads using Stellar's ~5s average ledger close time, ticks once per second for a smooth counter, and re-syncs with the Soroban RPC endpoint every 30 seconds to correct for drift. Actual withdrawals always re-fetch the real on-chain amount before submitting — the ticking estimate is a UI affordance, never the source of truth for a transaction.
- Next.js 14 (App Router) + TypeScript
- Tailwind CSS (dark mode by default)
@stellar/stellar-sdkfor building/simulating/submitting Soroban transactions@stellar/freighter-apifor wallet connection and signing- Recharts for data visualization
- date-fns for date formatting
npm install
cp .env.example .env.local # then fill in values, see below
npm run devOpen http://localhost:3000.
See .env.example for the full, documented list. In short:
NEXT_PUBLIC_STREAM_CONTRACT_ID,NEXT_PUBLIC_VAULT_CONTRACT_ID,NEXT_PUBLIC_REGISTRY_CONTRACT_ID— Soroban contract IDs..env.exampleships with the testnet deployment from 2026-10-12 (recorded in the contracts repo'sDEPLOYMENTS.md), socp .env.example .env.localgives a working testnet setup. A redeploy creates new ids; update these fromscripts/deploy.sh's output. On any other network leave them blank and contract-calling features show a clear "not yet configured" state rather than failing silently.NEXT_PUBLIC_STELLAR_NETWORK,NEXT_PUBLIC_STELLAR_NETWORK_PASSPHRASE,NEXT_PUBLIC_SOROBAN_RPC_URL,NEXT_PUBLIC_HORIZON_URL— network configuration, defaulted to Stellar testnet.NEXT_PUBLIC_API_BASE_URL— base URL for the indexer API (stellar-stream-hive-api), used for platform-wide stats and listing streams by sender/recipient. If unset, the app shows a graceful empty/loading state rather than fabricating numbers.
app/
page.tsx Landing page (hero, no-wallet live demo, platform stats)
streams/new/page.tsx Create a stream
streams/[id]/page.tsx Stream detail (timeline, live balance, withdraw/cancel)
sending/page.tsx Streams the connected wallet is sending
receiving/page.tsx Streams the connected wallet is receiving
components/
wallet/ Freighter connect button, network indicator
stream/ LiveBalance, StreamCard, timeline, withdraw/cancel, etc.
create/ Create-stream form fields and live preview
landing/ Landing-page-only sections (demo, stats)
layout/ Nav bar, footer
ui/ Generic primitives (spinner, empty/error states, copy button)
hooks/
useWallet.ts Freighter connection state
useLiveBalance.ts The per-second ticking balance hook
useStream.ts / useStreams.ts Data loading for one stream / lists of streams
lib/
contracts.ts Soroban contract call helpers (create/withdraw/cancel)
freighter.ts Freighter API wrapper with normalized error handling
stellar.ts Network config, RPC client, address validation
streaming.ts Client-side streamed-amount math (mirrors the contract)
format.ts BigInt <-> decimal string formatting, address shortening
api.ts Indexer API client
types/index.ts Shared domain types
npm run buildThis runs a full TypeScript + Next.js production build and must succeed with no type errors.
See CONTRIBUTING.md.
MIT
npm run lint # ESLint (next/core-web-vitals)
npm run typecheck # tsc --noEmit, strict mode
npm test # Vitest unit tests (watch mode: npm run test:watch)
npm run build # production buildAll four run in CI on every push and pull request (.github/workflows/ci.yml).
The unit tests target the logic where a mistake would cost someone money or show them a wrong balance, and run without a browser, wallet or network:
| Module | What is covered |
|---|---|
lib/streaming.ts |
The accrual formula against the contract's own test vectors: flooring, multiply-before-divide, cliff, monotonicity, BigInt precision beyond Number.MAX_SAFE_INTEGER, ledger/time estimation |
lib/format.ts |
Decimal parsing and formatting round-trips, truncation instead of rounding, address and explorer URL helpers |
lib/streamMapping.ts |
Mapping the indexer API's and the contract's responses into the app's Stream type |
lib/streamList.ts |
Status filters and CSV export (including spreadsheet-formula neutralisation) |
The frontend talks to two backends, and lib/streamMapping.ts is the one place
that knows their wire formats:
- Indexer API (
NEXT_PUBLIC_API_BASE_URL):GET /stats,GET /ready,GET /streams/:id,GET /senders/:address,GET /recipients/:address. List endpoints are paginated (200 per page); the client followsX-Total-Countuntil every page is loaded. Amounts arrive as decimal strings and are kept as strings until they are converted toBigIntfor display. - Stream contract (
NEXT_PUBLIC_STREAM_CONTRACT_ID):create_stream,withdraw(recipient, stream_id, amount),cancel_stream(sender, stream_id),withdrawable_amountandget_stream. Argument order matters here — Soroban passes arguments positionally — and is pinned inlib/contracts.ts.
/sending and /receiving can be filtered by status (all / live / completed /
cancelled) and exported to CSV. The export contains exactly the streams currently
shown, with amounts as human-readable decimals.