Initial commit
This commit is contained in:
175
packages/http-api/src/client.ts
Normal file
175
packages/http-api/src/client.ts
Normal 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);
|
||||
}
|
||||
119
packages/http-api/src/endpoints.ts
Normal file
119
packages/http-api/src/endpoints.ts
Normal 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);
|
||||
}
|
||||
2
packages/http-api/src/index.ts
Normal file
2
packages/http-api/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './client';
|
||||
export * from './endpoints';
|
||||
Reference in New Issue
Block a user