Part of https://www.notion.so/Laconic-Mainnet-Plan-1eca6b22d47280569cd0d1e6d711d949 Co-authored-by: Shreerang Kale <shreerangkale@gmail.com> Reviewed-on: #1 Co-authored-by: shreerang <shreerang@noreply.git.vdb.to> Co-committed-by: shreerang <shreerang@noreply.git.vdb.to>
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 pathPublicNodeProvider
- No authHeliusProvider
- API key in query paramQuickNodeProvider
- Token in URL pathSolanaPublicProvider
- 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 1GB 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 1GB 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:
- Check cache first
- Get next available provider (round-robin)
- Try request
- On success: cache and return
- On failure: log error, mark provider failed, try next
- 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=1
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:
-
Test HTTP Proxy:
- Start proxy
- Make getBalance request
- Verify response format
- Verify cache hit on second request
-
Test WebSocket Proxy:
- Connect WebSocket
- Subscribe to account
- Verify subscription response
- Verify cached messages
-
Test Failover:
- Mock provider failure
- Verify failover to next provider
- Verify error logged with ID
-
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
- Cache Storage: Need ~1GB disk space
- Memory Usage: Keep minimal, use disk cache
- Concurrent Clients: Basic round-robin if multiple connect
- Monitoring: Log all errors, provide error IDs
- 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
- Single endpoint proxies to 5 providers
- Automatic failover works
- Responses are cached (up to 1GB)
- Errors logged with retrievable IDs
- Both HTTP and WebSocket work
- Response format is unified
- Happy-path tests pass