Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-10 11:32:49 -08:00
parent 4d4f8cedad
commit 903c03c65b
815 changed files with 125522 additions and 264 deletions

View File

@@ -0,0 +1,366 @@
# API Gateway Architecture Specification
## Overview
This document specifies the API Gateway architecture that provides unified edge API access, authentication, authorization, rate limiting, and request routing for all explorer platform services.
## Architecture
```mermaid
flowchart TB
subgraph Clients
Web[Web Clients]
Mobile[Mobile Apps]
API_Client[API Clients]
end
subgraph Gateway[API Gateway]
Router[Request Router]
Auth[Authentication]
RateLimit[Rate Limiter]
Transform[Request Transformer]
Cache[Response Cache]
LB[Load Balancer]
end
subgraph Services[Backend Services]
Explorer[Explorer API]
Search[Search Service]
Analytics[Analytics Service]
Actions[Action Service]
Banking[Banking API]
end
Clients --> Router
Router --> Auth
Auth --> RateLimit
RateLimit --> Transform
Transform --> Cache
Cache --> LB
LB --> Explorer
LB --> Search
LB --> Analytics
LB --> Actions
LB --> Banking
```
## Gateway Components
### Request Router
**Purpose**: Route requests to appropriate backend services based on path, method, and headers.
**Routing Rules**:
- `/api/v1/blocks/*` → Explorer API
- `/api/v1/transactions/*` → Explorer API
- `/api/v1/addresses/*` → Explorer API
- `/api/v1/search/*` → Search Service
- `/api/v1/analytics/*` → Analytics Service
- `/api/v1/swap/*` → Action Service
- `/api/v1/bridge/*` → Action Service
- `/api/v1/banking/*` → Banking API
- `/graphql` → Explorer API (GraphQL endpoint)
**Implementation**: NGINX, Kong, AWS API Gateway, or custom router
### Authentication
**Purpose**: Authenticate requests and extract user/API key information.
**Methods**:
1. **API Key Authentication**:
- Header: `X-API-Key: <key>`
- Query parameter: `?api_key=<key>` (less secure)
- Validate key hash against database
- Extract user_id and tier from key
2. **OAuth 2.0**:
- Bearer token: `Authorization: Bearer <token>`
- Validate JWT token
- Extract user claims
3. **Session Authentication**:
- Cookie-based for web clients
- Validate session token
**Anonymous Access**: Allow unauthenticated requests with lower rate limits
### Authorization
**Purpose**: Authorize requests based on user permissions and API key tier.
**Authorization Checks**:
- API key tier (free, pro, enterprise)
- User roles and permissions
- Resource-level permissions (user's own data)
- IP whitelist restrictions
**Enforcement**:
- Block unauthorized requests (403 Forbidden)
- Filter responses based on permissions
- Log authorization failures
### Rate Limiting
**Purpose**: Prevent abuse and ensure fair usage.
**Rate Limiting Strategy**:
**Tiers**:
1. **Anonymous**: 10 req/s, 100 req/min
2. **Free API Key**: 100 req/s, 1000 req/min
3. **Pro API Key**: 500 req/s, 5000 req/min
4. **Enterprise API Key**: 1000 req/s, unlimited
**Per-Endpoint Limits**:
- Simple queries (GET): Higher limits
- Complex queries (search, analytics): Lower limits
- Write operations (POST): Strict limits
**Implementation**:
- Token bucket algorithm
- Redis for distributed rate limiting
- Sliding window for smooth rate limiting
**Rate Limit Headers**:
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
```
**Response on Limit Exceeded**:
```json
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please try again later.",
"retry_after": 60
}
```
HTTP Status: 429 Too Many Requests
### Request Transformation
**Purpose**: Transform requests before forwarding to backend services.
**Transformations**:
- Add user context headers (user_id, tier)
- Normalize request format
- Add tracing headers (request_id, trace_id)
- Validate and sanitize input
- Add default parameters
**Example Headers Added**:
```
X-User-ID: <uuid>
X-API-Tier: pro
X-Request-ID: <uuid>
X-Trace-ID: <uuid>
```
### Response Caching
**Purpose**: Cache responses to reduce backend load and improve latency.
**Cacheable Endpoints**:
- Block data (cache for 10 seconds)
- Token metadata (cache for 1 hour)
- Contract ABIs (cache for 1 hour)
- Analytics aggregates (cache for 5 minutes)
**Cache Keys**:
- Include path, query parameters, API key tier
- Exclude user-specific parameters
**Cache Headers**:
- `Cache-Control: public, max-age=60`
- `ETag` for conditional requests
- `Vary: X-API-Key` to vary by tier
**Cache Invalidation**:
- Time-based expiration
- Manual invalidation on data updates
- Event-driven invalidation
### Load Balancing
**Purpose**: Distribute requests across backend service instances.
**Strategies**:
- Round-robin: Even distribution
- Least connections: Send to least loaded instance
- Health-aware: Skip unhealthy instances
**Health Checks**:
- HTTP health check endpoint: `/health`
- Check interval: 10 seconds
- Unhealthy threshold: 3 consecutive failures
- Recovery threshold: 2 successful checks
## Request/Response Flow
### Request Flow
1. **Client Request** → Gateway
2. **Routing** → Determine target service
3. **Authentication** → Validate credentials
4. **Authorization** → Check permissions
5. **Rate Limiting** → Check limits
6. **Cache Check** → Return cached if available
7. **Request Transformation** → Add headers, normalize
8. **Load Balancing** → Select backend instance
9. **Forward Request** → Send to backend service
10. **Response Handling** → Transform, cache, return
### Response Flow
1. **Backend Response** → Gateway
2. **Response Transformation** → Add headers, format
3. **Cache Storage** → Store if cacheable
4. **Error Handling** → Format errors consistently
5. **Response to Client** → Return response
## Error Handling
### Error Format
**Standard Error Response**:
```json
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please try again later.",
"details": {},
"request_id": "uuid",
"timestamp": "2024-01-01T00:00:00Z"
}
}
```
### Error Codes
- `400 Bad Request`: Invalid request
- `401 Unauthorized`: Authentication required
- `403 Forbidden`: Authorization failed
- `404 Not Found`: Resource not found
- `429 Too Many Requests`: Rate limit exceeded
- `500 Internal Server Error`: Server error
- `502 Bad Gateway`: Backend service unavailable
- `503 Service Unavailable`: Service temporarily unavailable
- `504 Gateway Timeout`: Backend timeout
## Security Considerations
### Request Validation
- Validate request size (max 10MB)
- Validate content-type
- Sanitize input to prevent injection
- Validate API key format
### DDoS Protection
- Rate limiting per IP
- Connection limits
- Request size limits
- WAF integration (Cloudflare)
### TLS/SSL
- Require HTTPS for all requests
- TLS 1.2+ only
- Strong cipher suites
- Certificate management
## Monitoring and Observability
### Metrics
**Key Metrics**:
- Request rate (requests/second)
- Response time (p50, p95, p99)
- Error rate (by error type)
- Cache hit rate
- Rate limit hits
- Backend service health
### Logging
**Log Fields**:
- Request ID
- User ID / API Key
- Method, path, query parameters
- Response status, latency
- Backend service called
- Error messages
**Log Level**: INFO for normal requests, ERROR for failures
### Tracing
- Add trace ID to requests
- Propagate trace ID to backend services
- Correlate requests across services
## Configuration
### Gateway Configuration
```yaml
gateway:
port: 443
health_check_path: /health
rate_limiting:
redis_url: "redis://..."
default_limit: 100
default_period: 60
cache:
redis_url: "redis://..."
default_ttl: 60
services:
explorer_api:
base_url: "http://explorer-api:8080"
health_check: "/health"
search_service:
base_url: "http://search-service:8080"
health_check: "/health"
```
## Implementation Options
### Option 1: NGINX + Lua
- High performance
- Custom logic with Lua
- Good caching support
### Option 2: Kong
- API Gateway features out of the box
- Plugin ecosystem
- Good rate limiting
### Option 3: AWS API Gateway
- Managed service
- Good integration with AWS services
- Cost considerations
### Option 4: Custom Gateway (Go/Node.js)
- Full control
- Custom features
- More maintenance
**Recommendation**: Start with Kong or NGINX, migrate to custom if needed.
## References
- REST API: See `rest-api.md`
- Authentication: See `../security/auth-spec.md`
- Rate Limiting: See `../security/ddos-protection.md`

View File

@@ -0,0 +1,322 @@
# Etherscan-Compatible API Specification
## Overview
This document specifies the Etherscan-compatible API layer that provides an API surface matching Blockscout/Etherscan APIs for tool compatibility and easier migration.
**Base URL**: `https://api.explorer.d-bis.org/api`
**Note**: This is a compatibility layer that translates Etherscan API calls to our internal API.
## API Surface Mapping
### Account Endpoints
#### Get Account Balance
**Etherscan**: `GET /api?module=account&action=balance&address={address}&tag=latest`
**Our Implementation**:
- Maps to: `GET /v1/addresses/{chain_id}/{address}` (extract balance)
**Response Format** (matches Etherscan):
```json
{
"status": "1",
"message": "OK",
"result": "1000000000000000000"
}
```
#### Get Token Balance
**Etherscan**: `GET /api?module=account&action=tokenbalance&contractaddress={token}&address={address}&tag=latest`
**Our Implementation**:
- Maps to: `GET /v1/addresses/{chain_id}/{address}/tokens` (filter by token address)
**Response Format**:
```json
{
"status": "1",
"message": "OK",
"result": "1000000000000000000"
}
```
#### Get Transaction List
**Etherscan**: `GET /api?module=account&action=txlist&address={address}&startblock=0&endblock=99999999&page=1&offset=10&sort=desc`
**Our Implementation**:
- Maps to: `GET /v1/addresses/{chain_id}/{address}/transactions` (with pagination)
**Response Format**:
```json
{
"status": "1",
"message": "OK",
"result": [
{
"blockNumber": "12345",
"timeStamp": "1640995200",
"hash": "0x...",
"from": "0x...",
"to": "0x...",
"value": "1000000000000000000",
"gas": "21000",
"gasPrice": "20000000000",
"gasUsed": "21000",
"isError": "0",
"txreceipt_status": "1"
}
]
}
```
### Transaction Endpoints
#### Get Transaction Status
**Etherscan**: `GET /api?module=transaction&action=getstatus&txhash={hash}`
**Our Implementation**:
- Maps to: `GET /v1/transactions/{chain_id}/{hash}` (extract status)
**Response Format**:
```json
{
"status": "1",
"message": "OK",
"result": {
"isError": "0",
"errDescription": ""
}
}
```
#### Get Transaction Receipt Status
**Etherscan**: `GET /api?module=transaction&action=gettxreceiptstatus&txhash={hash}`
**Response Format**:
```json
{
"status": "1",
"message": "OK",
"result": {
"status": "1"
}
}
```
### Block Endpoints
#### Get Block by Number
**Etherscan**: `GET /api?module=proxy&action=eth_getBlockByNumber&tag={number}&boolean=true`
**Our Implementation**:
- Maps to: `GET /v1/blocks/{chain_id}/{number}` (transform format)
**Response Format** (Ethereum JSON-RPC format):
```json
{
"jsonrpc": "2.0",
"result": {
"number": "0x3039",
"hash": "0x...",
"transactions": [...]
}
}
```
### Contract Endpoints
#### Get Contract ABI
**Etherscan**: `GET /api?module=contract&action=getabi&address={address}`
**Our Implementation**:
- Maps to: `GET /v1/contracts/{chain_id}/{address}/abi`
**Response Format**:
```json
{
"status": "1",
"message": "OK",
"result": "[{...}]"
}
```
#### Verify Contract
**Etherscan**: `POST /api?module=contract&action=verifysourcecode`
**Our Implementation**:
- Maps to: `POST /v1/contracts/{chain_id}/{address}/verify`
**Parameter Mapping**:
- `sourceCode``source_code`
- `codeformat``verification_method`
- `contractaddress``address`
- `compilerversion``compiler_version`
### Token Endpoints
#### Get Token Total Supply
**Etherscan**: `GET /api?module=stats&action=tokensupply&contractaddress={address}`
**Our Implementation**:
- Maps to: `GET /v1/tokens/{chain_id}/{address}` (extract total_supply)
### Event Logs
#### Get Event Logs
**Etherscan**: `GET /api?module=logs&action=getLogs&address={address}&topic0={topic0}&fromBlock={fromBlock}&toBlock={toBlock}`
**Our Implementation**:
- Maps to: `GET /v1/logs` (with filters)
**Response Format**:
```json
{
"status": "1",
"message": "OK",
"result": [
{
"address": "0x...",
"topics": ["0x..."],
"data": "0x...",
"blockNumber": "0x3039",
"transactionHash": "0x...",
"logIndex": "0x0"
}
]
}
```
## Parameter Translation
### Common Parameters
| Etherscan | Our API | Notes |
|-----------|---------|-------|
| `module` | N/A | Determines endpoint category |
| `action` | N/A | Determines specific action |
| `address` | `address` | Same |
| `tag` | `block_number` | Convert "latest" to current block |
| `startblock` | `from_block` | Same |
| `endblock` | `to_block` | Same |
| `page` | `page` | Same |
| `offset` | `page_size` | Same |
| `sort` | `order` | Same (asc/desc) |
### Chain ID Handling
**Etherscan**: No chain_id parameter (single chain)
**Our API**: Require `chain_id` parameter or use default from context
**Mapping**: Add `chain_id` parameter to all requests
## Response Format Compatibility
### Status Codes
**Etherscan Format**:
- `status: "1"` = Success
- `status: "0"` = Error
**Our Format**:
- HTTP 200 = Success (map to status "1")
- HTTP 4xx/5xx = Error (map to status "0")
### Error Messages
**Etherscan Format**:
```json
{
"status": "0",
"message": "NOTOK",
"result": "Error message here"
}
```
**Our Mapping**:
- Map our error codes to Etherscan error messages
- Preserve error details in result field
## Migration Guide
### For Tools Using Etherscan API
**Step 1**: Update base URL
- Old: `https://api.etherscan.io/api`
- New: `https://api.explorer.d-bis.org/api`
**Step 2**: Add chain_id parameter (if needed)
- Add `&chainid=138` to requests
- Or use default chain from connection
**Step 3**: Test compatibility
- Verify response formats match
- Check error handling
- Validate data accuracy
### For New Integrations
**Recommendation**: Use native REST API instead of Etherscan-compatible API
- More features
- Better performance
- Clearer API design
## Limitations
### Not All Endpoints Supported
**Supported**:
- Account operations
- Transaction queries
- Block queries
- Contract verification
- Token operations
- Event logs
**Not Supported** (Etherscan-specific):
- Gas tracker (use our analytics API)
- Historical stats (use our analytics API)
- Token list (use our token API)
### Response Format Differences
**Some differences may exist**:
- Field naming (camelCase vs snake_case)
- Data precision
- Additional fields in our responses
## Implementation
### Translation Layer
**Architecture**:
- Request interceptor: Parse Etherscan-style requests
- Parameter mapper: Convert to our API format
- Response transformer: Convert our responses to Etherscan format
**Code Structure**:
```
compatibility/
├── etherscan/
│ ├── request_parser.py # Parse Etherscan requests
│ ├── parameter_mapper.py # Map parameters
│ └── response_transformer.py # Transform responses
```
## References
- REST API: See `rest-api.md`
- API Gateway: See `api-gateway.md`

