Initial commit

This commit is contained in:
defiQUG
2026-01-01 08:04:06 -08:00
commit d0bc005be1
75 changed files with 15082 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# @dbis-thirdweb/http-api
HTTP API client wrapper for Chain 138 with retries, timeouts, and type-safe endpoints.
## Usage
### Basic Client
```typescript
import { createAPIClient } from '@dbis-thirdweb/http-api';
const client = createAPIClient({
apiKey: 'your-api-key',
timeout: 30000,
retries: 3,
});
// GET request
const data = await client.get('/endpoint');
// POST request
const result = await client.post('/endpoint', { data: 'value' });
```
### Chain 138 API Endpoints
```typescript
import { createChain138API } from '@dbis-thirdweb/http-api';
const api = createChain138API({
apiKey: 'your-api-key',
});
// Get chain metadata
const metadata = await api.getChainMetadata();
// Get transaction receipt
const receipt = await api.getTransactionReceipt('0x...');
// Get transaction logs
const logs = await api.getTransactionLogs('0x...');
// Get block
const block = await api.getBlock(12345);
// Get latest block
const latestBlock = await api.getLatestBlock();
// Send transaction (if supported)
const txResult = await api.sendTransaction({
to: '0x...',
value: '1000000000000000000',
});
```
## Features
- Standardized client wrapper with retry logic
- Exponential backoff for retries
- Configurable timeouts
- Chain 138 base URL configuration
- Type-safe request/response interfaces
- Automatic chain ID header injection

View File

@@ -0,0 +1,36 @@
{
"name": "@dbis-thirdweb/http-api",
"version": "0.1.0",
"description": "HTTP API client wrapper for Chain 138",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"lint": "eslint src",
"test": "echo \"No tests yet\""
},
"keywords": [
"thirdweb",
"http-api",
"chain-138",
"api-client"
],
"author": "",
"license": "MIT",
"dependencies": {
"@dbis-thirdweb/chain": "workspace:*"
},
"devDependencies": {
"@types/node": "^20.0.0",
"tsup": "^8.0.0",
"typescript": "^5.0.0"
}
}

View File

