88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
/**
|
|
* Bridge quote API routes (swap+bridge+swap).
|
|
* Mount at POST /api/bridge/quote and POST /api/bridge/quote/swap-bridge-swap.
|
|
* Requires RPC_URL, BRIDGE_REGISTRY_ADDRESS (and registry ABI); optional: ENHANCED_SWAP_ROUTER_ADDRESS, DESTINATION_RPC_URL, DESTINATION_SWAP_ROUTER_ADDRESS.
|
|
* Sends CORS and Content-Type: application/json so the DApp (dapp.d-bis.org) can call cross-origin without CORB.
|
|
*/
|
|
|
|
import { Router, Request, Response } from 'express';
|
|
import { createQuoteServiceFromEnv } from './quote-service';
|
|
import { DestinationType } from './workflow-engine';
|
|
|
|
const router = Router();
|
|
|
|
const DAPP_ORIGINS = ['https://dapp.d-bis.org', 'http://192.168.11.58', 'http://localhost:5173'];
|
|
function setCorsAndJson(res: Response, req: Request): void {
|
|
const origin = req.get('origin');
|
|
if (origin && (DAPP_ORIGINS.includes(origin) || process.env.NODE_ENV !== 'production')) {
|
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
}
|
|
res.setHeader('Content-Type', 'application/json');
|
|
}
|
|
|
|
function getQuoteService(): ReturnType<typeof createQuoteServiceFromEnv> | null {
|
|
try {
|
|
return createQuoteServiceFromEnv();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/bridge/quote
|
|
* Body: { token, amount, destinationChainId, destinationType?, destinationAddress }
|
|
* Or swap+bridge+swap: { sourceToken, destinationToken, sourceChainId, destinationChainId, amount, destinationAddress }
|
|
*/
|
|
router.options('/quote', (req: Request, res: Response) => {
|
|
const origin = req.get('origin');
|
|
if (origin && (DAPP_ORIGINS.includes(origin) || process.env.NODE_ENV !== 'production')) {
|
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
}
|
|
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
res.setHeader('Access-Control-Max-Age', '86400');
|
|
res.sendStatus(204);
|
|
});
|
|
|
|
router.post('/quote', async (req: Request, res: Response) => {
|
|
setCorsAndJson(res, req);
|
|
try {
|
|
const svc = getQuoteService();
|
|
if (!svc) {
|
|
return res.status(503).json({ error: 'Quote service not configured (set RPC_URL, BRIDGE_REGISTRY_ADDRESS)' });
|
|
}
|
|
const body = req.body as Record<string, unknown>;
|
|
const sourceToken = body.sourceToken as string | undefined;
|
|
const destinationToken = body.destinationToken as string | undefined;
|
|
const sourceChainId = body.sourceChainId as number | undefined;
|
|
const destinationChainId = (body.destinationChainId ?? body.destinationChainId) as number;
|
|
const amount = (body.amount ?? body.amountIn) as string;
|
|
const token = (body.token ?? sourceToken) as string;
|
|
const destinationAddress = (body.destinationAddress ?? body.recipient) as string;
|
|
|
|
if (!amount || !destinationChainId) {
|
|
return res.status(400).json({ error: 'amount and destinationChainId are required' });
|
|
}
|
|
|
|
const request = {
|
|
token: token || '0x0000000000000000000000000000000000000000',
|
|
amount: String(amount),
|
|
destinationChainId: Number(destinationChainId),
|
|
destinationType: (body.destinationType as DestinationType) ?? DestinationType.EVM,
|
|
destinationAddress: destinationAddress || '0x0000000000000000000000000000000000000000',
|
|
};
|
|
|
|
const quote = await svc.getQuote(request);
|
|
res.json(quote);
|
|
} catch (err: unknown) {
|
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
res.status(500).json({ error: message });
|
|
}
|
|
});
|
|
|
|
export default router;
|