Skip to main content

Live Deployment

ComponentURL
Appshoot-production-f218.up.railway.app
Documentationdocs-1f2b6c2c.mintlify.app
On-chain program4HVnwG8...2iMG (devnet)

Prerequisites

ComponentVersionNotes
Node.js>= 20 LTSFrontend, API server, Autopilot SDK
Rust>= 1.78Keeper service
PostgreSQL>= 14Required — all state persisted to DB (no localStorage fallbacks)
Docker>= 24For production deployment

Stack

  • Next.js 16 with React 19 for the full-stack app
  • Prisma ORM with PostgreSQL for all server-side persistence
  • Rust / Axum for the keeper service (position monitor, scoring engine, lifecycle FSM)
  • Privy for wallet authentication (embedded + external wallets)
  • Adrena Data API (datapi.adrena.trade) for real position data
  • Yellowstone gRPC for real-time Solana position monitoring
  • Solana (devnet) for on-chain entry fees, settlement, and agent registration via the Shoot Anchor program

Local Development

1

Clone and install

git clone https://github.com/danielAsaboro/shoot.git
cd shoot
npm install
2

Environment variables

Copy .env.example to .env.local and configure:
cp .env.example .env.local
Minimum required:
DATABASE_URL=postgresql://user@localhost:5432/shoot?schema=public
NEXT_PUBLIC_COMPETITION_PROVIDER=adrena
NEXT_PUBLIC_PRIVY_APP_ID=your-privy-app-id
NEXT_PUBLIC_PRIVY_APP_SECRET=your-privy-app-secret
3

Database setup

# Push schema to PostgreSQL (no migration files needed in dev)
npx prisma db push

# Seed initial data (cohorts, wallets, World Cup traders)
npx tsx prisma/seed/index.ts
4

Run development server

npm run dev
# → http://localhost:3000
5

Run tests

npm test                          # 278 unit tests (main app)
cd sdk && npm install && npm test # 144 tests (autopilot SDK)
cd keeper && cargo test           # 60+ tests (keeper service)
npm run lint                      # ESLint
npm run build                     # production build check

Deploy to Railway (Docker)

The app is containerized with a multi-stage Dockerfile:
# Build and run locally
docker compose up

# Or deploy to Railway
# Railway auto-detects the Dockerfile and builds on push

Railway Configuration