View File

@@ -0,0 +1,351 @@
# GraphQL API Specification
## Overview
This document specifies the GraphQL API for the explorer platform, providing flexible queries, mutations for user data, and subscriptions for real-time updates.
**Endpoint**: `https://api.explorer.d-bis.org/graphql`
## Schema Overview
### Core Types
#### Block
```graphql
type Block {
chainId: Int!
number: BigInt!
hash: String!
parentHash: String!
timestamp: DateTime!
miner: Address
transactionCount: Int!
gasUsed: BigInt!
gasLimit: BigInt!
transactions: [Transaction!]!
}
```
#### Transaction
```graphql
type Transaction {
chainId: Int!
hash: String!
block: Block
blockNumber: BigInt
from: Address!
to: Address
value: BigInt!
gasPrice: BigInt
gasUsed: BigInt
status: TransactionStatus!
logs: [Log!]!
traces: [Trace!]!
}
```
#### Address
```graphql
type Address {
address: String!
chainId: Int!
balance: BigInt!
balanceUSD: Float
transactionCount: Int!
transactions: TransactionConnection!
tokens: [TokenBalance!]!
nfts: [NFT!]!
label: String
tags: [String!]!
isContract: Boolean!
contract: Contract
}
```
#### Token
```graphql
type Token {
address: String!
chainId: Int!
name: String
symbol: String
type: TokenType!
decimals: Int
totalSupply: BigInt
holderCount: Int!
transfers: TokenTransferConnection!
holders: HolderConnection!
}
```
#### Contract
```graphql
type Contract {
address: String!
chainId: Int!
name: String
verificationStatus: VerificationStatus!
sourceCode: String
abi: JSON
compilerVersion: String
createdAt: DateTime
}
```
## Queries
### Block Queries
```graphql
query GetBlock($chainId: Int!, $number: BigInt!) {
block(chainId: $chainId, number: $number) {
number
hash
timestamp
transactionCount
transactions {
hash
from
to
value
}
}
}
```
### Transaction Queries
```graphql
query GetTransaction($chainId: Int!, $hash: String!) {
transaction(chainId: $chainId, hash: $hash) {
hash
from
to
value
status
logs {
address
topics
data
}
traces {
type
from
to
value
}
}
}
```
### Address Queries
```graphql
query GetAddress($chainId: Int!, $address: String!) {
address(chainId: $chainId, address: $address) {
address
balance
balanceUSD
transactionCount
transactions(first: 10) {
edges {
node {
hash
blockNumber
value
}
}
}
tokens {
token {
symbol
name
}
balance
}
}
}
```
### Search Query
```graphql
query Search($query: String!, $chainId: Int) {
search(query: $query, chainId: $chainId) {
... on Block {
number
hash
}
... on Transaction {
hash
from
to
}
... on Address {
address
label
}
... on Token {
address
symbol
name
}
}
}
```
## Mutations
### User Preferences
```graphql
mutation UpdateWatchlist($chainId: Int!, $address: String!, $label: String) {
addToWatchlist(chainId: $chainId, address: $address, label: $label) {
success
watchlistItem {
address
label
}
}
}
mutation RemoveFromWatchlist($chainId: Int!, $address: String!) {
removeFromWatchlist(chainId: $chainId, address: $address) {
success
}
}
```
### Address Labels
```graphql
mutation AddAddressLabel($chainId: Int!, $address: String!, $label: String!) {
addAddressLabel(chainId: $chainId, address: $address, label: $label) {
success
label {
address
label
}
}
}
```
## Subscriptions
### New Blocks
```graphql
subscription NewBlocks($chainId: Int!) {
newBlock(chainId: $chainId) {
number
hash
timestamp
transactionCount
}
}
```
### Address Transactions
```graphql
subscription AddressTransactions($chainId: Int!, $address: String!) {
addressTransaction(chainId: $chainId, address: $address) {
hash
from
to
value
status
}
}
```
### Pending Transactions
```graphql
subscription PendingTransactions($chainId: Int!) {
pendingTransaction(chainId: $chainId) {
hash
from
to
value
gasPrice
}
}
```
## Resolver Architecture
### DataLoader Pattern
**Purpose**: Prevent N+1 query problems.
**Implementation**:
- Batch load related entities
- Cache within request context
- Example: Batch load all blocks for transactions in a query
### Field Resolvers
**Strategy**: Lazy loading of related fields.
**Example**:
```javascript
Block: {
transactions: (block, args, context) => {
return context.loaders.transactions.load({
chainId: block.chainId,
blockNumber: block.number
});
}
}
```
## Performance Considerations
### Query Complexity
**Limits**:
- Max depth: 10 levels
- Max complexity score: 1000
- Rate limit based on complexity
### Caching
**Strategy**:
- Cache frequently accessed fields (blocks, tokens)
- Cache duration: 1-60 seconds depending on data freshness needs
- Invalidate on updates
## Authentication
Same as REST API: API Key via header or OAuth Bearer token.
## Error Handling
**GraphQL Error Format**:
```json
{
"errors": [
{
"message": "Resource not found",
"extensions": {
"code": "NOT_FOUND",
"requestId": "uuid"
},
"path": ["block"]
}
],
"data": null
}
```
## References
- REST API: See `rest-api.md`
- API Gateway: See `api-gateway.md`

