projects — cas
CAS Backend Module
Purpose
The CAS (Conflux Automation System) backend is a Node.js/Express application that provides a local development environment for managing automated DeFi strategies on the Conflux eSpace. It handles user authentication via SIWE (Sign-In with Ethereum), job creation and management (limit orders, DCA, TWAP, swaps), administrative controls, pool data aggregation from external sources, system health monitoring, and real-time updates via Server-Sent Events (SSE). The backend uses SQLite for persistence and integrates with Conflux smart contracts through the CDK and viem libraries.
Architecture Overview
The backend follows a modular structure centered around an Express application that registers specialized routers for different functional domains. Core components include:
- HTTP Layer: Express app with CORS and JSON middleware
- API Routers: Health, Auth, Jobs, Admin, Pools, System, SSE
- Data Layer: SQLite database with repositories for jobs, executions, nonces, and settings
- Business Logic: Job validation, SIWE authentication, safety checks, pools data fetching
- Background Worker: Optional keeper process for executing jobs
- Real-time Updates: SSE hub for pushing job state changes to clients
Data flows through validation → repository operations → external service calls (when needed) → response formatting. The worker operates independently, reading from the same database to execute jobs based on price conditions and safety configurations.
Key Components
Application Initialization (src/app.ts)
Creates the Express application and mounts all API routers:
/health: Basic service health/auth: SIWE authentication endpoints/jobs: User job management (CRUD operations)/admin: Administrative controls (pause/resume, safety config)/pools: Token and trading pair data/system: Comprehensive system health status/sse: Server-Sent Events for real-time job updates
Configuration (src/config.ts)
Manages environment-based configuration with the CasBackendConfig interface:
- Server: Port, host, CORS origins
- Database: SQLite path
- Security: Auth secret, session/nonce TTLs
- Network: Testnet/Mainnet selection, RPC URL
- Contracts: AutomationManager, PriceAdapter, PermitHandler addresses
- Features: Keeper (worker) settings, price source selection
- Admin: List of authorized addresses
- Cache: Pools cache TTL
Validation ensures values are within expected ranges (e.g., positive numbers, valid network values).
Database Layer (src/db/sqlite.ts)
Extends the automation package’s SQLite runtime with CAS-specific tables:
- Core Tables:
auth_nonces: Stores SIWE nonces with expiration and usage trackingworker_heartbeat: Records keeper worker’s last seen timesettings: Persists safety configuration (global pause, max swap USD, slippage bps, max retries)
- Repositories:
jobs/executions: From automation package (Drizzle-based)nonces: Manages SIWE nonce lifecycle (issue, consume, sweep expired)settings: Handles safety configuration (get/set methods for all parameters)heartbeat: Tracks worker PID and last heartbeat timestamp
Schema initialization runs on database creation to ensure required tables exist.
Authentication System (src/routes/auth.ts)
Implements SIWE (EIP-4361) for secure, passwordless authentication:
- Endpoints:
GET /auth/nonce: Issues a nonce for address (validates EVM address format)POST /auth/verify: Verifies SIWE message/signature, returns JWTGET /auth/me: Returns current session data
- Validation Steps:
- Address format validation (viem
isAddress) - SIWE message parsing and verification (correct chainId: 71 testnet, 1030 mainnet)
- Nonce consumption (prevents replay attacks)
- Admin status determination (configurable admin addresses)
- Address format validation (viem
- Session Tokens: HMAC-signed JWTs with configurable TTL, containing address and admin claims
Job Management System
Validation (src/routes/job-validators.ts)
Validates and normalizes job creation requests:
- Job Types:
limit_order,dca,twap,swap(enforced viaJOB_TYPESset) - Common Fields:
- Token addresses (EVM format validation)
- Numeric parameters (non-negative integers, positive where required)
- Optional fields (maxRetries, expiresAt, onChainJobId)
- Type-Specific Handling:
- Limit order:
amountIn,minAmountOut,targetPrice,direction - DCA:
amountPerSwap,intervalSeconds,totalSwaps - TWAP:
amountIn,minAmountOut,trancheCount,trancheIntervalSeconds - Swap:
amountIn,minAmountOut
- Limit order:
Job Router (src/routes/jobs.ts)
Handles authenticated job operations:
- Creation:
POST /jobs→ validates input → creates job record → publishes SSE update - Listing:
GET /jobs→ filters by owner/status/type → returns job DTOs - Details:
GET /jobs/:id→ ownership verification → returns job - Cancellation:
POST /jobs/:id/cancel→ owner verification → updates status - Deletion:
DELETE /jobs/:id→ owner/admin verification → removes job and executions - Executions:
GET /jobs/:id/executions→ returns execution history - Updates:
GET /jobs/updates→ returns jobs modified since timestamp (for polling)
Administrators can list all jobs via /admin/jobs with optional status filtering.
Administrative Controls (src/routes/admin.ts)
Provides system management capabilities (admin-only):
- Global Pause:
GET /admin/status: Returns current pause statePOST /admin/pause: Sets paused=true in settingsPOST /admin/resume: Sets paused=false in settings
- Safety Configuration:
GET /admin/safety: Returns{maxSwapUsd, slippageBps, maxRetries, globalPause}PATCH /admin/safety: Updates individual safety parameters with validation:maxSwapUsd: non-negative number or nullslippageBps: integer between 0-10,000maxRetries: integer between 0-100
- Job Administration:
GET /admin/jobs: Lists all jobs (optional status filter)
Pools Data Service (src/routes/pools.ts)
Provides token and trading pair information:
- Sources:
- Testnet: Fallback to Swappi factory query for WCFX/USDT pair
- Mainnet: GeckoTerminal API (with pagination, retries, and caching)
- Caching:
- In-memory cache with TTL (configurable via
poolsCacheTtlMs) - Permanent storage of successfully fetched data to serve stale content on failure
- In-memory cache with TTL (configurable via
- Endpoints:
GET /pools: Returns{tokens, pairs, cachedAt}POST /pools/refresh: Forces cache refresh
- Data Normalization:
- Converts GeckoTerminal resource identifiers to lowercase addresses
- Filters pairs to ensure both tokens exist in the token list
- Sorts tokens by symbol for consistent ordering
System Health Monitoring (src/routes/system.ts)
Aggregates health metrics from multiple subsystems:
- Backend: Always reports OK (if server is running)
- Database:
- Job/execution counts
- Pending/active/failed job tallies
- Last execution timestamp
- RPC:
- Connectivity to Conflux node
- Current block number
- Request latency
- Contracts:
- Bytecode presence check for key contracts
- Address verification
- Worker:
- Status (unknown/active/idle) based on last heartbeat/execution
- Last seen timestamp
- Additional:
- Global pause state
- Service uptime (human-readable and seconds)
- Response: Structured as
CasSystemStatusResponseDTO
Real-Time Updates (src/sse/events.ts)
Implements Server-Sent Events for live job updates:
- Connection:
- Clients connect to
/sse/jobswith JWT (via Authorization header or token query param) - On connection: sends snapshot of user’s current jobs
- Clients connect to
- Update Mechanism:
- Hub per backend state manages client connections
- Background polling (15s interval) checks for job updates since last poll
- Changes pushed to relevant clients (filtered by job owner)
- Message Format:
data: {type: "snapshot"|"job_update", payload}\n\n- Payload contains job DTOs (from
@cfxdevkit/cas-shared)
- Reliability:
- Heartbeat ping every 30s to prevent timeout
- Client cleanup on connection close
- Error handling for failed job list queries
Keeper Worker (src/worker.ts)
Optional background process for job execution (enabled via KEEPER_ENABLED=true):
- Prerequisites:
- Signer private key in config
- Keeper enabled flag
- Components:
- Price Source:
- GeckoTerminal (testnet/mainnet) or
- Swappi (on-chain via router contract)
- Safety Guard:
- Reads safety config from database on each check (allows live updates)
- Extends base safety guard with dynamic config reloading
- Keeper Client:
- Submits transactions to AutomationManager contract
- Uses signer from private key
- Respects max gas price config
- Repositories:
- Job and execution repositories from database layer
- Heartbeat:
- Updates worker’s last seen time and PID in database
- Price Source:
- Operation:
- Runs at
keeperIntervalMs(default 15s) - Processes jobs with configurable concurrency
- Updates job status and creates execution records on success/failure
- Runs at
Interaction with Other Modules
Internal Packages
@cfxdevkit/cas-shared:- Provides TypeScript DTOs for all API requests/responses (e.g.,
CasJobDto,CasPoolsResponse) - Ensures type safety between backend and frontend
- Provides TypeScript DTOs for all API requests/responses (e.g.,
@cfxdevkit/automation:- Supplies core job execution logic, Drizzle repositories, and
Keeperclass - Defines job/execution schemas used by SQLite layer
- Supplies core job execution logic, Drizzle repositories, and
@cfxdevkit/cdk:- Lowest-level Conflux interaction (clients, chains, account signing)
@cfxdevkit/protocol:- Contract addresses and ABIs for Swappi, WCFX, etc.
@cfxdevkit/wallet-connect:- SIWE message creation and verification utilities
Frontend Integration
The Next.js frontend (apps/frontend) consumes the backend through:
- Direct API Calls: Via
CasApiClient(wrapsfetchwith automatic JWT attachment) - Context Management:
auth-context.tsx: Handles SIWE flow, token storage, and API client instantiationpools-context.tsx: Fetches and caches token data with balance information
- Proxying:
/app/api/[...path]/route.tsforwards requests to backend to avoid CORS during development
- State Synchronization:
- Auth context validates token with
/auth/meon load and token change - Pools context refreshes data on mount and account changes
- Auth context validates token with
Data Flow Examples
Authentication Flow
- Nonce Request:
- Client →
GET /auth/nonce?address=0x... - Backend → generates nonce → stores in
auth_nonces→ returns{nonce}
- Client →
- Signature Verification:
- Client → signs SIWE message with private key →
POST /auth/verify - Backend → verifies signature/chainId/nonce → consumes nonce → checks admin status → signs JWT → returns
{token, address, isAdmin}
- Client → signs SIWE message with private key →
- Session Use:
- Client → includes
Authorization: Bearer <token>in subsequent requests - Backend → verifies token via HMAC → extracts session data
- Client → includes
Job Creation and Execution
- Job Submission:
- Authenticated client →
POST /jobswith job data - Backend → validates input → creates job record (status:
pending) → publishes SSE snapshot update
- Authenticated client →
- Worker Processing:
- Keeper → queries pending jobs → evaluates price conditions via price source
- On trigger → builds transaction via AutomationManager → signs and sends transaction
- Result Handling:
- On success → updates job to
executed→ creates execution record → publishes SSE job update - On failure/timeout → updates job to
failed/expired→ publishes update
- On success → updates job to
- Client Updates:
- SSE connection → receives job update → UI reflects new status/execution data
Administrative Pause
- Pause Request:
- Admin client →
POST /admin/pause(with valid JWT) - Backend → verifies admin → sets
paused=trueinsettingstable → returns new state
- Admin client →
- Worker Effect:
- Keeper → on next safety check → reads updated config → sees
globalPause=true - → skips job execution until resumed
- Keeper → on next safety check → reads updated config → sees
- Resume:
- Similar process via
POST /admin/resume→ setspaused=false
- Similar process via
Error Handling
- Client Errors (4xx):
400: Invalid input (malformed address, invalid numeric values, missing fields)401: Missing/invalid authentication (token, SIWE signature, nonce)403: Insufficient permissions (non-admin accessing admin endpoints)404: Resource not found (job ID not found or access denied)
- Server Errors (5xx):
502: External service failure (GeckoTerminal/API unreachable)500: Unexpected internal errors (logged but not exposed to client)
- All validation errors return structured
{ error: "descriptive message" }responses - Tests verify error cases for all endpoints (see
src/app.*.test.ts)
Extensibility Points
- New Job Types:
- Add to
JOB_TYPESset injob-validators.ts - Extend
parseCreateJobInputwith type-specific validation - Implement execution logic in
@cfxdevkit/automation/conditions - Update automation layer to handle new type in job processing
- Add to
- Admin Features:
- Add new endpoints to
src/routes/admin.tsfollowing existing patterns - Extend
SafetyConfiginterfaces in shared package if needed
- Add new endpoints to
- Data Sources:
- Modify
src/routes/pools.tsto add alternative pools providers - Adjust fallback logic in
pool-fallback.tsfor different networks
- Modify
- Authentication:
- Replace SIWE with alternative method by updating
auth.tsroutes - Maintain session verification interface in
session.ts
- Replace SIWE with alternative method by updating
Development and Deployment
Local Setup
- Environment:
- Copy
.env.exampleto.env(if provided) - Set required variables (see
config.tsfor defaults) - Critical:
CAS_AUTH_SECRETfor session signing
- Copy
- Execution:
npm run dev: Hot-reloaded development (tsx watch)npm run start: Production build execution- Defaults to port 3011 (configurable via
PORTorCAS_BACKEND_PORT)
- Database:
- SQLite file at path defined by
CAS_SQLITE_PATH - Defaults to
../../../.data/cas-dev.sqliterelative to dist - Use
:memory:for ephemeral testing (as in test helpers)
- SQLite file at path defined by
Testing
- Test Suite:
- Located in
src/app.*.test.ts - Uses Vitest and Supertest for endpoint testing
- Mocks external dependencies (GeckoTerminal, RPC calls) where needed
- Located in
- Execution:
npm run test: Runs all backend tests- Isolates tests with in-memory SQLite databases
- Resets global mocks and pools cache between tests
- Coverage:
- Auth flows (SIWE, nonce handling, admin checks)
- Job CRUD operations and permissions
- Admin controls (pause/resume, safety config)
- Pools data (testnet fallback, mainnet GeckoTerminal)
- System health endpoint
Production Considerations
For production deployment beyond local development:
- Database:
- Consider migrating from SQLite to PostgreSQL for concurrency
- Update database layer in
db/sqlite.ts(would require significant changes)
- Security:
- Use strong, randomly generated
CAS_AUTH_SECRET - Restrict
CAS_CORS_ORIGINSto specific domains - Enable HTTPS termination at reverse proxy
- Use strong, randomly generated
- Scaling:
- Worker instances should share database (SQLite not suitable for multi-instance)
- Consider separating worker from API server for resource isolation
- Monitoring:
- Extend
/system/statusendpoint with custom metrics - Integrate with logging infrastructure (current logs go to stdout)
- Extend
- Configuration:
- Use secure secret management for private keys and auth secret
- Adjust cache TTLs and polling intervals based on traffic patterns
The CAS backend provides a secure, extensible foundation for automated DeFi strategy execution on Conflux eSpace, with clear separation of concerns between API handling, data persistence, business logic, and background processing. Its design prioritizes local development usability while maintaining patterns suitable for production evolution.