SettingValue
Build Commandprisma generate && next build
Start Commandnode server.js
Port3000
Health CheckGET /
Set these environment variables in Railway:
VariableRequiredDescription
DATABASE_URLYesPostgreSQL connection string (Railway provides this if you add a Postgres plugin)
NEXT_PUBLIC_COMPETITION_PROVIDERYesAlways adrena for production
NEXT_PUBLIC_PRIVY_APP_IDYesPrivy app ID from dashboard.privy.io
NEXT_PUBLIC_PRIVY_APP_SECRETYesPrivy app secret
NEXT_PUBLIC_SOLANA_CLUSTERYesdevnet (or mainnet for production)
ADRENA_DATA_API_BASE_URLNoDefaults to https://datapi.adrena.trade
CRON_SECRETYesBearer token for /api/cron/* authentication
ADMIN_SECRETYesBearer token for /api/admin/* authentication
DISCORD_WEBHOOK_URLNoDiscord webhook for competition notifications
DISCORD_OPS_WEBHOOK_URLNoSeparate webhook for sybil alerts
BUYBACK_WALLET_KEYPAIRNoJSON array of secret key bytes for Jupiter ADX buyback wallet. If not set, buybacks are recorded as “pending”.

Privy Configuration

  1. Create app at dashboard.privy.io
  2. Enable: Embedded Wallets, External Wallets (Phantom, Backpack, Solflare)
  3. Set allowed origins: https://your-domain.up.railway.app, http://localhost:3000
  4. Copy App ID to NEXT_PUBLIC_PRIVY_APP_ID

Cron Jobs

All cron endpoints are POST requests authenticated with a Bearer token. They are triggered by an external scheduler (Railway cron, GitHub Actions, cron-job.org, etc.) — this is by design to avoid Vercel cron dependency.
# Refresh scores — runs every 5 minutes
# Fetches positions, computes metrics, updates leaderboard, runs sybil detection
curl -X POST \
  -H "Authorization: Bearer $CRON_SECRET" \
  https://shoot-production-f218.up.railway.app/api/cron/refresh-scores

# Rotate cohorts — runs every 15 minutes
# Transitions cohorts: upcoming → live → settled
curl -X POST \
  -H "Authorization: Bearer $CRON_SECRET" \
  https://shoot-production-f218.up.railway.app/api/cron/rotate-cohorts
EndpointFrequencyWhat It Does
POST /api/cron/refresh-scoresEvery 5 minFetch positions, compute scores, emit quest events, run sybil detection, auto-settle expired cohorts
POST /api/cron/rotate-cohortsEvery 15 minTransition cohort state machine (upcoming → live → settled)

On-Chain Program

The Shoot Anchor program handles USDC entry fees, vault escrow, and settlement on Solana.
cd programs/shoot

# Build
anchor build

# Deploy to devnet
solana config set --url https://api.devnet.solana.com
solana program deploy target/deploy/shoot.so \
  --program-id target/deploy/shoot-keypair.json
PropertyValue
Program ID4HVnwG8iz7wdUbEQDH8cYGD6EuxNmMuEbvCrz8Ke2iMG
ClusterDevnet
AuthorityCChvxUR37fry8i2Gdvyrmwu2PH8vgZeTcFwtNqLxaHDW
ExplorerView on Solana Explorer
See On-Chain Program for full instruction documentation.

Keeper Service (Rust)

The keeper monitors Adrena positions via Yellowstone gRPC, computes scores, and manages competition lifecycle. It matches Adrena’s own keeper infrastructure pattern.
cd keeper

# Build
cargo build --release

# Run
GRPC_ENDPOINT=https://grpc.yellowstone.example.com \
GRPC_TOKEN=your-token \
DATABASE_URL=postgresql://user@localhost/shoot_keeper \
LISTEN_ADDR=0.0.0.0:8080 \
cargo run --release

Keeper Docker

# docker-compose.yml
services:
  keeper:
    build: ./keeper
    environment:
      - GRPC_ENDPOINT=${GRPC_ENDPOINT}
      - GRPC_TOKEN=${GRPC_TOKEN}
      - DATABASE_URL=${DATABASE_URL}
    ports:
      - "8080:8080"

Keeper Environment Variables

VariableRequiredDefaultDescription
GRPC_ENDPOINTYesYellowstone gRPC URL
GRPC_TOKENYesAuth token for gRPC
DATABASE_URLYesPostgreSQL connection string
ADRENA_PROGRAM_IDNo13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwetAdrena program to monitor
LISTEN_ADDRNo0.0.0.0:8080HTTP server bind address
See Keeper Service for full documentation.

Autopilot SDK

Install and test the autonomous trading SDK:
cd sdk
npm install
npm test    # 144 tests
See Autopilot SDK for strategy configuration and usage.

Claude Code Skill

The shoot-trading skill gives Claude Code agents complete context for the trading stack — 12 tools, transaction signing flow, on-chain lifecycle, and Surfpool-based local testing. Drop it into any working directory and run Claude Code non-interactively. Install:
mkdir -p .claude/skills/
cp -r shoot-trading-skill/ .claude/skills/shoot-trading/
Run agent non-interactively:
claude -p "Register an agent, enroll in a challenge, open 3 trades across BONK/JITOSOL/WBTC, and submit results." \
  --model claude-sonnet-4-5 \
  --dangerously-skip-permissions
Claude Code auto-discovers skills in .claude/skills/ — no additional config needed. See Claude Code Skill for full reference including Surfpool quirks, token symbols, and lifecycle sequence.

MCP Server

The app ships an MCP server at /api/mcp that exposes all 12 trading tools to Claude Code, Claude Desktop, Cursor, and any other MCP-compatible agent host.
{
  "mcpServers": {
    "shoot-trading": {
      "type": "http",
      "url": "https://shoot-production-f218.up.railway.app/api/mcp",
      "headers": { "Authorization": "Bearer shoot_ak_YOUR_KEY" }
    }
  }
}
See MCP Server for full installation instructions across all supported clients.

Vercel (Preview Deploys Only)

A vercel.json exists for preview deploys during development. Production deployment targets Railway.
vercel --env NEXT_PUBLIC_PRIVY_APP_ID=your-id

Architecture Overview

┌──────────────────────────────────────────────────────────────┐
│  Frontend (Next.js 16 / React 19 — Railway / Docker)         │
│  /             → ArenaHub (Challenges + World Cup tabs)      │
│  /profile      → Trader dashboard                           │
│  /api/competition/*  → Prop challenge APIs                  │
│  /api/world-cup/*    → World Cup APIs                       │
│  /api/agent/*        → Autopilot trading APIs               │
│  /api/cron/*         → Score refresh + cohort rotation      │
│  /api/admin/*        → Settlement + sybil review            │
│  /api/health         → Health check                         │
├──────────────────────────┬───────────────────────────────────┤
│  Keeper (Rust / Axum)    │  Adrena Data API                  │
│  :8080                   │  datapi.adrena.trade              │
│  gRPC position monitor   │  → real positions + metrics       │
│  Scoring engine          │                                   │
│  Lifecycle FSM           │  Yellowstone gRPC (Solana)        │
│  REST + SSE + Prometheus │  → real-time account changes      │
└──────────────────────────┴───────────────────────────────────┘
Data flow:
  1. adrena-live-adapter.ts reads data/competition-cohorts.json for enrolled wallets
  2. Fetches positions from datapi.adrena.trade/position?user_wallet=...
  3. computeMetricsFromPositions() derives PnL%, volume, win rate, consistency, drawdown
  4. computeTournamentScore() produces composite ranking score
  5. Sybil detection runs across the cohort
  6. buildCompetitionSnapshotFromSources() assembles the full snapshot for the UI

Adding New Wallets

Edit data/competition-cohorts.json and add wallet addresses to a cohort’s enrolledWallets array. The adapter will fetch their real positions from Adrena on the next snapshot request.