Skip to content

Repository files navigation

stablerates

stablerates tracks and compares Solana stablecoin borrow APYs across protocols.

Project overview

Workspace Purpose
apps/indexer Periodically fetches configured borrow-market APYs and stores observations.
apps/api Serves health, metadata, latest-rate, and historical-rate endpoints.
apps/web Browser dashboard with market filtering, chart ranges, table sorting, and rate history.
packages/core Shared domain types, protocol metadata, and server-only protocol adapters.
packages/clickhouse ClickHouse client, registry access, observation writes, and rate repository.
packages/logger Structured Pino logger for services.
packages/ui Shared React/shadcn UI primitives and styles.

The market registry and persisted data contract are documented in clickhouse/README.md.

Prerequisites

  • Bun 1.3.14 or later
  • Docker and Docker Compose for the local ClickHouse stack
  • A Solana RPC endpoint for reliable indexing (the public mainnet endpoint is the development default)
  • The Kora CLI and envsubst for the local x402 E2E test

Run locally

Install workspace dependencies and create service environment files from their examples:

bun install
cp apps/api/.env.example apps/api/.env
cp apps/indexer/.env.example apps/indexer/.env
cp apps/web/.env.example apps/web/.env

Start ClickHouse, load the registry data, then run the services in separate terminals:

docker compose up -d
clickhouse/scripts/init-mints.sh clickhouse/mints.csv
clickhouse/scripts/init-borrow-markets.sh clickhouse/borrow-markets.csv

bun run --cwd apps/api dev
bun run --cwd apps/indexer dev
bun run --cwd apps/web dev

Validate

bun run typecheck
bun run test
bun run build

E2E tests

bun run test:e2e runs every E2E test under e2e/. The x402 suite starts an in-process Surfnet instance, renders a temporary Kora configuration with isolated ports, and launches Kora against Surfnet’s dynamic RPC URL. It then exercises the real API → facilitator → Kora payment flow using the checked-in local keypairs, airdrops SOL to the treasury, Kora signer, and x402 user, and initializes/funds the treasury and user ATAs for every mint Kora supports.

Start Redis and ClickHouse before running the suite:

docker compose up -d redis clickhouse
bun run test:e2e

The test uses the repository .env.test configuration and only seeds/removes its own rate rows. Surfnet reads required account state from mainnet, but every payment transaction settles locally. x402 advertises the devnet CAIP identifier because that is the identifier supported by the current x402 SVM client.

Architecture

flowchart LR
  registry["ClickHouse market registry"] --> indexer["apps/indexer"]
  indexer <--> sources["Kamino / Jupiter / Project 0 / Save"]
  indexer --> observations["ClickHouse rate observations"]

  observations --> api["apps/api"]
  redis["Redis"] --> api
  browser["Browser"] -->|"public REST API"| api

  client["x402 client"] -->|"paid /v1/x402/* request"| api
  api -->|"verify / settle"| facilitator["apps/facilitator"]
  redis --> facilitator
  facilitator --> kora["Kora"]
  kora --> solana["Solana"]
  api --> payments["ClickHouse x402 payments"]
Loading
  • apps/indexer reads the ClickHouse market registry, obtains one rate per market through protocol adapters, and appends observations.
  • apps/api exposes health, readiness, metadata, free rate endpoints, and paid x402 rate endpoints. It reads ClickHouse, applies Redis-backed rate limiting to /v1/*, and records settled x402 payments in ClickHouse.
  • apps/facilitator implements the x402 facilitator supported, verify, and settle operations. It enforces the configured Solana network, treasury, and mint allowlist before asking Kora to sign or broadcast a payment.
  • apps/web is a TanStack Start/Vite dashboard. Browser code calls the public API directly using the public VITE_API_BASE_URL; it is not a BFF.
  • packages/clickhouse owns the ClickHouse client plus registry, observation, rate-query, and x402-payment persistence helpers.
  • packages/redis owns the shared Redis client factory. The API and facilitator each own their rate-limit middleware and connection lifecycle.
  • packages/core owns shared protocol identifiers, metadata, API/domain types, and server-only protocol adapters. packages/logger owns structured service logging, and packages/ui owns reusable React/shadcn components and styles.

About

DeFi dashboard that tracks and compares stablecoin borrow APYs.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages