feat(token-aggregation): reports, PMM quotes, config; Engine X flash vaults
- Expand token-aggregation API (report routes), canonical tokens, pools - Add flash vault contracts + tests (indexed, DODO cwUSDC, XAUT borrow) - PMM pools JSON, deploy/export scripts, metamask verified list Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import express, { Express, Request, Response, NextFunction } from 'express';
|
||||
import { Server } from 'http';
|
||||
import path from 'path';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import cors from 'cors';
|
||||
@@ -48,6 +49,7 @@ export class ApiServer {
|
||||
private indexerEnabled: boolean;
|
||||
private indexer: MultiChainIndexer | null;
|
||||
private omnlPoller: OmnlEventPoller | null;
|
||||
private server: Server | null;
|
||||
|
||||
private resolveTrustProxySetting(): boolean | number | string {
|
||||
const raw = (process.env.EXPRESS_TRUST_PROXY ?? process.env.TRUST_PROXY ?? '1').trim();
|
||||
@@ -65,6 +67,7 @@ export class ApiServer {
|
||||
this.indexerEnabled = this.resolveFeatureFlag('ENABLE_INDEXER', true);
|
||||
this.indexer = this.indexerEnabled ? new MultiChainIndexer() : null;
|
||||
this.omnlPoller = this.resolveFeatureFlag('ENABLE_OMNL_EVENT_POLLER', false) ? new OmnlEventPoller() : null;
|
||||
this.server = null;
|
||||
|
||||
this.setupMiddleware();
|
||||
this.setupRoutes();
|
||||
@@ -106,6 +109,14 @@ export class ApiServer {
|
||||
});
|
||||
next();
|
||||
});
|
||||
|
||||
const publicPath = path.join(__dirname, '../../public');
|
||||
if (existsSync(publicPath)) {
|
||||
this.app.use('/static', express.static(publicPath, {
|
||||
immutable: true,
|
||||
maxAge: '1d',
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private setupRoutes(): void {
|
||||
@@ -151,6 +162,39 @@ export class ApiServer {
|
||||
res.type('html').send(readFileSync(dashboardPath, 'utf8'));
|
||||
});
|
||||
|
||||
// Public API catalog (register before routers so GET /api/v1 is not swallowed by middleware-only mounts)
|
||||
const sendApiV1Catalog = (_req: Request, res: Response) => {
|
||||
res.json({
|
||||
service: 'token-aggregation',
|
||||
version: '1.0.0',
|
||||
note: 'Prefix paths with your public API origin (e.g. https://explorer.d-bis.org).',
|
||||
paths: {
|
||||
catalog: '/api/v1',
|
||||
health: '/health',
|
||||
chains: '/api/v1/chains',
|
||||
networks: '/api/v1/networks',
|
||||
config: '/api/v1/config',
|
||||
tokens: '/api/v1/tokens',
|
||||
tokenDetail: '/api/v1/tokens/{address}',
|
||||
quote: '/api/v1/quote',
|
||||
bridgeRoutes: '/api/v1/bridge/routes',
|
||||
bridgeStatus: '/api/v1/bridge/status',
|
||||
bridgeMetrics: '/api/v1/bridge/metrics',
|
||||
bridgePreflight: '/api/v1/bridge/preflight',
|
||||
tokenMappingPairs: '/api/v1/token-mapping/pairs',
|
||||
tokenMappingResolve: '/api/v1/token-mapping/resolve',
|
||||
reportTokenList: '/api/v1/report/token-list',
|
||||
routesTree: '/api/v1/routes/tree',
|
||||
plannerProvidersCapabilities: '/api/v2/providers/capabilities',
|
||||
plannerRoutesPlan: '/api/v2/routes/plan',
|
||||
plannerIntentsPlan: '/api/v2/intents/plan',
|
||||
plannerInternalExecutionPlan: '/api/v2/routes/internal-execution-plan',
|
||||
},
|
||||
});
|
||||
};
|
||||
this.app.get('/api/v1', sendApiV1Catalog);
|
||||
this.app.get('/api/v1/', sendApiV1Catalog);
|
||||
|
||||
// API routes
|
||||
this.app.use('/api/v1', tokenRoutes);
|
||||
this.app.use('/api/v1', configRoutes);
|
||||
@@ -206,21 +250,25 @@ export class ApiServer {
|
||||
|
||||
async start(): Promise<void> {
|
||||
try {
|
||||
// Start server
|
||||
this.server = this.app.listen(this.port, () => {
|
||||
logger.info(`Token Aggregation Service listening on port ${this.port}`);
|
||||
logger.info(`Health check: http://localhost:${this.port}/health`);
|
||||
logger.info(`API: http://localhost:${this.port}/api/v1`);
|
||||
});
|
||||
|
||||
if (this.indexer) {
|
||||
await this.indexer.initialize();
|
||||
await this.indexer.startAll();
|
||||
this.indexer
|
||||
.initialize()
|
||||
.then(() => this.indexer?.startAll())
|
||||
.catch((error) => {
|
||||
logger.error('Token aggregation indexer failed after API startup:', error);
|
||||
});
|
||||
} else {
|
||||
logger.info('Token aggregation indexer disabled by ENABLE_INDEXER flag');
|
||||
}
|
||||
|
||||
this.omnlPoller?.start();
|
||||
|
||||
// Start server
|
||||
this.app.listen(this.port, () => {
|
||||
logger.info(`Token Aggregation Service listening on port ${this.port}`);
|
||||
logger.info(`Health check: http://localhost:${this.port}/health`);
|
||||
logger.info(`API: http://localhost:${this.port}/api/v1`);
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to start server:', error);
|
||||
process.exit(1);
|
||||
@@ -230,6 +278,18 @@ export class ApiServer {
|
||||
async stop(): Promise<void> {
|
||||
this.omnlPoller?.stop();
|
||||
this.indexer?.stopAll();
|
||||
if (this.server) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.server?.close((error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
this.server = null;
|
||||
}
|
||||
logger.info('Server stopped');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user