HypeNode is an autonomous crypto index platform: an AI agent researches market signals (hype, sentiment, fund flows, macro), assembles a basket of tokens, backtests it, and deploys it on-chain as a Smart Strategy Index (SSI) that anyone can hold or subscribe to. Built on Next.js 14 + TypeScript + Tailwind on the frontend, with a Python LangGraph + MCP backend powering the "Research-to-Execution" agent loop.
Two products live in one app:
- Indexer (
/dashboard,/builder,/portfolio,/agent, …) — manage your own on-chain indices end-to-end. Sign in with email / Google / Twitter / wallet via Privy → research → build basket → simulate → deploy to SSI Registry on Sepolia → monitor. - Publisher (
/publisher/radar,/publisher/proposals, …) — agent drafts hype-driven indices, you review & publish them on-chain to earn fees.
Public surface (no sign-in required): /discover marketplace, /docs,
/privacy, /status (live endpoint probes).
For the full product roadmap and current status see
docs/product-roadmap.md. For a deeper architecture
overview see docs/dokumentasi-aplikasi.md.
| Layer | Tech |
|---|---|
| Frontend | Next.js 14.2.32 App Router · React 18 · TypeScript · Tailwind 3 |
| Wallet | Privy (email / Google / Twitter / wallet) · wagmi · viem |
| Auth | SIWE (Sign-In With Ethereum) · iron-session cookies |
| API surface | Next.js route handlers (/app/api/* — 56 endpoints) |
| Data | SoSoValue OpenAPI v1 (rate-limited proxy + per-path TTL cache) |
| LLM agent | Python 3.11 · FastAPI · LangGraph · MCP · pluggable Anthropic / OpenAI |
| Storage | Supabase Postgres (saved runs, threads, billing) · SQLite (agent-service/data/hypenode.db for risk config + decision log) |
| Execution | SSI Registry (Sepolia) via wagmi useWriteContract · SoDEX REST v1 |
| Risk hedge | Auto-route to USSI on threshold breach (planned for Wave 3) |
app/
layout.tsx Root layout — Providers + AppFooter (every page)
page.tsx Landing — pick Indexer or Publisher
providers.tsx PrivyProvider → QueryClient → WagmiProvider stack
onboarding/role/ First-time role selection
(indexer)/ 16 indexer pages share IndexerTopBar layout
dashboard, research, analyses, builder, agent, portfolio, risk,
history, chat, backtest (page + BacktestClient + RunHistoryModal),
settings, tokens, tokens/[id], fundraising, fundraising/[id], stocks
publisher/ 6 publisher pages share PublisherTopBar layout
radar, proposals, proposals/[id], published, earnings, config
(public)/ Unauthenticated info surface
docs, privacy, status (live endpoint probes every 30s)
discover, discover/[id] Public marketplace (no sign-in)
share/backtest/[code] Public share link for a saved backtest run
api/ 56 route handlers
auth/{nonce,verify,me,logout,role} SIWE sign-in flow
sosovalue/[...path] Catch-all proxy (browser → server)
terminal/{sentiment,fund-flow,news,sectors}
ssi/{list,wrap,snapshot/[ticker],constituents/[ticker],klines/[ticker]}
currencies/[id]/klines · currencies-list
etfs/[symbol]/{history,summary}
agent/{state,reasoning,history,step,pause,halt,reset,tools,
propose-basket,run-backtest,risk-config}
backtest/{run, runs, runs/[id], share} Saved runs + share links
builder/drafts (+ [id]) Builder draft persistence
chat/threads (+ [id]/messages) Chat thread storage
portfolio + portfolio/snapshots Wallet positions + snapshots
proposals (+ [id]) · publisher · risk · settings · sodex/submit
cron/portfolio-snapshot Periodic snapshot job
asset-logos · stock-logos · billing/{usage,topup}
components/
auth/ SignInButton (Privy login + SIWE), WalletBadge
(with Export wallet for embedded), WalletBalance,
WalletMismatchBanner, CreatorIdentityCard
ui/ Card, Btn, Tag, Spark, LineChart, Meter,
Toggle, HypeGauge, ProjectLogo, StockLogo,
SectionLabel, LogoSplash, Metric, Mono, Label
nav/ IndexerTopBar, PublisherTopBar, AppFooter,
RoleSwitcher
dashboard/ SmartMoneyWidget, StablecoinFlowWidget,
MacroCalendar, SsiCompositeLogo
builder/, portfolio/, agent/, chat/, publisher/, live/, history/, research/,
billing/ Inline + modal top-up flows
lib/
tokens.ts Design tokens (matches hifi-kit colors exactly)
text.ts cleanText / safeSlice — strips lone UTF-16
surrogates so emoji-tailed news titles don't
trip SSR-vs-CSR hydration
auth/{session.ts, wagmi.ts, useSessionGuard.ts, role.ts}
hooks/{useAutoRefetch.ts, useTabActivity.ts}
api/
sosovalue.ts Shared rate-limiter / cache / dedup transport
sosovalue/ Per-endpoint typed wrappers:
tokens, treasuries, analyses, news-trending, news-search,
fundraising, macro, etf-snapshot, crypto-stocks
ssi.ts SSI Registry contract reads
sodex/{client,typedData,serverSigner} EIP-712 signed exchange actions
portfolio.ts, portfolio-snapshot.ts
agent.ts, backtest.ts Agent service & backtest clients
supabase/{server.ts, auth.ts, types.ts} Postgres persistence client
stock-logos.ts, project-slugs.ts Curated logo mappings
middleware.ts SIWE auth guard for protected routes
supabase/migrations/ SQL migrations (bt_runs, chat_threads, etc.)
agent-service/ Python FastAPI + LangGraph + MCP
src/
main.py HTTP surface for Next.js (port 8001)
chat_agent.py Multi-provider (Anthropic / OpenAI) tool-use loop
graph.py 10-node state machine: signal → … → exec
with emergency_exit branch (USSI hedge)
mcp_server.py Standalone MCP server — 17 read-only tools, installable via hypenode-mcp
state.py Pydantic models
tools/ terminal · ssi · sodex · risk · backtest ·
real_backtest · basket · macro · treasuries ·
rootdata
store.py SQLite-backed persistence
docs/
product-roadmap.md Current 3-wave roadmap (MVP → Publisher → Production)
dokumentasi-aplikasi.md Architecture & UX reference (Indonesian)
indexer-vs-publisher.md Two-product breakdown
sosovalue-api.md SoSoValue endpoint reference
Every page except the landing (/), the public surface (/discover, /docs,
/privacy, /status), and /api/* routes is gated by SIWE through
middleware.ts:
- User clicks "Get access" —
SignInButtonopens the Privy modal with email · Google · Twitter · wallet options - Privy authenticates the user:
- Email/social → auto-creates an embedded EOA (managed by Privy MPC)
- External wallet (MetaMask / Rabby / WC) → bridges through the
@privy-io/wagmiconnector
- The embedded wallet is set as the wagmi active connector so the next
useSignMessagedispatches to the right address (this avoids the "logged in with Gmail but signed with MetaMask" bug) POST /api/auth/nonce→ server generates SIWE nonce- Wallet signs the EIP-4361 message; client
POST /api/auth/verifywith the signature - Server verifies via
siwe, seals{address, chainId, role}into an iron-session cookie - Subsequent requests pass the auth check; mismatched wallet shows
WalletMismatchBanner
Sign out via POST /api/auth/logout + Privy useLogout() (drops the
iron-session cookie, Privy session, and wagmi connector in one call). Role
selection at /onboarding/role distinguishes Indexer-only users from
Publishers.
Embedded-wallet users can self-custody by clicking Export wallet in the WalletBadge dropdown — Privy reveals the private key after passkey/PIN verification, after which it can be imported into MetaMask for use on external dApps like sodex.dev.
SESSION_PASSWORD (32+ chars high-entropy) is required in production —
without it the cookie seal is forgeable.
# 1. install
npm install
# 2. dev — frontend (synthetic data without API keys)
npm run dev
# Multiple ports may be in use on shared dev machines — Next will pick the
# first free port from 3000 upward and print the actual URL as
# "Local: http://localhost:<port>". Use that URL, not a hard-coded one.
# 3. agent service (optional — lights up live reasoning + chat)
cd agent-service
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Pick your LLM provider + key:
# LLM_PROVIDER=anthropic → ANTHROPIC_API_KEY=...
# LLM_PROVIDER=openai → OPENAI_API_KEY=... (recommended model: gpt-4o)
# Also set SOSOVALUE_API_KEY for live market data.
python -m src.main # http://localhost:8001The frontend renders fine without the agent service. Most data clients fall
back to deterministic synthetic data when SOSOVALUE_API_KEY is missing — see
the audit notes in docs/product-roadmap.md for
known gaps where synthetic data masks real outages (planned cleanup in
Wave 3).
With the agent service running:
- Agent Console (
/agent) reasoning log polls every 5 s - Chat composer (
/chat) talks to Claude or GPT-4o depending onLLM_PROVIDER; the sameTOOL_SCHEMASis converted to each provider's tool-call format internally so the UX is identical across backends - Decision log persists to SQLite (
agent-service/data/hypenode.db) - Risk config persists across restarts
- Saved backtest runs, chat threads, and share links live in Supabase
npm run build && npm startThe build produces ~90 routes (30 pages · 56 API handlers · landing · 404 +
chunked layout shells). npm run typecheck runs tsc --noEmit for a quick
type sweep. npm run lint runs next lint.
ESLint is currently set to ignoreDuringBuilds: true in
next.config.mjs — the @typescript-eslint plugin was
pruned during the Privy install, which makes any eslint-disable-next-line @typescript-eslint/... comment fail the build. Lint manually before commit:
npm run lint (or re-add the plugin in Sosovalue/package.json to restore
build-time linting).
See .env.example for the canonical list. Highlights:
| Var | Default | Purpose |
|---|---|---|
AGENT_SERVICE_URL |
http://localhost:8001 |
Frontend → Python agent |
SESSION_PASSWORD |
empty (required in prod) | iron-session cookie seal |
NEXT_PUBLIC_PRIVY_APP_ID |
empty → login UI disabled | Privy app id (get one at dashboard.privy.io) |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
empty (legacy — Privy bundles WC internally) | RainbowKit / standalone WC pairing |
SOSOVALUE_API_KEY |
empty → synthetic | SoSoValue OpenAPI v1 |
SOSOVALUE_API_BASE |
https://openapi.sosovalue.com/openapi/v1 |
API base URL |
SOSOVALUE_MIN_GAP_MS |
700 (HF tier; bump to 65000 on Demo tier) |
Min ms between outbound calls |
SOSOVALUE_BUCKET_CAPACITY |
20 |
Token bucket burst capacity |
SOSOVALUE_CACHE_TTL_MS |
60000 (bump to 600000 for slower dashboards) |
Default per-path cache TTL |
SOSOVALUE_QUOTA_BACKOFF_MS |
1800000 (30 min) |
Backoff after monthly quota error |
SSI_CHAIN_ID |
11155111 (Sepolia) |
SSI Registry chain |
SSI_RPC_URL |
empty | RPC endpoint |
SSI_REGISTRY_ADDRESS |
empty | Deployed registry address |
NEXT_PUBLIC_SSI_CHAIN_ID / _ADDRESS |
mirror of SSI_* | Browser-visible (PublishActions) |
SSI_PRIVATE_KEY |
empty | Hot key for autonomous publishes only |
SODEX_ENV |
testnet |
mainnet (286623) or testnet (138565) |
SODEX_SPOT_BASE / SODEX_PERPS_BASE |
testnet endpoints | SoDEX gateway URLs |
SODEX_PRIVATE_KEY / SODEX_API_KEY_NAME |
empty | Master-wallet or delegated API key |
LLM_PROVIDER |
anthropic |
anthropic or openai — chat backend |
ANTHROPIC_API_KEY / ANTHROPIC_MODEL |
empty / claude-sonnet-4-5 |
Used when LLM_PROVIDER=anthropic |
OPENAI_API_KEY / OPENAI_MODEL |
empty / gpt-4o |
Used when LLM_PROVIDER=openai |
NEXT_PUBLIC_SUPABASE_URL |
empty | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
empty | Supabase anon key (browser-safe) |
SUPABASE_SERVICE_ROLE_KEY |
empty | Server-only Supabase role for writes |
LANGGRAPH_CHECKPOINT_DIR |
./.checkpoints |
LangGraph state store |
Auth = single header x-soso-api-key: <YOUR_KEY>. Get a key by following the
setup guide.
The shared transport (lib/api/sosovalue.ts) wraps every
endpoint with a singleton rate limiter, in-flight dedup, persistent cache
(globalThis-scoped to survive Next.js HMR + route handler isolation), and a
catch-all browser proxy (app/api/sosovalue/[...path]/route.ts)
so the API key never leaves the server.
Endpoint groups (typed wrappers in lib/api/sosovalue/):
| Wrapper file | Endpoints |
|---|---|
sosovalue.ts |
/etfs, /etfs/{ticker}/history, /etfs/summary-history, /news, /currencies/sector-spotlight, /indices, /indices/{ticker}/{constituents,market-snapshot,klines}, /currencies/{id}/{market-snapshot,klines} |
tokens.ts |
/currencies, /currencies/{id}/{token-economics,supply,pairs,fundraising} |
treasuries.ts |
/btc-treasuries, /btc-treasuries/{ticker}/purchase-history |
analyses.ts |
/analyses, /analyses/{name} (stablecoin mcap, fund flows…) |
news-trending.ts |
/news/hot, /news/featured |
news-search.ts |
/news/search |
fundraising.ts |
/fundraising/projects, /fundraising/projects/{id} |
macro.ts |
/macro/events, /macro/events/{id}/history |
etf-snapshot.ts |
/etfs/{ticker}/market-snapshot |
crypto-stocks.ts |
/crypto-stocks, /crypto-stocks/{ticker}/{snapshot,klines} |
For the full endpoint reference and response shapes see
docs/sosovalue-api.md.
The transport uses a token bucket instead of a plain serializer:
- Burst capacity:
SOSOVALUE_BUCKET_CAPACITYtokens (default 20) refill at one token perSOSOVALUE_MIN_GAP_MS. Lets a dashboard SSR fan out 20 calls in parallel without serializing - Per-path TTL (overrides
SOSOVALUE_CACHE_TTL_MSdefault):- klines / market-snapshot / news → 30–60 s
- currency details / token-economics → 5 min
- everything else → 15 min default
- In-flight dedup: parallel reads for the same path share one promise
- Error-class backoffs based on the SoSoValue error envelope:
- 5xx /
code 500001→ 5 min code 402901"monthly" → 6 hcode 402901other → 5 min400101 / 400102/ 401 → 6 h (auth failure, global)400301 / 400401 / 400402/ 403 / 404 → 1 h per-path negative cache
- 5xx /
- Stale-on-failure: previous good payload wins over a fresh error
- Cache lives in
globalThisso it survives Next.js HMR and route-handler isolation; bumped viaSTATE_VERSIONwhen the shape changes
The Python agent service (agent-service/src/tools/terminal.py)
ships its own equivalent limiter tuned via SOSOVALUE_MIN_GAP_SEC /
SOSOVALUE_CACHE_TTL_SEC. The agent loop in
agent-service/src/main.py sleeps AGENT_LOOP_SEC
(default 120 s) between iterations and rotates through SoSoValue's narrative
sectors so each cycle has at least one fresh token to spend.
Read-only endpoints (/markets/*, /accounts/{addr}/*) are public — no key.
Trade actions (/trade/orders/batch, /trade/orders/replace,
/trade/orders/schedule-cancel, /accounts/transfers, plus perps
/trade/orders, /trade/leverage, /trade/margin) require an EIP-712 signed
exchange action:
domain = { name: "spot"|"futures", version: "1",
chainId: 286623 (mainnet) | 138565 (testnet),
verifyingContract: 0x0…0 }
message = { payloadHash: keccak256(json.Marshal({type, params})), nonce: uint64 }The signature is 0x01 || ECDSA(...) (the leading byte tells the gateway it's
an EIP-712 action). Use a wallet (viem signTypedData, ethers v6, or
window.ethereum) — never bake a private key into the agent process.
lib/api/sodex/typedData.ts → buildSignedEnvelope
returns the typed-data envelope to feed into your wallet.
The agent supports both products via three chat tools, all browser-signed:
| Tool | Product | Symbol form | Sizing |
|---|---|---|---|
sodex_execute_trade |
spot | vSOL_vUSDC (testnet v prefix) |
USDC notional |
sodex_sell_trade |
spot | same | asset quantity |
sodex_perps_trade |
perps | SOL-USD (plain ticker) |
contract size + leverage |
On testnet, spot pairs frequently flip to cancel-only mode when no market
maker is around; perps tends to carry the live liquidity. The chat agent
should pick sodex_perps_trade whenever the user mentions leverage, long/short,
or "futures", or as a fallback when spot returns "symbol is in cancel only mode".
EIP-712 domain name is "spot" vs "futures" (not "perps"). The shared
/api/sodex/submit endpoint forwards both products transparently because the
prepared envelope carries base_url + path inline.
Default deployment target is Sepolia (SSI_CHAIN_ID=11155111).
- The Builder pipeline (
/builder) signsregisterIndex(...)via wagmiuseWriteContractfrom the user's connected wallet — no server hot key needed - The Portfolio page (
/portfolio) reads positions by filtering the registry's events forcreator == connectedWallet SSI_PRIVATE_KEYis only used for autonomous agent publishes (rebalance loop) when explicitly enabled — UI publishes always use the user's wallet
To deploy elsewhere (e.g. ValueChain L1 mainnet 286623), override
SSI_CHAIN_ID + SSI_RPC_URL + SSI_REGISTRY_ADDRESS and the matching
NEXT_PUBLIC_* mirrors.
Copy .env.example and agent-service/.env.example.
The standalone MCP server ships as part of the agent-service package and exposes 17 read-only SoSoValue research tools directly inside Claude Desktop — no API or custom UI required.
cd agent-service
pip install -e . # registers `hypenode-mcp` console-scriptThen point Claude Desktop at the binary — see the full setup guide in
agent-service/docs/CLAUDE_DESKTOP_SETUP.md
and copy the ready-made config from
agent-service/claude_desktop_config.example.json.
Available tools: sector sentiment, fund flows, news, sector spotlight, SSI index list, basket builder, real-klines backtest, currency snapshots, risk-gate checker, macro calendar, macro event history, smart-money signal, fundraising rounds, RootData project/investor search. All read-only — stateful trade tools are excluded by design.
The detailed plan lives in docs/product-roadmap.md.
Three waves:
- Wave 1 — Indexer MVP (~95% complete) — single user can build → simulate → deploy → monitor end-to-end on Sepolia
- Wave 2 — Publisher Revenue Loop (vault contract, subscriber deposits,
USDC fee streaming, marketplace
/discover) - Wave 3 — Production Hardening (mainnet migration, external audit, observability, mobile-responsive, ToS/Privacy, pricing tiers)
Public launch target: 15 June 2026.
Outstanding items at the README layer (not in the per-wave roadmap):
- Several typed wrappers substitute synthetic payloads when SoSoValue returns empty data — masks real outages; needs honest empty states
- Chat agent is non-streaming — full response returned after tool loop completes. SSE streaming is the highest-leverage UX win still on the table
- No stop-generation button on chat (timeout-bounded at
CHAT_LOOP_TIMEOUT_SEC, default 90 s) and no message edit / regenerate - Reasoning stream on
/agentis still polling, not SSE - ESLint disabled at build time — restore by adding
@typescript-eslint/*back to the parentSosovalue/package.jsondevDependencies - Mobile-responsive pass pending — most app pages assume ≥900px width
Internal — subject to MDlabs policy.
