SatoshiPilot is an AI-powered DeFi copilot for the Stacks blockchain. It aggregates live yield data from eight Bitcoin DeFi protocols, exposes a conversational interface where users instruct the AI in natural language, and executes multi-step on-chain strategies — swapping tokens, comparing vaults, and staging signed transactions — through a structured Plan/Execute protocol backed by a Groq LLM with live protocol data injected into every inference call.
- Overview
- Features
- Architecture
- Project Structure
- Integrated Protocols
- AI Assistant
- Getting Started
- API Reference
- Frontend Pages
- Deploying to Render
- Tech Stack
SatoshiPilot is built around the idea that interacting with Bitcoin DeFi on Stacks should not require a user to understand individual protocol mechanics, contract addresses, or transaction construction. The interface is a chat window backed by a Groq LLM. The user states an intent — yield optimisation, a token swap, a protocol comparison, a portfolio summary — and the AI determines the sequence of operations required, fetches the relevant live data, and guides execution.
The backend injects the full live dataset from all eight integrated protocols into the LLM system prompt on every request. This means APY figures, TVL, lock-up periods, and pool identifiers the AI references are always current, not approximated from training data.
For strategies that require more than a single on-chain action, the AI enters a Plan/Execute flow. It presents a numbered checklist of steps, waits for explicit user confirmation, then executes exactly one step per response with a visible progress tracker. Steps that require a wallet signature — a swap, a deposit, a withdrawal — trigger a pre-filled transaction modal in the browser. The user reviews the parameters and signs in their own wallet. The AI is notified of the outcome via a structured callback and either advances to the next step or handles the failure explicitly.
For direct single-action requests the planning phase is skipped entirely and the modal is staged immediately.
The frontend is a React/TypeScript SPA. The backend is a Node.js ESM API server that owns all SDK credentials, LLM calls, and on-chain reads. No private keys, Groq API keys, or Bitflow SDK calls exist in the client bundle.
AI Copilot with Structured LLM Responses
The AI layer uses Groq's llama-3.1-8b-instant model. Every LLM response is enforced as a structured JSON object containing a phase, an ordered plan array, a current_step_index, an action field for modal triggers, params for that action, and a pool_cards array for visual comparison rendering. The model is never permitted to return free-form text — the backend parser extracts the JSON from the raw completion and rejects malformed output gracefully.
Plan/Execute Protocol
Complex multi-step strategies (e.g. compare pools across protocols, route a swap, then deposit the output) are handled through a two-phase flow. In the plan phase the AI presents a numbered checklist and sets awaiting_confirmation: true. On confirmation it enters the execute phase, advancing current_step_index by one per response and setting action + params when a step requires a wallet interaction. A StepperBar component in the UI reflects the current state. Simple single-step requests bypass planning entirely and go directly to phase: execute.
Live Protocol Data Injection
APY, TVL, lock-up periods, yield assets, and pool identifiers are fetched concurrently from all eight protocol integrations on each /api/protocols request and cached server-side for five minutes. This cache is shared with the AI route, so every LLM inference call receives an up-to-date pool summary injected into the system prompt. The AI is explicitly instructed never to use training-data yield figures.
Bitflow DEX Integration
Token swap routing is handled entirely server-side using the @bitflowlabs/core-sdk. The API exposes token discovery, route enumeration, parallel best-route quoting, and contract-call parameter generation as discrete endpoints. The frontend receives ready-to-sign openContractCall parameters and never instantiates the SDK or holds swap routing state.
Modal-Based Transaction Staging
Deposits, withdrawals, and swaps are executed through pre-filled modals mounted at the app root. The AI stages a modal by returning an action and params in its response; the frontend handleToolCall bridge maps this to the appropriate Zustand store action which opens the modal. After the user signs or cancels in their Stacks wallet, an outcome callback fires and the result is sent back to the AI as a structured event message to continue or branch the conversation.
Multi-Protocol Vault Browser The Vaults page renders all protocol pools with live APY, TVL, category, lock-up period, and a direct deposit entry point. Pools are filterable by category, asset, and protocol. Any vault can be deposited into independently of the AI flow.
Portfolio Balances STX, locked STX, sBTC, xBTC, and stSTX balances are fetched from the Hiro API via the backend proxy and displayed in real time, refreshing automatically every 30 seconds after wallet connection.
Protocol Comparison Cards
When the AI determines a pool comparison is warranted, it populates the pool_cards field in its response with structured pool objects. The frontend renders these as side-by-side cards showing protocol, product, APY, TVL, lock-up period, and a risk classification badge derived from the pool category, with a direct selection action that feeds back into the conversation.
Browser (React + TypeScript)
|
| REST (JSON)
v
Express API Server (Node.js ESM)
|
|-- /api/protocols -- aggregates 8 protocol fetchers concurrently
|-- /api/balances -- proxies Hiro blockchain API
|-- /api/swap/* -- wraps Bitflow SDK (tokens, routes, quote, params)
|-- /api/ai/chat -- Groq LLM inference with live pool data injected
|
|-- Groq (llama-3.1-8b-instant)
|-- Hiro API (balances, contract reads)
|-- Bitflow SDK (swap routing)
|-- Protocol APIs / On-chain reads (StackingDAO, ALEX, Zest, ...)
The frontend and backend are completely independent deployments. The frontend is a static Vite build that communicates with the backend exclusively through the REST API. No private keys, API keys, or SDK credentials exist in the frontend bundle.
SatoshiPilot/
|-- package.json Root scripts (dev:backend, dev:frontend, build)
|-- render.yaml Render deployment config for both services
|
|-- backend/
| |-- package.json
| |-- src/
| |-- server.js Express app, CORS, health endpoint
| |-- routes/
| | |-- ai.js POST /api/ai/chat — Groq inference
| | |-- balances.js GET /api/balances/:address
| | |-- protocols.js GET /api/protocols
| | |-- swap.js GET/POST /api/swap/*
| |-- services/
| | |-- protocols/
| | |-- index.js Concurrent aggregator
| | |-- alex.js
| | |-- arkadiko.js
| | |-- bitflow.js
| | |-- cycle.js
| | |-- granite.js
| | |-- hermetica.js
| | |-- stackingdao.js
| | |-- velar.js
| | |-- zest.js
| |-- lib/
| | |-- bitflow/
| | |-- client.js Singleton Bitflow SDK instance
| | |-- parallelQuote.js Parallel route quoting
| |-- utils/
| |-- cache.js In-process TTL cache
| |-- contractRead.js Hiro API helper
|
|-- frontend/
|-- index.html
|-- vite.config.ts
|-- src/
|-- App.tsx Root shell, routing, modal mounts
|-- main.tsx
|-- pages/
| |-- AIChatPage.tsx Full-page AI chat with Plan/Execute UI
| |-- VaultsPage.tsx Protocol vault browser with filters
| |-- SwapPage.tsx Token swap interface
|-- components/
| |-- layout/ Header, navigation
| |-- modals/ DepositModal, SwapModal
| |-- swap/ TokenPicker, token utilities
| |-- vaults/ VaultCard, VaultFilters
| |-- wallet/ WalletConnectButton, BalanceCard, WalletInfo
| |-- ai/ ChatPanel (floating)
|-- store/
| |-- chatStore.ts Zustand — conversation history, plan state
| |-- poolStore.ts Zustand — deposit modal state + callbacks
| |-- swapStore.ts Zustand — swap modal state + callbacks
|-- contexts/
| |-- WalletContext.tsx Stacks wallet connection, balance refresh
|-- lib/
| |-- ai/handleToolCall.ts AI tool -> modal bridge
| |-- bitflow/executeSwap.ts Swap transaction builder
| |-- stacks/deposit.ts Deposit transaction builder
|-- services/
| |-- hiroApi.ts Hiro balance fetching
| |-- protocolsApi.ts Frontend protocols API wrapper
|-- types/
|-- pools.ts
|-- protocol.ts
|-- wallet.ts
| Protocol | Category | Asset | Data Source |
|---|---|---|---|
| StackingDAO | Liquid stacking | STX | On-chain contract |
| Bitflow | DEX liquidity pool | STX/stSTX | On-chain contract |
| ALEX | AMM DEX | STX/sBTC | ALEX REST API |
| Hermetica | Synthetic yield | sBTC | Hermetica REST API |
| Velar | DEX liquidity pool | STX | Velar REST API |
| Zest | Lending/borrowing | sBTC | On-chain contract |
| Arkadiko | Lending/stablecoin | STX/DIKO | On-chain contract |
| Granite | Lending | sBTC | On-chain contract |
Each protocol fetcher is isolated and fault-tolerant. If one fetcher throws, the aggregator returns an error entry for that protocol and continues serving the others.
The assistant runs on Groq's llama-3.1-8b-instant model. Every response is a structured JSON object — never plain text — with the following fields:
{
"message": "Plain English shown to the user.",
"phase": "plan | execute | complete | error",
"plan": ["Step label 1", "Step label 2"],
"current_step_index": 0,
"action": "stage_swap | stage_deposit | stage_withdraw | null",
"params": {},
"awaiting_confirmation": true,
"pool_cards": []
}Phase behaviour:
plan— used only for tasks spanning three or more distinct operations across multiple modals or protocols. The user sees a checklist and Confirm/Cancel buttons before anything executes.execute— one step per reply. A StepperBar above the messages tracks progress. Steps that require wallet interaction setactionandparamsto open the appropriate modal.complete— single-step answers and direct actions. No plan card is shown.error— failure with a clear human-readable description.
Action tools (open modals):
stage_deposit— opens the deposit modal for a givenpool_idand optionalamountstage_swap— opens the swap modal withfrom_token,to_token, optionalamountstage_withdraw— opens the withdrawal modal for a givenpool_id
Read-only tools (resolved inline by the AI):
get_best_pool— finds the highest-APY pool for a given assetcompare_pools— compares a list of pools and populatespool_cardsfor the UIexplain_pool— describes a pool's mechanicsget_portfolio_summary— summarises the connected wallet's positions
The backend injects the full live pool dataset into the system prompt on every request, so the AI never falls back on stale training data for APY or TVL figures.
- Node.js 20.10.0 or later
- A Groq API key (free tier is sufficient)
- Optional: a Hiro Platform API key for higher rate limits on balance lookups
backend/.env
GROQ_API_KEY=your_groq_api_key
FRONTEND_URL=http://localhost:5173
HIRO_API_KEY=your_hiro_api_key # optional
PORT=3001 # optional, defaults to 3001
PROTOCOLS_CACHE_TTL_MS=300000 # optional, defaults to 5 minutes
frontend/.env
VITE_API_BASE_URL=http://localhost:3001
Leave VITE_API_BASE_URL unset or empty to use a relative URL (useful when frontend and backend are served from the same origin).
Install dependencies and start both services from the root:
# Install all dependencies
npm install --prefix backend
npm install --prefix frontend
# Start backend (port 3001, file-watch enabled)
npm run dev:backend
# Start frontend (port 5173, Vite HMR)
npm run dev:frontendOr start each service individually from its own directory:
# Backend
cd backend && npm run dev
# Frontend
cd frontend && npm run devReturns { status: "ok" }. Use this to verify the server is running.
Returns the aggregated pool data from all eight protocols.
{
"protocols": [
{
"id": "stackingdao-liquid",
"protocol": "StackingDAO",
"product": "Liquid Stacking",
"category": "liquid-stacking",
"asset": "STX",
"yieldAsset": "stSTX",
"apy": 7.42,
"tvlUsd": 45200000,
"lockupDays": 0,
"url": "https://stackingdao.com"
}
],
"fetchedAt": "2026-03-13T10:00:00.000Z"
}Proxies Hiro's balance endpoint. Returns STX balance, locked STX, and all fungible token balances for the given Stacks address.
Returns all tokens available for swapping. Pass ?from=<tokenId> to return only tokens reachable from a given token (used to populate the TO-token dropdown after a FROM token is selected).
Returns all routes between two tokens. Query params: tokenX, tokenY.
Body: { tokenX, tokenY, amount }. Returns the best route and estimated output amount.
Body: { tokenX, tokenY, amount, route, slippage, senderAddress }. Returns the full contract-call parameters ready to pass to openContractCall.
Body:
{
"messages": [
{ "role": "user", "content": "Swap 10 STX to sBTC" }
],
"userAddress": "SP..."
}Returns the structured AI response object described in the AI Assistant section.
AI Chat (/ai)
Full-page conversation interface. Renders plan cards with step checklists and Confirm/Cancel buttons for multi-step plans, a step progress bar during execution, and pool comparison cards when the AI compares protocols.
Portfolio Displays the connected wallet's STX balance, locked STX, sBTC, xBTC, and stSTX holdings. Balances auto-refresh every 30 seconds.
Vaults Browsable list of all protocol pools with live APY, TVL, category, lock-up period, and a deposit button. Filterable by category, asset, and protocol.
Swap Token swap interface powered by Bitflow. Selects FROM and TO tokens via the TokenPicker component, auto-fetches the best quote with a 600 ms debounce, displays the route path, and builds the transaction on confirm.
A render.yaml file at the root configures both services for Render's Blueprint deployment.
- Push the repository to GitHub.
- In the Render dashboard, create a new Blueprint and point it at the repository.
- Set the following environment variables in the Render dashboard (they are marked
sync: falsein the YAML and will not be committed):
| Service | Variable | Value |
|---|---|---|
| Backend API | GROQ_API_KEY |
Your Groq API key |
| Backend API | FRONTEND_URL |
The deployed frontend URL from Render |
| Backend API | HIRO_API_KEY |
Your Hiro Platform API key (recommended) |
| Frontend | VITE_API_BASE_URL |
The deployed backend URL from Render |
The frontend is deployed as a static site with a catch-all rewrite to index.html for client-side routing.
Frontend
- React 19 with TypeScript
- Vite 7
- Zustand 5 (state management)
- @stacks/connect (wallet integration — Xverse, Leather, and all WalletConnect-compatible Stacks wallets)
Backend
- Node.js 20+ (ESM)
- Express 5
- groq-sdk (Groq LLM API)
- @bitflowlabs/core-sdk (swap routing)
- @stacks/transactions (Clarity value encoding)
Infrastructure
- Render (backend: Node web service, frontend: static site)
- Hiro API (blockchain data)
- Groq Cloud (LLM inference)