@@ -0,0 +1,175 @@
import { chain138 } from '@dbis-thirdweb/chain';
/**
* HTTP API client configuration
*/
export interface APIClientConfig {
/**
* Base URL for API requests
*/
baseURL: string;
/**
* API key (if required)
*/
apiKey?: string;
/**
* Request timeout in milliseconds
* @default 30000
*/
timeout?: number;
/**
* Number of retries for failed requests
* @default 3
*/
retries?: number;
/**
* Retry delay in milliseconds (exponential backoff base)
* @default 1000
*/
retryDelay?: number;
/**
* Chain ID (should be Chain 138)
*/
chainId: number;
}
/**
* Default configuration for Chain 138
*/
export const defaultConfig: APIClientConfig = {
baseURL: 'https://api.thirdweb.com',
timeout: 30000,
retries: 3,
retryDelay: 1000,
chainId: chain138.chainId,
};
/**
* Sleep utility for retry delays
*/
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* HTTP API client with retry logic and timeout handling
*/
export class APIClient {
private config: APIClientConfig;
constructor(config: Partial<APIClientConfig> = {}) {
this.config = { ...defaultConfig, ...config };
}
/**
* Make HTTP request with retries and timeout
*/
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${this.config.baseURL}${endpoint}`;
const timeout = this.config.timeout || 30000;
// Add API key to headers if provided
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string>),
};
if (this.config.apiKey) {
headers['Authorization'] = `Bearer ${this.config.apiKey}`;
}
// Add chain ID to headers if needed
headers['x-chain-id'] = this.config.chainId.toString();
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
let lastError: Error | null = null;
const maxRetries = this.config.retries || 3;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, {
...options,
headers,
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data as T;
} catch (error) {
clearTimeout(timeoutId);
lastError = error as Error;
// Don't retry on abort (timeout)
if (error instanceof Error && error.name === 'AbortError') {
throw new Error(`Request timeout after ${timeout}ms`);
}
// Don't retry on last attempt
if (attempt < maxRetries) {
const delay = this.config.retryDelay! * Math.pow(2, attempt); // Exponential backoff
await sleep(delay);
continue;
}
}
}
throw lastError || new Error('Request failed after retries');
}
/**
* GET request
*/
async get<T>(endpoint: string): Promise<T> {
return this.request<T>(endpoint, { method: 'GET' });
}
/**
* POST request
*/
async post<T>(endpoint: string, body?: unknown): Promise<T> {
return this.request<T>(endpoint, {
method: 'POST',
body: body ? JSON.stringify(body) : undefined,
});
}
/**
* PUT request
*/
async put<T>(endpoint: string, body?: unknown): Promise<T> {
return this.request<T>(endpoint, {
method: 'PUT',
body: body ? JSON.stringify(body) : undefined,
});
}
/**
* DELETE request
*/
async delete<T>(endpoint: string): Promise<T> {
return this.request<T>(endpoint, { method: 'DELETE' });
}
}
/**
* Create API client for Chain 138
*/
export function createAPIClient(config?: Partial<APIClientConfig>): APIClient {
return new APIClient(config);
}

View File

@@ -0,0 +1,119 @@
import { APIClient, createAPIClient } from './client';
/**
* Chain metadata response
*/
export interface ChainMetadata {
chainId: number;
name: string;
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
rpc: string[];
explorers?: Array<{
name: string;
url: string;
}>;
}
/**
* Transaction receipt response
*/
export interface TransactionReceipt {
blockHash: string;
blockNumber: number;
contractAddress: string | null;
cumulativeGasUsed: string;
effectiveGasPrice: string;
from: string;
gasUsed: string;
logs: Array<{
address: string;
topics: string[];
data: string;
}>;
logsBloom: string;
status: number;
to: string | null;
transactionHash: string;
transactionIndex: number;
}
/**
* Block response
*/
export interface BlockResponse {
number: number;
hash: string;
parentHash: string;
timestamp: number;
transactions: string[];
}
/**
* Chain 138 API endpoints wrapper
*/
export class Chain138API {
constructor(private client: APIClient) {}
/**
* Fetch chain metadata for Chain 138
*/
async getChainMetadata(): Promise<ChainMetadata> {
return this.client.get<ChainMetadata>('/v1/chains/138');
}
/**
* Get transaction receipt
*/
async getTransactionReceipt(txHash: string): Promise<TransactionReceipt> {
return this.client.get<TransactionReceipt>(`/v1/chains/138/transactions/${txHash}/receipt`);
}
/**
* Get transaction logs
*/
async getTransactionLogs(txHash: string): Promise<TransactionReceipt['logs']> {
const receipt = await this.getTransactionReceipt(txHash);
return receipt.logs;
}
/**
* Get block by number
*/
async getBlock(blockNumber: number): Promise<BlockResponse> {
return this.client.get<BlockResponse>(`/v1/chains/138/blocks/${blockNumber}`);
}
/**
* Get latest block
*/
async getLatestBlock(): Promise<BlockResponse> {
return this.client.get<BlockResponse>('/v1/chains/138/blocks/latest');
}
/**
* Send transaction via API (if supported)
*/
async sendTransaction(txData: {
to: string;
value?: string;
data?: string;
gasLimit?: string;
gasPrice?: string;
}): Promise<{ txHash: string }> {
return this.client.post<{ txHash: string }>('/v1/chains/138/transactions', txData);
}
}
/**
* Create Chain 138 API client
*/
export function createChain138API(
config?: Parameters<typeof createAPIClient>[0]
): Chain138API {
const client = createAPIClient(config);
return new Chain138API(client);
}

View File

@@ -0,0 +1,2 @@
export * from './client';
export * from './endpoints';

View File

@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"composite": false
},
"include": ["src/**/*"],
"references": [
{ "path": "../chain" }
]
}