Add live route matrix and stable bridge decision flows
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m25s
CI/CD Pipeline / Lint and Format (push) Has been cancelled
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
Deploy ChainID 138 / Deploy ChainID 138 (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled
CI/CD Pipeline / Security Scanning (push) Successful in 13m49s
Verify Deployment / Verify Deployment (push) Failing after 1m22s
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m25s
CI/CD Pipeline / Lint and Format (push) Has been cancelled
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
Deploy ChainID 138 / Deploy ChainID 138 (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled
CI/CD Pipeline / Security Scanning (push) Successful in 13m49s
Verify Deployment / Verify Deployment (push) Failing after 1m22s
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { cacheMiddleware } from '../middleware/cache';
|
||||
import {
|
||||
AggregatorRouteFilters,
|
||||
filterLiveAggregatorRoutes,
|
||||
getAggregatorRouteMatrixPath,
|
||||
loadAggregatorRouteMatrix,
|
||||
} from '../../config/aggregator-route-matrix';
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
function parseFilters(req: Request): AggregatorRouteFilters {
|
||||
const fromChainId = req.query.fromChainId ? parseInt(String(req.query.fromChainId), 10) : undefined;
|
||||
const toChainId = req.query.toChainId ? parseInt(String(req.query.toChainId), 10) : undefined;
|
||||
|
||||
return {
|
||||
family: req.query.family ? String(req.query.family) : undefined,
|
||||
fromChainId: Number.isFinite(fromChainId) ? fromChainId : undefined,
|
||||
toChainId: Number.isFinite(toChainId) ? toChainId : undefined,
|
||||
routeType: req.query.routeType ? String(req.query.routeType) : undefined,
|
||||
tokenIn: req.query.tokenIn ? String(req.query.tokenIn) : undefined,
|
||||
tokenOut: req.query.tokenOut ? String(req.query.tokenOut) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/routes/matrix
|
||||
* Returns the canonical aggregator route matrix from config/aggregator-route-matrix.json.
|
||||
*/
|
||||
router.get('/routes/matrix', cacheMiddleware(30 * 1000), (req: Request, res: Response) => {
|
||||
const matrix = loadAggregatorRouteMatrix();
|
||||
if (!matrix) {
|
||||
return res.status(503).json({
|
||||
error: 'Aggregator route matrix not available',
|
||||
});
|
||||
}
|
||||
|
||||
const filters = parseFilters(req);
|
||||
const includeNonLive = String(req.query.includeNonLive ?? 'false').toLowerCase() === 'true';
|
||||
const liveRoutes = filterLiveAggregatorRoutes(
|
||||
[...matrix.liveSwapRoutes, ...matrix.liveBridgeRoutes],
|
||||
filters
|
||||
);
|
||||
|
||||
return res.json({
|
||||
generatedAt: new Date().toISOString(),
|
||||
sourcePath: getAggregatorRouteMatrixPath(),
|
||||
version: matrix.version,
|
||||
updated: matrix.updated,
|
||||
filters,
|
||||
homeChainId: matrix.homeChainId,
|
||||
liveRoutes,
|
||||
blockedOrPlannedRoutes: includeNonLive ? matrix.blockedOrPlannedRoutes : undefined,
|
||||
counts: {
|
||||
liveSwapRoutes: matrix.liveSwapRoutes.length,
|
||||
liveBridgeRoutes: matrix.liveBridgeRoutes.length,
|
||||
blockedOrPlannedRoutes: matrix.blockedOrPlannedRoutes.length,
|
||||
filteredLiveRoutes: liveRoutes.length,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/v1/routes/ingestion
|
||||
* Adapter-focused flat export of only live routes, filtered by family/chain/token when provided.
|
||||
*/
|
||||
router.get('/routes/ingestion', cacheMiddleware(30 * 1000), (req: Request, res: Response) => {
|
||||
const matrix = loadAggregatorRouteMatrix();
|
||||
if (!matrix) {
|
||||
return res.status(503).json({
|
||||
error: 'Aggregator route matrix not available',
|
||||
});
|
||||
}
|
||||
|
||||
const filters = parseFilters(req);
|
||||
const liveRoutes = filterLiveAggregatorRoutes(
|
||||
[...matrix.liveSwapRoutes, ...matrix.liveBridgeRoutes],
|
||||
filters
|
||||
);
|
||||
|
||||
return res.json({
|
||||
generatedAt: new Date().toISOString(),
|
||||
format: 'aggregator-ingestion-v1',
|
||||
version: matrix.version,
|
||||
updated: matrix.updated,
|
||||
filters,
|
||||
routes: liveRoutes,
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
319
services/token-aggregation/src/api/routes/partner-payloads.ts
Normal file
319
services/token-aggregation/src/api/routes/partner-payloads.ts
Normal file
@@ -0,0 +1,319 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { cacheMiddleware } from '../middleware/cache';
|
||||
import {
|
||||
filterLiveAggregatorRoutes,
|
||||
loadAggregatorRouteMatrix,
|
||||
} from '../../config/aggregator-route-matrix';
|
||||
import {
|
||||
buildPartnerPayload,
|
||||
PartnerName,
|
||||
} from '../../services/partner-payload-adapters';
|
||||
import { dispatchPartnerPayload } from '../../services/partner-payload-dispatcher';
|
||||
import { buildInternalExecutionPlan } from '../../services/internal-execution-plan';
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
interface PartnerPayloadRequestBody {
|
||||
partner?: string;
|
||||
amount?: string;
|
||||
fromChainId?: number;
|
||||
toChainId?: number;
|
||||
routeType?: string;
|
||||
tokenIn?: string;
|
||||
tokenOut?: string;
|
||||
takerAddress?: string;
|
||||
fromAddress?: string;
|
||||
toAddress?: string;
|
||||
recipient?: string;
|
||||
slippagePercent?: string;
|
||||
slippageBps?: string;
|
||||
includeUnsupported?: boolean;
|
||||
routeId?: string;
|
||||
}
|
||||
|
||||
function normalizePartner(input: string | undefined): PartnerName | null {
|
||||
if (!input) return null;
|
||||
const value = input.trim().toLowerCase();
|
||||
if (value === '1inch') return '1inch';
|
||||
if (value === '0x' || value === 'zeroex') return '0x';
|
||||
if (value === 'lifi') return 'LiFi';
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildPayloads(args: {
|
||||
partner: PartnerName;
|
||||
amount: string;
|
||||
fromChainId?: number;
|
||||
toChainId?: number;
|
||||
routeType?: string;
|
||||
tokenIn?: string;
|
||||
tokenOut?: string;
|
||||
takerAddress?: string;
|
||||
fromAddress?: string;
|
||||
toAddress?: string;
|
||||
recipient?: string;
|
||||
slippagePercent?: string;
|
||||
slippageBps?: string;
|
||||
includeUnsupported?: boolean;
|
||||
}) {
|
||||
const matrix = loadAggregatorRouteMatrix();
|
||||
if (!matrix) {
|
||||
return {
|
||||
error: {
|
||||
status: 503,
|
||||
body: {
|
||||
error: 'Aggregator route matrix not available',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const liveRoutes = filterLiveAggregatorRoutes(
|
||||
[...matrix.liveSwapRoutes, ...matrix.liveBridgeRoutes],
|
||||
{
|
||||
fromChainId: args.fromChainId,
|
||||
toChainId: args.toChainId,
|
||||
routeType: args.routeType,
|
||||
tokenIn: args.tokenIn,
|
||||
tokenOut: args.tokenOut,
|
||||
}
|
||||
);
|
||||
|
||||
const payloads = liveRoutes.map((route) =>
|
||||
buildPartnerPayload(args.partner, route, {
|
||||
amount: args.amount,
|
||||
takerAddress: args.takerAddress,
|
||||
fromAddress: args.fromAddress,
|
||||
toAddress: args.toAddress,
|
||||
recipient: args.recipient,
|
||||
slippagePercent: args.slippagePercent,
|
||||
slippageBps: args.slippageBps,
|
||||
})
|
||||
);
|
||||
|
||||
const filteredPayloads = args.includeUnsupported ? payloads : payloads.filter((payload) => payload.supported);
|
||||
|
||||
return {
|
||||
result: {
|
||||
generatedAt: new Date().toISOString(),
|
||||
format: 'partner-payload-templates-v1',
|
||||
partner: args.partner,
|
||||
amount: args.amount,
|
||||
count: filteredPayloads.length,
|
||||
supportedCount: payloads.filter((payload) => payload.supported).length,
|
||||
payloads: filteredPayloads,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/routes/partner-payloads
|
||||
* Returns partner-specific request payload templates generated from live ingestion routes.
|
||||
* By default returns only supported payloads; pass includeUnsupported=true to inspect all templates.
|
||||
*/
|
||||
router.get('/routes/partner-payloads', cacheMiddleware(30 * 1000), (req: Request, res: Response) => {
|
||||
const partner = normalizePartner(req.query.partner ? String(req.query.partner) : undefined);
|
||||
if (!partner) {
|
||||
return res.status(400).json({
|
||||
error: 'partner is required and must be one of: 1inch, 0x, LiFi',
|
||||
example: '/api/v1/routes/partner-payloads?partner=LiFi&amount=1000000',
|
||||
});
|
||||
}
|
||||
|
||||
const amount = req.query.amount ? String(req.query.amount) : '';
|
||||
if (!amount) {
|
||||
return res.status(400).json({
|
||||
error: 'amount is required',
|
||||
example: '/api/v1/routes/partner-payloads?partner=0x&amount=1000000',
|
||||
});
|
||||
}
|
||||
|
||||
const response = buildPayloads({
|
||||
partner,
|
||||
amount,
|
||||
fromChainId: req.query.fromChainId ? parseInt(String(req.query.fromChainId), 10) : undefined,
|
||||
toChainId: req.query.toChainId ? parseInt(String(req.query.toChainId), 10) : undefined,
|
||||
routeType: req.query.routeType ? String(req.query.routeType) : undefined,
|
||||
tokenIn: req.query.tokenIn ? String(req.query.tokenIn) : undefined,
|
||||
tokenOut: req.query.tokenOut ? String(req.query.tokenOut) : undefined,
|
||||
takerAddress: req.query.takerAddress ? String(req.query.takerAddress) : undefined,
|
||||
fromAddress: req.query.fromAddress ? String(req.query.fromAddress) : undefined,
|
||||
toAddress: req.query.toAddress ? String(req.query.toAddress) : undefined,
|
||||
recipient: req.query.recipient ? String(req.query.recipient) : undefined,
|
||||
slippagePercent: req.query.slippagePercent ? String(req.query.slippagePercent) : undefined,
|
||||
slippageBps: req.query.slippageBps ? String(req.query.slippageBps) : undefined,
|
||||
includeUnsupported: String(req.query.includeUnsupported ?? 'false').toLowerCase() === 'true',
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
return res.status(response.error.status).json(response.error.body);
|
||||
}
|
||||
|
||||
return res.json(response.result);
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/v1/routes/partner-payloads/resolve
|
||||
* Accepts JSON body and returns only supported partner payloads by default.
|
||||
*/
|
||||
router.post('/routes/partner-payloads/resolve', cacheMiddleware(30 * 1000), (req: Request, res: Response) => {
|
||||
const body = (req.body ?? {}) as PartnerPayloadRequestBody;
|
||||
const partner = normalizePartner(body.partner);
|
||||
|
||||
if (!partner) {
|
||||
return res.status(400).json({
|
||||
error: 'partner is required and must be one of: 1inch, 0x, LiFi',
|
||||
example: {
|
||||
partner: '0x',
|
||||
amount: '1000000',
|
||||
fromChainId: 138,
|
||||
routeType: 'swap',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!body.amount || !String(body.amount).trim()) {
|
||||
return res.status(400).json({
|
||||
error: 'amount is required',
|
||||
});
|
||||
}
|
||||
|
||||
const response = buildPayloads({
|
||||
partner,
|
||||
amount: String(body.amount),
|
||||
fromChainId: typeof body.fromChainId === 'number' ? body.fromChainId : undefined,
|
||||
toChainId: typeof body.toChainId === 'number' ? body.toChainId : undefined,
|
||||
routeType: body.routeType,
|
||||
tokenIn: body.tokenIn,
|
||||
tokenOut: body.tokenOut,
|
||||
takerAddress: body.takerAddress,
|
||||
fromAddress: body.fromAddress,
|
||||
toAddress: body.toAddress,
|
||||
recipient: body.recipient,
|
||||
slippagePercent: body.slippagePercent,
|
||||
slippageBps: body.slippageBps,
|
||||
includeUnsupported: body.includeUnsupported === true,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
return res.status(response.error.status).json(response.error.body);
|
||||
}
|
||||
|
||||
return res.json(response.result);
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/v1/routes/partner-payloads/dispatch
|
||||
* Resolves partner payloads and dispatches exactly one supported payload.
|
||||
*/
|
||||
router.post('/routes/partner-payloads/dispatch', async (req: Request, res: Response) => {
|
||||
const body = (req.body ?? {}) as PartnerPayloadRequestBody;
|
||||
const partner = normalizePartner(body.partner);
|
||||
|
||||
if (!partner) {
|
||||
return res.status(400).json({
|
||||
error: 'partner is required and must be one of: 1inch, 0x, LiFi',
|
||||
});
|
||||
}
|
||||
|
||||
if (!body.amount || !String(body.amount).trim()) {
|
||||
return res.status(400).json({
|
||||
error: 'amount is required',
|
||||
});
|
||||
}
|
||||
|
||||
const response = buildPayloads({
|
||||
partner,
|
||||
amount: String(body.amount),
|
||||
fromChainId: typeof body.fromChainId === 'number' ? body.fromChainId : undefined,
|
||||
toChainId: typeof body.toChainId === 'number' ? body.toChainId : undefined,
|
||||
routeType: body.routeType,
|
||||
tokenIn: body.tokenIn,
|
||||
tokenOut: body.tokenOut,
|
||||
takerAddress: body.takerAddress,
|
||||
fromAddress: body.fromAddress,
|
||||
toAddress: body.toAddress,
|
||||
recipient: body.recipient,
|
||||
slippagePercent: body.slippagePercent,
|
||||
slippageBps: body.slippageBps,
|
||||
includeUnsupported: true,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
return res.status(response.error.status).json(response.error.body);
|
||||
}
|
||||
|
||||
const supportedPayloads = response.result.payloads.filter((payload) => payload.supported);
|
||||
const selectedPayload = body.routeId
|
||||
? supportedPayloads.find((payload) => payload.routeId === body.routeId)
|
||||
: supportedPayloads.length === 1
|
||||
? supportedPayloads[0]
|
||||
: undefined;
|
||||
|
||||
if (!selectedPayload) {
|
||||
const fallback = buildInternalExecutionPlan({
|
||||
routeId: body.routeId,
|
||||
fromChainId: typeof body.fromChainId === 'number' ? body.fromChainId : undefined,
|
||||
toChainId: typeof body.toChainId === 'number' ? body.toChainId : undefined,
|
||||
tokenIn: body.tokenIn,
|
||||
tokenOut: body.tokenOut,
|
||||
amountIn: String(body.amount),
|
||||
slippageBps: body.slippageBps,
|
||||
});
|
||||
|
||||
return res.status(400).json({
|
||||
error: body.routeId
|
||||
? 'No supported payload found for the requested routeId'
|
||||
: 'Dispatch requires exactly one supported payload; refine filters or pass routeId',
|
||||
supportedRouteIds: supportedPayloads.map((payload) => payload.routeId),
|
||||
fallbackPlan: fallback.plan,
|
||||
fallbackError: fallback.error,
|
||||
});
|
||||
}
|
||||
|
||||
const dispatchResult = await dispatchPartnerPayload(selectedPayload);
|
||||
return res.json({
|
||||
generatedAt: new Date().toISOString(),
|
||||
partner,
|
||||
routeId: selectedPayload.routeId,
|
||||
dispatch: dispatchResult,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/v1/routes/internal-execution-plan
|
||||
* Returns a Chain 138 DODO PMM fallback execution plan for one live internal route.
|
||||
*/
|
||||
router.post('/routes/internal-execution-plan', (req: Request, res: Response) => {
|
||||
const body = (req.body ?? {}) as PartnerPayloadRequestBody;
|
||||
if (!body.amount || !String(body.amount).trim()) {
|
||||
return res.status(400).json({
|
||||
error: 'amount is required',
|
||||
});
|
||||
}
|
||||
|
||||
const result = buildInternalExecutionPlan({
|
||||
routeId: body.routeId,
|
||||
fromChainId: typeof body.fromChainId === 'number' ? body.fromChainId : undefined,
|
||||
toChainId: typeof body.toChainId === 'number' ? body.toChainId : undefined,
|
||||
tokenIn: body.tokenIn,
|
||||
tokenOut: body.tokenOut,
|
||||
amountIn: String(body.amount),
|
||||
slippageBps: body.slippageBps,
|
||||
});
|
||||
|
||||
if (result.error || !result.plan) {
|
||||
return res.status(400).json({
|
||||
error: result.error || 'Unable to build internal execution plan',
|
||||
candidateRouteIds: result.candidateRouteIds,
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
generatedAt: new Date().toISOString(),
|
||||
format: 'internal-execution-plan-v1',
|
||||
plan: result.plan,
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
94
services/token-aggregation/src/api/routes/routes.ts
Normal file
94
services/token-aggregation/src/api/routes/routes.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { cacheMiddleware } from '../middleware/cache';
|
||||
import { RouteDecisionTreeService } from '../../services/route-decision-tree';
|
||||
|
||||
const router = Router();
|
||||
const treeService = new RouteDecisionTreeService();
|
||||
|
||||
/**
|
||||
* GET /api/v1/routes/tree
|
||||
* Query:
|
||||
* - chainId
|
||||
* - tokenIn
|
||||
* - tokenOut (optional)
|
||||
* - amountIn (optional)
|
||||
* - destinationChainId (optional)
|
||||
*/
|
||||
router.get('/routes/tree', cacheMiddleware(10 * 1000), async (req: Request, res: Response) => {
|
||||
try {
|
||||
const chainId = parseInt(req.query.chainId as string, 10);
|
||||
const tokenIn = req.query.tokenIn as string;
|
||||
const tokenOut = req.query.tokenOut as string | undefined;
|
||||
const amountIn = req.query.amountIn as string | undefined;
|
||||
const destinationChainIdRaw = req.query.destinationChainId as string | undefined;
|
||||
const destinationChainId = destinationChainIdRaw ? parseInt(destinationChainIdRaw, 10) : undefined;
|
||||
|
||||
if (!chainId || !tokenIn) {
|
||||
return res.status(400).json({
|
||||
error: 'chainId and tokenIn are required',
|
||||
});
|
||||
}
|
||||
|
||||
const tree = await treeService.build({
|
||||
chainId,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
amountIn,
|
||||
destinationChainId,
|
||||
});
|
||||
|
||||
res.json(tree);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console -- route error logging
|
||||
console.error('Route tree error:', error);
|
||||
res.status(500).json({
|
||||
error: error instanceof Error ? error.message : 'Internal server error',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/v1/routes/depth
|
||||
* Convenience endpoint for the most relevant depth metrics.
|
||||
*/
|
||||
router.get('/routes/depth', cacheMiddleware(10 * 1000), async (req: Request, res: Response) => {
|
||||
try {
|
||||
const chainId = parseInt(req.query.chainId as string, 10);
|
||||
const tokenIn = req.query.tokenIn as string;
|
||||
const tokenOut = req.query.tokenOut as string | undefined;
|
||||
const amountIn = req.query.amountIn as string | undefined;
|
||||
const destinationChainIdRaw = req.query.destinationChainId as string | undefined;
|
||||
const destinationChainId = destinationChainIdRaw ? parseInt(destinationChainIdRaw, 10) : undefined;
|
||||
|
||||
if (!chainId || !tokenIn) {
|
||||
return res.status(400).json({
|
||||
error: 'chainId and tokenIn are required',
|
||||
});
|
||||
}
|
||||
|
||||
const tree = await treeService.build({
|
||||
chainId,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
amountIn,
|
||||
destinationChainId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
generatedAt: tree.generatedAt,
|
||||
decision: tree.decision,
|
||||
source: tree.source,
|
||||
destination: tree.destination,
|
||||
pools: tree.pools,
|
||||
missingQuoteTokenPools: tree.missingQuoteTokenPools,
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console -- route error logging
|
||||
console.error('Route depth error:', error);
|
||||
res.status(500).json({
|
||||
error: error instanceof Error ? error.message : 'Internal server error',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -8,9 +8,12 @@ import adminRoutes from './routes/admin';
|
||||
import configRoutes from './routes/config';
|
||||
import bridgeRoutes from './routes/bridge';
|
||||
import quoteRoutes from './routes/quote';
|
||||
import routeTreeRoutes from './routes/routes';
|
||||
import tokenMappingRoutes from './routes/token-mapping';
|
||||
import heatmapRoutes from './routes/heatmap';
|
||||
import arbitrageRoutes from './routes/arbitrage';
|
||||
import aggregatorRouteMatrixRoutes from './routes/aggregator-routes';
|
||||
import partnerPayloadRoutes from './routes/partner-payloads';
|
||||
import { MultiChainIndexer } from '../indexer/chain-indexer';
|
||||
import { getDatabasePool } from '../database/client';
|
||||
import winston from 'winston';
|
||||
@@ -102,10 +105,13 @@ export class ApiServer {
|
||||
this.app.use('/api/v1', configRoutes);
|
||||
this.app.use('/api/v1/report', reportRoutes);
|
||||
this.app.use('/api/v1/bridge', bridgeRoutes);
|
||||
this.app.use('/api/v1', routeTreeRoutes);
|
||||
this.app.use('/api/v1/token-mapping', tokenMappingRoutes);
|
||||
this.app.use('/api/v1', quoteRoutes);
|
||||
this.app.use('/api/v1', heatmapRoutes);
|
||||
this.app.use('/api/v1', arbitrageRoutes);
|
||||
this.app.use('/api/v1', aggregatorRouteMatrixRoutes);
|
||||
this.app.use('/api/v1', partnerPayloadRoutes);
|
||||
|
||||
// Admin routes (stricter rate limit)
|
||||
this.app.use('/api/v1/admin', strictRateLimiter, adminRoutes);
|
||||
|
||||
Reference in New Issue
Block a user