solana-proxy/docs/solana-proxy-implementation-plan.md
2025-07-15 21:16:36 -04:00

7.1 KiB

Solana RPC Proxy Implementation Plan

Project Overview

A Python-based reverse proxy for Solana RPC endpoints that provides unified access to multiple free providers with automatic failover, response caching, and detailed error tracking.

Architecture Components

1. Provider Module (providers.py)

Purpose: Abstract provider differences and handle authentication

Provider class:
- name: str
- http_url: str  
- ws_url: str
- transform_request(request) -> request
- transform_response(response) -> response
- is_available() -> bool
- mark_failed() -> None
- backoff_until: datetime

Provider Implementations:

  • AlchemyProvider - API key in URL path
  • PublicNodeProvider - No auth
  • HeliusProvider - API key in query param
  • QuickNodeProvider - Token in URL path
  • SolanaPublicProvider - No auth

2. Cache Module (cache.py)

Purpose: Disk-based caching for HTTP and WebSocket responses

Cache class:
- get(method: str, params: dict) -> Optional[response]
- set(method: str, params: dict, response: dict) -> None
- size_check() -> None  # Enforce 100GB limit
- clear_oldest() -> None  # LRU eviction

Implementation Notes:

  • Use diskcache library for simplicity
  • Key format: f"{method}:{json.dumps(params, sort_keys=True)}"
  • Store both HTTP responses and WebSocket messages
  • Implement 100GB limit with LRU eviction

3. Error Logger Module (errors.py)

Purpose: SQLite-based error logging with UUID tracking

ErrorLogger class:
- log_error(provider: str, request: dict, error: Exception) -> str (UUID)
- get_error(error_id: str) -> Optional[dict]
- setup_db() -> None

Database Schema:

CREATE TABLE errors (
    id TEXT PRIMARY KEY,  -- UUID
    timestamp DATETIME,
    provider TEXT,
    request_method TEXT,
    request_params TEXT,  -- JSON
    error_type TEXT,
    error_message TEXT,
    error_traceback TEXT
);

4. Response Normalizer Module (normalizer.py)

Purpose: Handle minor provider response differences

normalize_response(provider: str, response: dict) -> dict
normalize_error(error: Exception, error_id: str) -> dict

Normalization Tasks:

  • Ensure consistent field names
  • Handle null vs missing fields
  • Standardize error formats
  • Add proxy metadata (cached, provider used)

5. Request Router Module (router.py)

Purpose: Core failover and routing logic

Router class:
- providers: List[Provider]
- cache: Cache
- error_logger: ErrorLogger
- 
- route_request(method: str, params: dict) -> response
- get_available_provider() -> Optional[Provider]
- mark_provider_failed(provider: Provider) -> None

Failover Algorithm:

  1. Check cache first
  2. Get next available provider (round-robin)
  3. Try request
  4. On success: cache and return
  5. On failure: log error, mark provider failed, try next
  6. All failed: return error with ID

6. HTTP Proxy Module (http_proxy.py)

Purpose: aiohttp server for HTTP JSON-RPC

- setup_routes(app: aiohttp.Application)
- handle_rpc_request(request: aiohttp.Request) -> aiohttp.Response

7. WebSocket Proxy Module (ws_proxy.py)

Purpose: WebSocket subscription handling

- handle_ws_connection(request: aiohttp.Request) -> aiohttp.WebSocketResponse
- proxy_ws_messages(client_ws, provider_ws, cache, provider_name)

WebSocket Complexity:

  • Maintain subscription ID mapping
  • Cache subscription responses
  • Handle reconnection on provider failure

8. Main Application (main.py)

Purpose: Wire everything together

- load_config() -> dict  # From .env
- setup_providers(config) -> List[Provider]
- create_app() -> aiohttp.Application
- main() -> None

Configuration (.env)

# Provider endpoints and auth
ALCHEMY_API_KEY=your_key_here
HELIUS_API_KEY=your_key_here
QUICKNODE_ENDPOINT=your_endpoint.quiknode.pro
QUICKNODE_TOKEN=your_token_here

# Proxy settings
PROXY_PORT=8545
CACHE_SIZE_GB=100
BACKOFF_MINUTES=30

# Logging
LOG_LEVEL=INFO
ERROR_DB_PATH=./errors.db

Rate Limits Documentation

# providers.py comments

# Alchemy (Source: https://docs.alchemy.com/reference/throughput)
# Free tier: 330 CUPs (Compute Units per Second)
# WebSocket: 10 concurrent requests per connection

# PublicNode (Source: https://publicnode.com)
# No published rate limits - "completely free"

# Helius (Source: https://docs.helius.dev/pricing)
# Free tier: 10 requests/second
# 1M credits per month

# QuickNode (Source: https://www.quicknode.com/pricing)
# Free tier: 10M credits/month
# WebSocket: 50 credits per response

# Solana Public (Source: https://solana.com/docs/cluster/rpc-endpoints)
# Rate limits subject to change without notice
# Not intended for production use

Testing Strategy (test_e2e.py)

Happy-path end-to-end tests only:

  1. Test HTTP Proxy:

    • Start proxy
    • Make getBalance request
    • Verify response format
    • Verify cache hit on second request
  2. Test WebSocket Proxy:

    • Connect WebSocket
    • Subscribe to account
    • Verify subscription response
    • Verify cached messages
  3. Test Failover:

    • Mock provider failure
    • Verify failover to next provider
    • Verify error logged with ID
  4. Test All Providers:

    • Iterate through each provider
    • Verify basic request works
    • Verify auth handled correctly

Implementation Notes

Functional Programming Style

  • Use pure functions where possible
  • Avoid class state mutations
  • Use immutable data structures
  • Compose small functions

KISS Principles

  • No complex health checking (just try request)
  • No credit tracking (let providers handle)
  • Simple round-robin selection
  • Basic LRU cache eviction

DRY Principles

  • Single Provider base class
  • Reuse request/response transformation
  • Common error handling flow
  • Shared cache logic for HTTP/WS

Deployment Considerations

  1. Cache Storage: Need ~100GB disk space
  2. Memory Usage: Keep minimal, use disk cache
  3. Concurrent Clients: Basic round-robin if multiple connect
  4. Monitoring: Log all errors, provide error IDs
  5. Security: Keep API keys in .env, never log them

Future Enhancements (Out of Scope)

  • Credit/quota tracking
  • Advanced health checking
  • Response time optimization
  • Geographic routing
  • Analytics dashboard
  • Webhook error notifications

File Structure

solana-proxy/
├── .env                 # Configuration
├── .env.example         # Template
├── requirements.txt     # Dependencies
├── main.py             # Entry point
├── providers.py        # Provider abstractions
├── cache.py            # Caching logic
├── errors.py           # Error logging
├── normalizer.py       # Response normalization
├── router.py           # Request routing
├── http_proxy.py       # HTTP handler
├── ws_proxy.py         # WebSocket handler
└── test_e2e.py         # End-to-end tests

Dependencies

aiohttp==3.9.0
python-dotenv==1.0.0
diskcache==5.6.0
aiohttp-cors==0.7.0

Success Criteria

  1. Single endpoint proxies to 5 providers
  2. Automatic failover works
  3. Responses are cached (up to 100GB)
  4. Errors logged with retrievable IDs
  5. Both HTTP and WebSocket work
  6. Response format is unified
  7. Happy-path tests pass