399
docs/specs/api/rest-api.md Normal file
View File

@@ -0,0 +1,399 @@
# REST API Specification
## Overview
This document specifies the REST API for the explorer platform. The API follows RESTful principles and provides comprehensive access to blockchain data, search, and analytics.
**Base URL**: `https://api.explorer.d-bis.org/v1`
**Authentication**: API Key via `X-API-Key` header (optional for read-only endpoints with rate limits)
## API Design Principles
1. **RESTful**: Use HTTP methods appropriately (GET, POST, etc.)
2. **Consistent**: Uniform response format
3. **Versioned**: `/v1` in path, version in response headers
4. **Paginated**: All list endpoints support pagination
5. **Filtered**: Support filtering and sorting
6. **Documented**: OpenAPI 3.0 specification
## Response Format
### Success Response
```json
{
"data": { ... },
"meta": {
"pagination": {
"page": 1,
"page_size": 20,
"total": 100,
"total_pages": 5
}
}
}
```
### Error Response
```json
{
"error": {
"code": "not_found",
"message": "Resource not found",
"details": {},
"request_id": "uuid"
}
}
```
## Endpoints
### Blocks
#### List Blocks
`GET /blocks`
**Query Parameters**:
- `chain_id` (integer, required): Chain ID
- `page` (integer, default: 1): Page number
- `page_size` (integer, default: 20, max: 100): Items per page
- `min_block` (integer): Minimum block number
- `max_block` (integer): Maximum block number
- `miner` (string): Filter by miner address
- `sort` (string, default: "number"): Sort field (number, timestamp, gas_used)
- `order` (string, default: "desc"): Sort order (asc, desc)
**Response**:
```json
{
"data": [
{
"chain_id": 138,
"number": 12345,
"hash": "0x...",
"timestamp": "2024-01-01T00:00:00Z",
"miner": "0x...",
"transaction_count": 100,
"gas_used": 15000000,
"gas_limit": 20000000
}
],
"meta": { "pagination": {...} }
}
```
#### Get Block by Number
`GET /blocks/{chain_id}/{number}`
**Response**: Single block object
#### Get Block by Hash
`GET /blocks/{chain_id}/hash/{hash}`
**Response**: Single block object
### Transactions
#### List Transactions
`GET /transactions`
**Query Parameters**:
- `chain_id` (integer, required)
- `block_number` (integer): Filter by block
- `from_address` (string): Filter by sender
- `to_address` (string): Filter by recipient
- `status` (string): Filter by status (success, failed)
- `min_value` (string): Minimum value
- `page`, `page_size`, `sort`, `order`
**Response**: Array of transaction objects
#### Get Transaction by Hash
`GET /transactions/{chain_id}/{hash}`
**Response**: Single transaction object with full details
**Includes**:
- Transaction data
- Receipt data
- Logs
- Traces (if available)
#### Get Transaction Receipt
`GET /transactions/{chain_id}/{hash}/receipt`
**Response**: Transaction receipt object
### Addresses
#### Get Address Info
`GET /addresses/{chain_id}/{address}`
**Response**:
```json
{
"address": "0x...",
"chain_id": 138,
"balance": "1000000000000000000",
"balance_usd": "3000.00",
"transaction_count": 500,
"token_count": 10,
"label": "My Wallet",
"tags": ["wallet"],
"is_contract": false
}
```
#### Get Address Transactions
`GET /addresses/{chain_id}/{address}/transactions`
**Query Parameters**: Standard pagination and filtering
**Response**: Array of transactions
#### Get Address Token Holdings
`GET /addresses/{chain_id}/{address}/tokens`
**Query Parameters**:
- `type` (string): Filter by token type (ERC20, ERC721, ERC1155)
- `page`, `page_size`
**Response**: Array of token holdings with balances
#### Get Address NFTs
`GET /addresses/{chain_id}/{address}/nfts`
**Response**: Array of NFT holdings (ERC-721/1155)
### Tokens
#### List Tokens
`GET /tokens`
**Query Parameters**:
- `chain_id` (integer, required)
- `type` (string): Filter by type
- `verified` (boolean): Filter verified tokens
- `search` (string): Search by name or symbol
- `page`, `page_size`, `sort`, `order`
**Response**: Array of token objects
#### Get Token Info
`GET /tokens/{chain_id}/{address}`
**Response**: Token details including metadata
#### Get Token Holders
`GET /tokens/{chain_id}/{address}/holders`
**Query Parameters**: Pagination
**Response**: Array of holder addresses with balances
#### Get Token Transfers
`GET /tokens/{chain_id}/{address}/transfers`
**Query Parameters**: Pagination, `from_address`, `to_address`
**Response**: Array of token transfers
### Contracts
#### Get Contract Info
`GET /contracts/{chain_id}/{address}`
**Response**: Contract details including verification status, source code, ABI
#### Verify Contract
`POST /contracts/{chain_id}/{address}/verify`
**Request Body**:
```json
{
"compiler_version": "0.8.19",
"optimization_enabled": true,
"optimization_runs": 200,
"source_code": "...",
"constructor_arguments": "0x...",
"verification_method": "standard_json"
}
```
**Response**: Verification status
#### Get Contract Source Code
`GET /contracts/{chain_id}/{address}/source`
**Response**: Source code and metadata
#### Get Contract ABI
`GET /contracts/{chain_id}/{address}/abi`
**Response**: Contract ABI JSON
### Logs
#### Get Logs
`GET /logs`
**Query Parameters**:
- `chain_id` (integer, required)
- `address` (string): Filter by contract address
- `topic0` (string): Filter by event signature
- `from_block` (integer): Start block
- `to_block` (integer): End block
- `page`, `page_size`
**Response**: Array of log objects
### Traces
#### Get Transaction Traces
`GET /transactions/{chain_id}/{hash}/traces`
**Response**: Array of trace objects
#### Get Block Traces
`GET /blocks/{chain_id}/{number}/traces`
**Response**: Array of trace objects for all transactions in block
### Search
#### Unified Search
`GET /search`
**Query Parameters**:
- `chain_id` (integer, optional): Filter by chain
- `q` (string, required): Search query
- `type` (string): Filter by type (block, transaction, address, token, contract)
- `page`, `page_size`
**Response**: Mixed array of results with type indicators
### Analytics
#### Network Stats
`GET /analytics/{chain_id}/network/stats`
**Response**:
```json
{
"current_block": 12345,
"tps": 15.5,
"gps": 30000000,
"avg_gas_price": 20000000000,
"pending_transactions": 50
}
```
#### Gas Price Statistics
`GET /analytics/{chain_id}/gas/price`
**Query Parameters**:
- `period` (string): Time period (1h, 24h, 7d, 30d)
**Response**: Gas price statistics (min, max, avg, percentiles)
#### Top Contracts
`GET /analytics/{chain_id}/contracts/top`
**Query Parameters**:
- `by` (string): Sort by (transactions, gas_used, value)
- `limit` (integer, default: 100)
**Response**: Array of top contracts
## Pagination
All list endpoints support pagination:
**Query Parameters**:
- `page` (integer, default: 1): Page number (1-indexed)
- `page_size` (integer, default: 20, max: 100): Items per page
**Response Metadata**:
```json
{
"meta": {
"pagination": {
"page": 1,
"page_size": 20,
"total": 500,
"total_pages": 25,
"has_next": true,
"has_prev": false
}
}
}
```
## Filtering and Sorting
**Common Filter Parameters**:
- Date ranges: `from_date`, `to_date`
- Block ranges: `from_block`, `to_block`
- Value ranges: `min_value`, `max_value`
- Address filters: `from_address`, `to_address`
**Sorting**:
- `sort` (string): Field to sort by
- `order` (string): `asc` or `desc`
## Rate Limiting
See API Gateway specification for rate limiting details.
**Rate Limit Headers**:
- `X-RateLimit-Limit`: Request limit
- `X-RateLimit-Remaining`: Remaining requests
- `X-RateLimit-Reset`: Reset timestamp
## OpenAPI Specification
Full OpenAPI 3.0 specification available at:
- `/api-docs/openapi.json`
- Interactive docs: `/api-docs` (Swagger UI)
## Versioning
**Current Version**: v1
**Version Header**: `API-Version: 1`
**Deprecation**: Deprecated endpoints return `Deprecation` header with deprecation date
## References
- API Gateway: See `api-gateway.md`
- GraphQL API: See `graphql-api.md`
- Etherscan-Compatible API: See `etherscan-compatible-api.md`

