Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
322
docs/specs/api/etherscan-compatible-api.md
Normal file
322
docs/specs/api/etherscan-compatible-api.md
Normal 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`
|
||||
|
||||
Reference in New Issue
Block a user