View File

@@ -0,0 +1,337 @@
# WebSocket API Specification
## Overview
This document specifies the WebSocket API for real-time blockchain data subscriptions, including new blocks, pending transactions, and address activity.
**Endpoint**: `wss://api.explorer.d-bis.org/ws`
## Connection Lifecycle
### Connection
**URL**: `wss://api.explorer.d-bis.org/ws?chain_id=138`
**Query Parameters**:
- `chain_id` (optional): Default chain ID for subscriptions
- `api_key` (optional): API key for authenticated connections
**Headers**:
- `Authorization: Bearer <token>` (optional)
### Authentication
**Methods**:
1. Query parameter: `?api_key=<key>`
2. Header: `Authorization: Bearer <token>`
3. Message: Send auth message after connection
**Auth Message**:
```json
{
"type": "auth",
"api_key": "your-api-key"
}
```
**Response**:
```json
{
"type": "auth_success",
"user_id": "uuid",
"tier": "pro"
}
```
### Heartbeat
**Purpose**: Keep connection alive and detect disconnections.
**Client Ping**: Send every 30 seconds
```json
{
"type": "ping"
}
```
**Server Pong**: Response within 1 second
```json
{
"type": "pong",
"timestamp": 1640995200
}
```
**Timeout**: Connection closed if no pong received within 60 seconds
## Subscription Model
### Subscribe to New Blocks
**Request**:
```json
{
"type": "subscribe",
"channel": "blocks",
"chain_id": 138,
"params": {
"include_transactions": false
}
}
```
**Response**:
```json
{
"type": "subscribed",
"subscription_id": "sub_123",
"channel": "blocks"
}
```
**Updates**:
```json
{
"type": "update",
"subscription_id": "sub_123",
"data": {
"number": 12345,
"hash": "0x...",
"timestamp": "2024-01-01T00:00:00Z",
"transaction_count": 100
}
}
```
### Subscribe to Pending Transactions
**Request**:
```json
{
"type": "subscribe",
"channel": "pending_transactions",
"chain_id": 138,
"params": {
"from_address": "0x...", // Optional filter
"min_value": "1000000000000000000" // Optional filter
}
}
```
**Updates**: Transaction objects as they enter mempool
### Subscribe to Address Activity
**Request**:
```json
{
"type": "subscribe",
"channel": "address",
"chain_id": 138,
"params": {
"address": "0x..."
}
}
```
**Updates**: Transactions involving the address
### Subscribe to Logs/Events
**Request**:
```json
{
"type": "subscribe",
"channel": "logs",
"chain_id": 138,
"params": {
"address": "0x...",
"topics": ["0xddf252..."] // Event signatures
}
}
```
**Updates**: Matching log entries
### Unsubscribe
**Request**:
```json
{
"type": "unsubscribe",
"subscription_id": "sub_123"
}
```
**Response**:
```json
{
"type": "unsubscribed",
"subscription_id": "sub_123"
}
```
## Message Formats
### Client Messages
**Subscribe**:
```json
{
"type": "subscribe",
"channel": "<channel_name>",
"chain_id": 138,
"params": { ... }
}
```
**Unsubscribe**:
```json
{
"type": "unsubscribe",
"subscription_id": "<id>"
}
```
**Ping**:
```json
{
"type": "ping"
}
```
### Server Messages
**Update**:
```json
{
"type": "update",
"subscription_id": "<id>",
"data": { ... }
}
```
**Error**:
```json
{
"type": "error",
"code": "invalid_subscription",
"message": "Invalid subscription parameters",
"subscription_id": "<id>"
}
```
**Notification**:
```json
{
"type": "notification",
"level": "info", // info, warning, error
"message": "Subscription limit reached"
}
```
## Subscription Limits
**Per Connection**:
- Anonymous: 5 subscriptions
- Free API Key: 20 subscriptions
- Pro API Key: 100 subscriptions
- Enterprise: Unlimited
**Per Chain**:
- Limit subscriptions per chain to prevent abuse
## Reconnection Strategy
### Automatic Reconnection
**Strategy**: Exponential backoff
**Backoff Schedule**:
- Initial: 1 second
- Max: 60 seconds
- Multiplier: 2x
**Behavior**:
- Reconnect automatically
- Resubscribe to all active subscriptions
- Resume from last received update
### Manual Reconnection
**Steps**:
1. Close connection gracefully
2. Reconnect with same parameters
3. Resubscribe to desired channels
## Rate Limiting
**Limits**:
- Message rate: 10 messages/second per connection
- Subscription rate: 5 subscriptions/minute per connection
- Update rate: Throttled per subscription type
**Throttling**: Server may batch or skip updates if client cannot keep up
## Error Codes
- `invalid_message`: Malformed message
- `invalid_subscription`: Invalid subscription parameters
- `subscription_limit`: Too many subscriptions
- `rate_limit`: Rate limit exceeded
- `unauthorized`: Authentication required
- `chain_not_supported`: Chain ID not supported
- `internal_error`: Server error
## Security Considerations
### Connection Security
- WSS (WebSocket Secure) required
- TLS 1.2+ only
- Certificate validation
### Authentication
- API key validation
- User permission checks
- Subscription authorization
### Message Validation
- Validate all incoming messages
- Sanitize parameters
- Reject invalid requests
## Performance Considerations
### Message Batching
**Strategy**: Batch multiple updates into single message when possible
**Example**:
```json
{
"type": "batch_update",
"updates": [
{ "subscription_id": "sub_1", "data": {...} },
{ "subscription_id": "sub_2", "data": {...} }
]
}
```
### Backpressure
**Strategy**: Drop updates if client cannot process them
**Indicators**:
- Client buffer full
- No ping/pong responses
- Slow message processing
## References
- REST API: See `rest-api.md`
- GraphQL API: See `graphql-api.md`
- Mempool Service: See `../mempool/mempool-service.md`